diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 0302d8560..3336d47aa 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -274,6 +274,8 @@ bool expr_is_pure(Expr *expr) return exprid_is_pure(expr->builtin_access_expr.inner); case EXPR_VARIANT: return exprid_is_pure(expr->variant_expr.type_id) && exprid_is_pure(expr->variant_expr.ptr); + case EXPR_POINTER_OFFSET: + return exprid_is_pure(expr->pointer_offset_expr.ptr) && exprid_is_pure(expr->pointer_offset_expr.offset); case EXPR_COMPILER_CONST: case EXPR_CONST: case EXPR_IDENTIFIER: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 08ac3ddb0..84e48e6ec 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -731,6 +731,13 @@ typedef struct ExprId type_id; } ExprVariant; +typedef struct +{ + bool raw_offset : 1; + ExprId ptr; + ExprId offset; +} ExprPointerOffset; + typedef enum { ACCESS_LEN, @@ -976,6 +983,7 @@ struct Expr_ ExprVariantSwitch variant_switch; // 32 ExprCast cast_expr; // 12 ExprVariant variant_expr; + ExprPointerOffset pointer_offset_expr; TypeInfo *type_expr; // 8 ExprConst const_expr; // 32 ExprArgv argv_expr; // 16 @@ -1936,6 +1944,7 @@ void expr_insert_addr(Expr *original); void expr_insert_deref(Expr *expr); bool expr_may_addr(Expr *expr); Expr *expr_variable(Decl *decl); +Expr *expr_negate_expr(Expr *expr); void expr_rewrite_to_builtin_access(SemaContext *context, Expr *expr, Expr *parent, BuiltinAccessKind kind, Type *type); INLINE Expr *expr_new_expr(ExprKind kind, Expr *expr); INLINE bool expr_ok(Expr *expr); @@ -2262,7 +2271,7 @@ INLINE CanonicalType *type_pointer_type(Type *type) { CanonicalType *res = type->canonical; if (res->type_kind != TYPE_POINTER) return NULL; - return res->pointer; + return res->pointer ; } INLINE bool type_is_pointer(Type *type) diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 9fbdc64bc..22eeea3be 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -184,6 +184,10 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) case EXPR_BUILTIN: case EXPR_RETVAL: return expr; + case EXPR_POINTER_OFFSET: + MACRO_COPY_EXPRID(expr->pointer_offset_expr.offset); + MACRO_COPY_EXPRID(expr->pointer_offset_expr.ptr); + return expr; case EXPR_BUILTIN_ACCESS: MACRO_COPY_EXPRID(expr->builtin_access_expr.inner); return expr; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index d3bb0f969..458d8ea5e 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -234,6 +234,7 @@ typedef enum EXPR_SLICE_ASSIGN, EXPR_SUBSCRIPT, EXPR_SUBSCRIPT_ADDR, + EXPR_POINTER_OFFSET, EXPR_STRINGIFY, EXPR_ARGV_TO_SUBARRAY, EXPR_TERNARY, @@ -801,7 +802,8 @@ typedef enum typedef enum { - CONSTANT_EVAL_ANY, - CONSTANT_EVAL_FOLDABLE, - CONSTANT_EVAL_NO_LINKTIME, + CONSTANT_EVAL_NO_SIDE_EFFECTS, + CONSTANT_EVAL_GLOBAL_INIT, + CONSTANT_EVAL_LOCAL_INIT, + CONSTANT_EVAL_CONSTANT_VALUE, } ConstantEvalKind; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index bfb5affb0..ac9de430f 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -575,7 +575,7 @@ static inline void gencontext_emit_subscript(GenContext *c, BEValue *value, Expr assert(needs_len); index.value = LLVMBuildNUWSub(c->builder, llvm_zext_trunc(c, len.value, llvm_get_type(c, index.type)), index.value, ""); } - if (needs_len && active_target.feature.safe_mode) + if (needs_len && active_target.feature.safe_mode && !llvm_is_global_eval(c)) { llvm_emit_array_bounds_check(c, &index, len.value, index_expr->span); } @@ -589,6 +589,33 @@ static inline void gencontext_emit_subscript(GenContext *c, BEValue *value, Expr } } +static inline void llvm_emit_pointer_offset(GenContext *c, BEValue *value, Expr *expr) +{ + Expr *pointer = exprptr(expr->pointer_offset_expr.ptr); + Expr *offset_expr = exprptr(expr->pointer_offset_expr.offset); + LLVMTypeRef pointee_type = llvm_get_pointee_type(c, pointer->type); + + // Emit the pointer + llvm_emit_expr(c, value, pointer); + llvm_value_rvalue(c, value); + + // Now calculate the offset: + BEValue offset; + llvm_emit_expr(c, &offset, offset_expr); + llvm_value_rvalue(c, &offset); + + if (expr->pointer_offset_expr.raw_offset) + { + LLVMValueRef raw_pointer = llvm_emit_bitcast_ptr(c, value->value, type_char); + LLVMValueRef pointer_offset = llvm_emit_pointer_gep_raw(c, c->byte_type, raw_pointer, offset.value); + value->value = LLVMBuildBitCast(c->builder, pointer_offset, pointee_type, ""); + } + else + { + value->value = llvm_emit_pointer_gep_raw(c, pointee_type, value->value, offset.value); + } +} + static MemberIndex find_member_index(Decl *parent, Decl *member) { @@ -3596,7 +3623,7 @@ void gencontext_emit_elvis_expr(GenContext *c, BEValue *value, Expr *expr) } Expr *else_expr = exprptr(expr->ternary_expr.else_expr); - if (expr_is_constant_eval(else_expr, CONSTANT_EVAL_ANY)) + if (expr_is_constant_eval(else_expr, CONSTANT_EVAL_NO_SIDE_EFFECTS)) { BEValue right; llvm_emit_expr(c, &right, else_expr); @@ -3661,7 +3688,7 @@ void gencontext_emit_ternary_expr(GenContext *c, BEValue *value, Expr *expr) Expr *else_expr = exprptr(expr->ternary_expr.else_expr); Expr *then_expr = exprptr(expr->ternary_expr.then_expr); - if (!IS_OPTIONAL(expr) && expr_is_constant_eval(else_expr, CONSTANT_EVAL_ANY) && expr_is_constant_eval(then_expr, CONSTANT_EVAL_ANY)) + if (!IS_OPTIONAL(expr) && expr_is_constant_eval(else_expr, CONSTANT_EVAL_NO_SIDE_EFFECTS) && expr_is_constant_eval(then_expr, CONSTANT_EVAL_NO_SIDE_EFFECTS)) { BEValue left; llvm_emit_expr(c, &left, then_expr); @@ -5557,6 +5584,7 @@ static inline void llvm_emit_variant(GenContext *c, BEValue *value, Expr *expr) LLVMValueRef var = llvm_get_undef(c, type_any); var = llvm_emit_insert_value(c, var, ptr.value, 0); var = llvm_emit_insert_value(c, var, typeid.value, 1); + assert(!LLVMIsConstant(ptr.value) || !LLVMIsConstant(typeid.value) || LLVMIsConstant(var)); llvm_value_set(value, var, type_any); } @@ -5774,6 +5802,9 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_SLICE: gencontext_emit_slice(c, value, expr); return; + case EXPR_POINTER_OFFSET: + llvm_emit_pointer_offset(c, value, expr); + return; case EXPR_FAILABLE: llvm_emit_failable(c, value, expr); return; diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 116cef02e..f5d552ae5 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -1115,6 +1115,11 @@ void llvm_emit_panic(GenContext *c, const char *message, const char *file, const void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc) { + if (llvm_is_const(value->value)) + { + assert(!LLVMConstIntGetZExtValue(value->value) && "Unexpected bounds check failed."); + return; + } LLVMBasicBlockRef panic_block = llvm_basic_block_new(c, "panic"); LLVMBasicBlockRef ok_block = llvm_basic_block_new(c, "checkok"); assert(llvm_value_is_bool(value)); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 64d664877..c2f3aaefb 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -518,11 +518,11 @@ static inline LLVMValueRef llvm_generate_introspection_global(GenContext *c, LLV assert(type->backend_typeid); } LLVMValueRef values[INTROSPECT_INDEX_TOTAL] = { - [INTROSPECT_INDEX_KIND] = llvm_const_int(c, type_char, introspect_type), - [INTROSPECT_INDEX_SIZEOF] = llvm_const_int(c, type_usize, type_size(type)), + [INTROSPECT_INDEX_KIND] = LLVMConstInt(c->byte_type, introspect_type, false), + [INTROSPECT_INDEX_SIZEOF] = LLVMConstInt(c->size_type, type_size(type), false), [INTROSPECT_INDEX_INNER] = inner ? llvm_get_typeid(c, inner) : llvm_get_zero(c, type_typeid), - [INTROSPECT_INDEX_LEN] = llvm_const_int(c, type_usize, len), - [INTROSPECT_INDEX_ADDITIONAL] = additional ? additional : llvm_get_array(llvm_get_type(c, type_typeid), NULL, 0) + [INTROSPECT_INDEX_LEN] = LLVMConstInt(c->size_type,len, false), + [INTROSPECT_INDEX_ADDITIONAL] = additional ? additional : LLVMConstArray(c->size_type, NULL, 0) }; LLVMValueRef global_name; scratch_buffer_clear(); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index b068bf613..9d963a793 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -824,8 +824,10 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_CT_EVAL: case EXPR_VARIANT: case EXPR_CT_CONV: + case EXPR_POINTER_OFFSET: UNREACHABLE case EXPR_BUILTIN_ACCESS: + return false; case EXPR_POST_UNARY: return recursive_may_narrow_float(expr->unary_expr.expr, type); @@ -989,6 +991,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_CT_EVAL: case EXPR_VARIANT: case EXPR_CT_CONV: + case EXPR_POINTER_OFFSET: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_int(expr->unary_expr.expr, type); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 0adc3deef..0a39f525f 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -782,7 +782,7 @@ static inline bool sema_analyse_enum_param(SemaContext *context, Decl *param, bo SEMA_ERROR(expr, "Default arguments may not be failable."); return false; } - if (!expr_is_constant_eval(expr, CONSTANT_EVAL_ANY)) + if (!expr_is_constant_eval(expr, CONSTANT_EVAL_GLOBAL_INIT)) { SEMA_ERROR(expr, "Only constant expressions may be used as default values."); return false; @@ -903,7 +903,7 @@ static inline bool sema_analyse_enum(SemaContext *context, Decl *decl) Expr *arg = args[j]; if (!sema_analyse_expr_rhs(context, associated_values[j]->type, arg, false)) return false; - if (!expr_is_constant_eval(arg, CONSTANT_EVAL_ANY)) + if (!expr_is_constant_eval(arg, CONSTANT_EVAL_GLOBAL_INIT)) { SEMA_ERROR(arg, "Expected a constant expression as parameter."); return false; @@ -2127,7 +2127,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) if ((init = decl->var.init_expr)) { if (!sema_analyse_expr_rhs(context, decl->type, init, false)) return false; - if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY)) + if (!expr_is_constant_eval(init, CONSTANT_EVAL_CONSTANT_VALUE)) { SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); return false; @@ -2144,7 +2144,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) if ((init = decl->var.init_expr)) { if (!sema_analyse_expr(context, init)) return false; - if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY)) + if (!expr_is_constant_eval(init, CONSTANT_EVAL_CONSTANT_VALUE)) { SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); return false; @@ -2214,7 +2214,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) if (!decl->var.type_info) { if (!sema_analyse_expr(context, init_expr)) return false; - if (is_global && !expr_is_constant_eval(init_expr, CONSTANT_EVAL_ANY)) + if (is_global && !expr_is_constant_eval(init_expr, CONSTANT_EVAL_GLOBAL_INIT)) { SEMA_ERROR(init_expr, "This expression cannot be evaluated at compile time."); return false; @@ -2263,7 +2263,14 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type); } - if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, false)) return decl_poison(decl); + Decl *function = context->current_function; + if (is_static) context->current_function = NULL; + if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, false)) + { + context->current_function = function; + return decl_poison(decl); + } + context->current_function = function; if (type_is_inferred) { @@ -2275,7 +2282,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) Expr *init_expr = decl->var.init_expr; // 2. Check const-ness - if ((is_global || decl->var.is_static) && !expr_is_constant_eval(init_expr, CONSTANT_EVAL_ANY)) + if ((is_global || decl->var.is_static) && !expr_is_constant_eval(init_expr, CONSTANT_EVAL_GLOBAL_INIT)) { SEMA_ERROR(init_expr, "The expression must be a constant value."); } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 4fc4fb959..a5d7b16ee 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -51,6 +51,17 @@ int BINOP_PREC_REQ[BINARYOP_LAST + 1] = const char *ct_eval_error = "EVAL_ERROR"; +Expr *expr_negate_expr(Expr *expr) +{ + if (expr->expr_kind == EXPR_UNARY && expr->unary_expr.operator == UNARYOP_NEG) + { + return expr->inner_expr; + } + Expr *neg = expr_new_expr(EXPR_UNARY, expr); + neg->unary_expr = (ExprUnary) { .operator = UNARYOP_NEG, .expr = expr }; + return neg; +} + void expr_rewrite_to_builtin_access(SemaContext *context, Expr *expr, Expr *parent, BuiltinAccessKind kind, Type *type) { expr->expr_kind = EXPR_BUILTIN_ACCESS; @@ -162,25 +173,33 @@ Expr *expr_variable(Decl *decl) static inline bool expr_unary_addr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) { - if (eval_kind != CONSTANT_EVAL_ANY) return false; + // An address is never a constant value. + if (eval_kind == CONSTANT_EVAL_CONSTANT_VALUE) return false; Expr *inner = expr->unary_expr.expr; switch (inner->expr_kind) { case EXPR_CONST: case EXPR_INITIALIZER_LIST: case EXPR_DESIGNATED_INITIALIZER_LIST: + // We can't create temporaries as const locally or making them into compile time constants. + if (eval_kind == CONSTANT_EVAL_LOCAL_INIT) return false; return expr_is_constant_eval(inner, eval_kind); case EXPR_IDENTIFIER: { + // The address of an identifier is side effect free. + if (eval_kind == CONSTANT_EVAL_NO_SIDE_EFFECTS) return true; Decl *decl = inner->identifier_expr.decl; if (decl->decl_kind == DECL_FUNC) return true; if (decl->decl_kind != DECL_VAR) return false; + assert(eval_kind == CONSTANT_EVAL_LOCAL_INIT || eval_kind == CONSTANT_EVAL_GLOBAL_INIT); switch (decl->var.kind) { case VARDECL_CONST: case VARDECL_GLOBAL: + // Fine for both local and global init. return true; case VARDECL_LOCAL: + // Getting the address of a local can never be constant init unless it is static. return decl->var.is_static; case VARDECL_PARAM: case VARDECL_MEMBER: @@ -194,6 +213,7 @@ static inline bool expr_unary_addr_is_constant_eval(Expr *expr, ConstantEvalKind case VARDECL_UNWRAPPED: case VARDECL_ERASE: case VARDECL_REWRAPPED: + // None of these are constant. return false; } } @@ -207,16 +227,16 @@ bool expr_cast_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) switch (expr->cast_expr.kind) { case CAST_ERROR: + UNREACHABLE case CAST_BSINT: case CAST_BSARRY: - return false; + return true; case CAST_ANYPTR: case CAST_ERBOOL: case CAST_EUBOOL: case CAST_EUER: case CAST_EREU: case CAST_XIERR: - case CAST_PTRPTR: case CAST_STRPTR: case CAST_PTRBOOL: case CAST_BOOLINT: @@ -230,24 +250,28 @@ bool expr_cast_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case CAST_SISI: case CAST_SIUI: case CAST_SIFP: - case CAST_XIPTR: case CAST_UISI: case CAST_UIUI: case CAST_UIFP: - case CAST_APTSA: - case CAST_SAPTR: case CAST_SABOOL: case CAST_STST: - case CAST_PTRANY: - case CAST_ENUMLOW: case CAST_VECARR: case CAST_ARRVEC: - if (eval_kind == CONSTANT_EVAL_FOLDABLE) return false; + if (eval_kind != CONSTANT_EVAL_NO_SIDE_EFFECTS) return false; + return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind); + case CAST_XIPTR: + case CAST_PTRPTR: + case CAST_APTSA: + case CAST_SAPTR: + case CAST_ENUMLOW: + return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind); + case CAST_PTRANY: + if (eval_kind == CONSTANT_EVAL_LOCAL_INIT || eval_kind == CONSTANT_EVAL_CONSTANT_VALUE) return false; return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind); case CAST_EUINT: case CAST_ERINT: case CAST_PTRXI: - if (eval_kind != CONSTANT_EVAL_ANY) return false; + if (eval_kind == CONSTANT_EVAL_CONSTANT_VALUE) return false; return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind); } UNREACHABLE @@ -255,29 +279,10 @@ bool expr_cast_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) static bool expr_binary_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) { if (expr->binary_expr.operator >= BINARYOP_ASSIGN) return false; - if (eval_kind == CONSTANT_EVAL_FOLDABLE) return false; + // Pointer add is already handled. + if (eval_kind == CONSTANT_EVAL_GLOBAL_INIT) return false; Expr *left = exprptr(expr->binary_expr.left); Expr *right = exprptr(expr->binary_expr.right); - int pointers = type_flatten(left->type)->type_kind == TYPE_POINTER ? 1 : 0; - pointers += type_flatten(right->type)->type_kind == TYPE_POINTER ? 1 : 0; - switch (expr->binary_expr.operator) - { - case BINARYOP_ERROR: - UNREACHABLE - case BINARYOP_SUB: - // Pointer diffs are not compile time resolved. - if (pointers == 2) return false; - if (pointers == 0) eval_kind = CONSTANT_EVAL_NO_LINKTIME; - break; - case BINARYOP_ADD: - assert(pointers != 2); - if (pointers == 0) eval_kind = CONSTANT_EVAL_NO_LINKTIME; - break; - default: - // For the default operations we don't accept linktime resolution - eval_kind = CONSTANT_EVAL_NO_LINKTIME; - break; - } if (!expr_is_constant_eval(left, eval_kind)) return false; if (!expr_is_constant_eval(right, eval_kind)) return false; return true; @@ -313,9 +318,12 @@ static bool sema_bit_assignment_check(Expr *right, Decl *member) bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) { + assert(expr->resolve_status == RESOLVE_DONE); RETRY: switch (expr->expr_kind) { + case EXPR_POINTER_OFFSET: + return exprid_is_constant_eval(expr->pointer_offset_expr.ptr, eval_kind) && exprid_is_constant_eval(expr->pointer_offset_expr.offset, eval_kind); case EXPR_CT_CONV: return true; case EXPR_RETVAL: @@ -340,7 +348,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case ACCESS_PTR: break; case ACCESS_TYPEOFANY: - if (eval_kind != CONSTANT_EVAL_ANY) return false; + if (eval_kind != CONSTANT_EVAL_NO_SIDE_EFFECTS) return false; break; } return exprid_is_constant_eval(expr->builtin_access_expr.inner, eval_kind); @@ -402,9 +410,11 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_DESIGNATED_INITIALIZER_LIST: return expr_list_is_constant_eval(expr->designated_init_list, eval_kind); case EXPR_SLICE: - if (expr->slice_expr.start && !exprid_is_constant_eval(expr->slice_expr.start, CONSTANT_EVAL_FOLDABLE)) return false; + return false; + /* + if (expr->slice_expr.start && !exprid_is_constant_eval(expr->slice_expr.start, eval_kind)) return false; if (expr->slice_expr.end && !exprid_is_constant_eval(expr->slice_expr.end, CONSTANT_EVAL_FOLDABLE)) return false; - return exprid_is_constant_eval(expr->slice_expr.expr, eval_kind); + return exprid_is_constant_eval(expr->slice_expr.expr, eval_kind);*/ case EXPR_SUBSCRIPT: if (!exprid_is_constant_eval(expr->subscript_expr.index, eval_kind)) return false; expr = exprptr(expr->subscript_expr.expr); @@ -427,11 +437,10 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) default: return false; } - return eval_kind != CONSTANT_EVAL_FOLDABLE; + return true; } } goto RETRY; - case EXPR_TERNARY: assert(!exprid_is_constant_eval(expr->ternary_expr.cond, eval_kind)); return false; @@ -441,7 +450,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) expr = expr->inner_expr; goto RETRY; case EXPR_TYPEID: - return eval_kind == CONSTANT_EVAL_ANY; + return eval_kind != CONSTANT_EVAL_CONSTANT_VALUE; case EXPR_UNARY: switch (expr->unary_expr.operator) { @@ -450,10 +459,13 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) return false; case UNARYOP_ADDR: return expr_unary_addr_is_constant_eval(expr, eval_kind); + case UNARYOP_TADDR: + if (eval_kind == CONSTANT_EVAL_CONSTANT_VALUE || eval_kind == CONSTANT_EVAL_LOCAL_INIT) return false; + expr = expr->unary_expr.expr; + goto RETRY; case UNARYOP_NEG: case UNARYOP_BITNEG: case UNARYOP_NOT: - case UNARYOP_TADDR: expr = expr->unary_expr.expr; goto RETRY; case UNARYOP_INC: @@ -661,6 +673,7 @@ static bool sema_check_expr_lvalue(Expr *top_expr, Expr *expr) case EXPR_TYPEID_INFO: case EXPR_VARIANT: case EXPR_BUILTIN_ACCESS: + case EXPR_POINTER_OFFSET: goto ERR; } UNREACHABLE @@ -758,6 +771,7 @@ bool expr_may_addr(Expr *expr) case EXPR_TYPEID_INFO: case EXPR_VARIANT: case EXPR_BUILTIN_ACCESS: + case EXPR_POINTER_OFFSET: return false; } UNREACHABLE @@ -861,7 +875,7 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) switch (decl->var.kind) { case VARDECL_CONST: - if (!expr_is_constant_eval(decl->var.init_expr, CONSTANT_EVAL_ANY)) + if (!expr_is_constant_eval(decl->var.init_expr, CONSTANT_EVAL_NO_SIDE_EFFECTS)) { UNREACHABLE } @@ -1777,7 +1791,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr // $foo assert(callee.macro); if (!sema_analyse_expr_rhs(context, type, arg, true)) return false; - if (!expr_is_constant_eval(arg, CONSTANT_EVAL_ANY)) + if (!expr_is_constant_eval(arg, CONSTANT_EVAL_CONSTANT_VALUE)) { SEMA_ERROR(arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?"); return false; @@ -1996,7 +2010,7 @@ static bool sema_check_stmt_compile_time(SemaContext *context, Ast *ast) case AST_RETURN_STMT: case AST_BLOCK_EXIT_STMT: if (!ast->return_stmt.expr) return true; - return expr_is_constant_eval(ast->return_stmt.expr, CONSTANT_EVAL_ANY); + return expr_is_constant_eval(ast->return_stmt.expr, CONSTANT_EVAL_CONSTANT_VALUE); case AST_EXPR_STMT: return sema_check_expr_compile_time(context, ast->expr_stmt); case AST_COMPOUND_STMT: @@ -2218,7 +2232,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s { Ast *ret = macro_context.returns[0]; Expr *result = ret ? ret->return_stmt.expr : NULL; - if (result && expr_is_constant_eval(result, CONSTANT_EVAL_ANY)) + if (result && expr_is_constant_eval(result, CONSTANT_EVAL_CONSTANT_VALUE)) { if (sema_check_stmt_compile_time(¯o_context, body)) { @@ -3116,6 +3130,50 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, Type *current_type = underlying_type; Expr *current_expr = subscripted; + int64_t index_value = -1; + if (expr_is_const_int(index) && (current_type->type_kind == TYPE_ARRAY || current_type == type_complist)) + { + // 4c. And that it's in range. + if (int_is_neg(index->const_expr.ixx)) + { + SEMA_ERROR(index, "The index may not be negative."); + return false; + } + int64_t size = current_type == type_complist ? vec_size(current_expr->initializer_list) : current_type->array.len; + assert(size >= 0 && "Unexpected overflow"); + if (!int_fits(index->const_expr.ixx, TYPE_I64)) + { + SEMA_ERROR(index, "The index is out of range.", size); + return false; + } + index_value = int_to_i64(index->const_expr.ixx); + if (expr->subscript_expr.from_back) + { + index_value = size - index_value; + } + if (index_value < 0 || index_value >= size) + { + if (expr->subscript_expr.from_back) + { + SEMA_ERROR(index, + size > 1 + ? "An index of '%lld' from the end is out of range, a value between 1 and %lld was expected." + : "An index of '%lld' from the end is out of range, a value of %lld was expected.", + (long long)(size - index_value), + (long long)size); + } + else + { + SEMA_ERROR(index, + size > 1 + ? "An index of '%lld' is out of range, a value between 0 and %lld was expected." + : "An index of '%lld' is out of range, a value of %lld was expected.", + (long long)index_value, + (long long)size - 1); + } + return false; + } + } // 4. If we are indexing into a complist if (current_type == type_complist) { @@ -3130,52 +3188,12 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, assert(current_expr->expr_kind == EXPR_INITIALIZER_LIST); // 4b. Now we need to check that we actually have a valid type. - if (index->expr_kind != EXPR_CONST || index->const_expr.const_kind != CONST_INTEGER) + if (index_value < 0) { SEMA_ERROR(index, "To subscript a compile time list a compile time integer index is needed."); return false; } - // 4c. And that it's in range. - if (int_is_neg(index->const_expr.ixx)) - { - SEMA_ERROR(index, "The index may not be negative."); - return false; - } - int64_t size = vec_size(current_expr->initializer_list); - assert(size >= 0 && "Unexpected overflow"); - if (!int_fits(index->const_expr.ixx, TYPE_I64)) - { - SEMA_ERROR(index, "The index is out of range.", size); - return false; - } - int64_t i = int_to_i64(index->const_expr.ixx); - if (expr->subscript_expr.from_back) - { - i = size - i; - } - if (i < 0 || i >= size) - { - if (expr->subscript_expr.from_back) - { - SEMA_ERROR(index, - size > 1 - ? "An index of '%lld' from the end is out of range, a value between 1 and %lld was expected." - : "An index of '%lld' from the end is out of range, a value of %lld was expected.", - (long long)(size - i), - (long long)size); - } - else - { - SEMA_ERROR(index, - size > 1 - ? "An index of '%lld' is out of range, a value between 0 and %lld was expected." - : "An index of '%lld' is out of range, a value of %lld was expected.", - (long long)i, - (long long)size - 1); - } - return false; - } - Expr *indexed_expr = current_expr->initializer_list[i]; + Expr *indexed_expr = current_expr->initializer_list[index_value]; if (!sema_cast_rvalue(context, indexed_expr)) return false; expr_replace(expr, indexed_expr); return true; @@ -3238,6 +3256,37 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, return true; } +static inline bool sema_expr_analyse_pointer_offset(SemaContext *context, Expr *expr) +{ + assert(expr->expr_kind == EXPR_POINTER_OFFSET); + + // 1. Evaluate the pointer + Expr *pointer = exprptr(expr->pointer_offset_expr.ptr); + if (!sema_analyse_expr(context, pointer)) return false; + + // 2. Evaluate the offset. + Expr *offset = exprptr(expr->pointer_offset_expr.offset); + if (!sema_analyse_expr(context, offset)) return false; + if (!cast_implicit(offset, type_iptrdiff)) return false; + + // 3. Store optionality + bool is_optional = IS_OPTIONAL(pointer) || IS_OPTIONAL(offset); + + // 4. Possibly constant fold + if (expr_is_const(pointer) && expr_is_const(offset)) + { + assert(!is_optional); + Int mul = { .i.low = type_size(type_flatten(pointer->type)->pointer), .type = offset->const_expr.ixx.type }; + Int offset_val = int_mul(mul, offset->const_expr.ixx); + Int res = int_add64(offset_val, pointer->const_expr.ptr); + pointer->const_expr.ptr = res.i.low; + expr_replace(expr, pointer); + return true; + } + expr->type = type_add_optional(pointer->type, is_optional); + return true; +} + static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr) { assert(expr->expr_kind == EXPR_SLICE); @@ -4696,8 +4745,8 @@ static bool sema_expr_analyse_designated_initializer(SemaContext *context, Type { initializer->type = sema_type_lower_by_size(initializer->type, (ArraySize)(max_index + 1)); } - - if (expr_is_constant_eval(initializer, CONSTANT_EVAL_ANY)) + initializer->resolve_status = RESOLVE_DONE; + if (expr_is_constant_eval(initializer, context->current_function ? CONSTANT_EVAL_LOCAL_INIT : CONSTANT_EVAL_GLOBAL_INIT)) { ConstInitializer *const_init = MALLOCS(ConstInitializer); sema_create_const_initializer(const_init, initializer); @@ -4850,8 +4899,8 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte // 6. There's the case of too few values as well. Mark the last field as wrong. assert(elements_needed <= size); - - if (expr_is_constant_eval(initializer, CONSTANT_EVAL_ANY)) + initializer->resolve_status = RESOLVE_DONE; + if (expr_is_constant_eval(initializer, context->current_function ? CONSTANT_EVAL_LOCAL_INIT : CONSTANT_EVAL_GLOBAL_INIT)) { bool is_union = type_flatten_distinct(initializer->type)->type_kind == TYPE_UNION; assert(!is_union || vec_size(elements) == 1); @@ -4944,7 +4993,8 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex return false; } - if (expr_is_constant_eval(initializer, CONSTANT_EVAL_ANY)) + initializer->resolve_status = RESOLVE_DONE; + if (expr_is_constant_eval(initializer, context->current_function ? CONSTANT_EVAL_LOCAL_INIT : CONSTANT_EVAL_GLOBAL_INIT)) { ConstInitializer *const_init = CALLOCS(ConstInitializer); const_init->kind = CONST_INIT_ARRAY_FULL; @@ -5662,21 +5712,38 @@ static bool sema_expr_analyse_sub(SemaContext *context, Expr *expr, Expr *left, // 6. Convert to iptrdiff if (!cast_implicit(right, type_iptrdiff)) return true; - // Constant fold. - if (expr_both_const(left, right)) + if (left->expr_kind == EXPR_POINTER_OFFSET) { - left->const_expr.ptr -= right->const_expr.ixx.i.low * type_size(left_type->pointer); - expr_replace(expr, left); + SourceSpan old_span = expr->span; + *expr = *left; + left->span = old_span; + left->expr_kind = EXPR_BINARY; + left->binary_expr = (ExprBinary){ + .operator = BINARYOP_SUB, + .left = expr->pointer_offset_expr.offset, + .right = exprid(right) + }; + left->resolve_status = RESOLVE_NOT_DONE; + if (!sema_analyse_expr(context, left)) return false; + expr->pointer_offset_expr.offset = exprid(left); + } + else + { + expr->expr_kind = EXPR_POINTER_OFFSET; + expr->pointer_offset_expr.ptr = exprid(left); + expr->pointer_offset_expr.offset = exprid(expr_negate_expr(right)); + expr->pointer_offset_expr.raw_offset = false; } - // Assign the type of the left side. - expr->type = left->type; + expr->resolve_status = RESOLVE_NOT_DONE; + if (!sema_analyse_expr(context, expr)) return false; if (cast_to_iptr) { expr->resolve_status = RESOLVE_DONE; return cast(expr, cast_to_iptr); } + return true; } @@ -5767,20 +5834,35 @@ static bool sema_expr_analyse_add(SemaContext *context, Expr *expr, Expr *left, assert(success && "This should always work"); (void)success; - if (expr_both_const(left, right)) + // Folding offset. + if (left->expr_kind == EXPR_POINTER_OFFSET) { - assert(left->const_expr.const_kind == CONST_POINTER); - assert(right->const_expr.const_kind == CONST_INTEGER); - uint64_t low_bits = right->const_expr.ixx.i.low; - left->const_expr.ptr += low_bits * type_size(left_type->pointer); - expr_replace(expr, left); + SourceSpan old_span = expr->span; + *expr = *left; + left->span = old_span; + left->expr_kind = EXPR_BINARY; + left->binary_expr = (ExprBinary){ + .operator = BINARYOP_ADD, + .left = expr->pointer_offset_expr.offset, + .right = exprid(right) + }; + left->resolve_status = RESOLVE_NOT_DONE; + if (!sema_analyse_expr(context, left)) return false; + expr->pointer_offset_expr.offset = exprid(left); } else { // Set the type and other properties. expr->type = left->type; + expr->pointer_offset_expr.raw_offset = false; + expr->pointer_offset_expr.ptr = exprid(left); + expr->pointer_offset_expr.offset = exprid(right); + expr->expr_kind = EXPR_POINTER_OFFSET; } + expr->resolve_status = RESOLVE_NOT_DONE; + if (!sema_analyse_expr(context, expr)) return false; + if (cast_to_iptr) { expr->resolve_status = RESOLVE_DONE; @@ -7866,6 +7948,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) return sema_expr_analyse_failable(context, expr); case EXPR_COMPILER_CONST: return sema_expr_analyse_compiler_const(context, expr); + case EXPR_POINTER_OFFSET: + return sema_expr_analyse_pointer_offset(context, expr); case EXPR_POISONED: return false; case EXPR_SLICE_ASSIGN: diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index ff849c3cf..b77637fb1 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -94,6 +94,11 @@ static inline bool expr_is_const_string(Expr *expr) return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_STRING; } +static inline bool expr_is_const_int(Expr *expr) +{ + return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INTEGER; +} + INLINE bool expr_is_const_list(Expr *expr) { return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 79ae04c88..74d24a771 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -2037,7 +2037,7 @@ static bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *statement) SEMA_ERROR(collection, "Expected a list to iterate over"); return false; } - if (!expr_is_constant_eval(collection, CONSTANT_EVAL_ANY)) + if (!expr_is_constant_eval(collection, CONSTANT_EVAL_CONSTANT_VALUE)) { SEMA_ERROR(collection, "A compile time $foreach must be over a constant value."); return false; @@ -2268,7 +2268,7 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement continue; } if (!sema_analyse_expr(context, expr)) goto EXIT_ERROR; - if (!expr_is_constant_eval(expr, CONSTANT_EVAL_FOLDABLE)) + if (!expr_is_constant_eval(expr, CONSTANT_EVAL_CONSTANT_VALUE)) { SEMA_ERROR(expr, "Only constant expressions are allowed."); goto EXIT_ERROR; diff --git a/src/version.h b/src/version.h index 0a3cf9e8f..fa4fbfd63 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.24" \ No newline at end of file +#define COMPILER_VERSION "0.3.25" \ No newline at end of file diff --git a/test/test_suite/arrays/global_init.c3t b/test/test_suite/arrays/global_init.c3t new file mode 100644 index 000000000..23b6868f6 --- /dev/null +++ b/test/test_suite/arrays/global_init.c3t @@ -0,0 +1,71 @@ +// #target: macos-x64 +module test; + +int* foo = &&3; + +int a; +int c; +int d; +int[3] abc; +int *b = (&((&a + 1)[2]) + 1 - 2) + 5; +int *bf = &abc[2] + 2; +int *bf2 = &abc[2]; +int *bf3 = &abc[2] + 2; + +fn void main() { + int *bf34 = (&abc[2] + 2) + 3; + static variant[3] x = { &&42, &&'c', &&"for" }; + variant[3] y = { &&42, &&'c', &&"for" }; +} + +/* #expect: test.ll + +@.taddr = private global i32 3, align 4 +@test_foo = local_unnamed_addr global i32* @.taddr, align 8 +@test_a = global i32 0, align 4 +@test_c = local_unnamed_addr global i32 0, align 4 +@test_d = local_unnamed_addr global i32 0, align 4 +@test_abc = local_unnamed_addr global [3 x i32] zeroinitializer, align 4 +@test_b = local_unnamed_addr global i32* getelementptr (i32, i32* @test_a, i64 7), align 8 +@test_bf = local_unnamed_addr global i32* getelementptr ([3 x i32], [3 x i32]* @test_abc, i64 1, i64 1), align 8 +@test_bf2 = local_unnamed_addr global i32* getelementptr inbounds ([3 x i32], [3 x i32]* @test_abc, i64 0, i64 2), align 8 +@test_bf3 = local_unnamed_addr global i32* getelementptr ([3 x i32], [3 x i32]* @test_abc, i64 1, i64 1), align 8 +@.taddr.9 = private global i32 42, align 4 +@"ct$int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@.taddr.10 = private global i8 99, align 1 +@"ct$char" = linkonce constant %.introspect { i8 3, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@.str = private unnamed_addr constant [4 x i8] c"for\00", align 1 +@.taddr.11 = private global [3 x i8]* bitcast ([4 x i8]* @.str to [3 x i8]*), align 8 +@"ct$a3$char" = linkonce constant %.introspect { i8 15, i64 3, i64 ptrtoint (%.introspect* @"ct$char" to i64), i64 3, [0 x i64] zeroinitializer }, align 8 +@"ct$p$a3$char" = linkonce constant %.introspect { i8 19, i64 8, i64 ptrtoint (%.introspect* @"ct$a3$char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"main$x" = internal unnamed_addr global [3 x %variant] [%variant { i8* bitcast (i32* @.taddr.9 to i8*), i64 ptrtoint (%.introspect* @"ct$int" to i64) }, %variant { i8* @.taddr.10, i64 ptrtoint (%.introspect* @"ct$char" to i64) }, %variant { i8* bitcast ([3 x i8]** @.taddr.11 to i8*), i64 ptrtoint (%.introspect* @"ct$p$a3$char" to i64) }], align 16 +@.str.12 = private unnamed_addr constant [4 x i8] c"for\00", align 1 + +; Function Attrs: nounwind +define void @test_main() #0 { +entry: + %bf34 = alloca i32*, align 8 + %y = alloca [3 x %variant], align 16 + %taddr = alloca i32, align 4 + %taddr1 = alloca i8, align 1 + %taddr2 = alloca [3 x i8]*, align 8 + store i32* getelementptr + %0 = getelementptr inbounds [3 x %variant], [3 x %variant]* %y, i64 0, i64 0 + store i32 42, i32* %taddr, align 4 + %1 = bitcast i32* %taddr to i8* + %2 = insertvalue %variant undef, i8* %1, 0 + %3 = insertvalue %variant %2, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1 + store %variant %3, %variant* %0, align 16 + %4 = getelementptr inbounds [3 x %variant], [3 x %variant]* %y, i64 0, i64 1 + store i8 99, i8* %taddr1, align 1 + %5 = insertvalue %variant undef, i8* %taddr1, 0 + %6 = insertvalue %variant %5, i64 ptrtoint (%.introspect* @"ct$char" to i64), 1 + store %variant %6, %variant* %4, align 16 + %7 = getelementptr inbounds [3 x %variant], [3 x %variant]* %y, i64 0, i64 2 + store [3 x i8]* bitcast ([4 x i8]* @.str.12 to [3 x i8]*), [3 x i8]** %taddr2, align 8 + %8 = bitcast [3 x i8]** %taddr2 to i8* + %9 = insertvalue %variant undef, i8* %8, 0 + %10 = insertvalue %variant %9, i64 ptrtoint (%.introspect* @"ct$p$a3$char" to i64), 1 + store %variant %10, %variant* %7, align 16 + ret void +} diff --git a/test/test_suite/arrays/global_init_array_out_of_range.c3 b/test/test_suite/arrays/global_init_array_out_of_range.c3 new file mode 100644 index 000000000..7471b4927 --- /dev/null +++ b/test/test_suite/arrays/global_init_array_out_of_range.c3 @@ -0,0 +1,2 @@ +int[3] abc; +int *bf3 = (&abc[4]) + 2; // #error: An index of '4' is out of range, a value between 0 and 2 was expected \ No newline at end of file diff --git a/test/test_suite2/arrays/global_init.c3t b/test/test_suite2/arrays/global_init.c3t new file mode 100644 index 000000000..88698f917 --- /dev/null +++ b/test/test_suite2/arrays/global_init.c3t @@ -0,0 +1,62 @@ +// #target: macos-x64 +module test; + +int* foo = &&3; + +int a; +int c; +int d; +int[3] abc; +int *b = (&((&a + 1)[2]) + 1 - 2) + 5; +int *bf = &abc[2] + 2; +int *bf2 = &abc[2]; +int *bf3 = &abc[2] + 2; + +fn void main() { + int *bf34 = (&abc[2] + 2) + 3; + static variant[3] x = { &&42, &&'c', &&"for" }; + variant[3] y = { &&42, &&'c', &&"for" }; +} + +/* #expect: test.ll + +@.taddr = private global i32 3, align 4 +@test_foo = local_unnamed_addr global ptr @.taddr, align 8 +@test_a = global i32 0, align 4 +@test_c = local_unnamed_addr global i32 0, align 4 +@test_d = local_unnamed_addr global i32 0, align 4 +@test_abc = local_unnamed_addr global [3 x i32] zeroinitializer, align 4 +@test_b = local_unnamed_addr global ptr getelementptr (i32, ptr @test_a, i64 7), align 8 +@test_bf = local_unnamed_addr global ptr getelementptr ([3 x i32], ptr @test_abc, i64 1, i64 1), align 8 +@test_bf2 = local_unnamed_addr global ptr getelementptr inbounds ([3 x i32], ptr @test_abc, i64 0, i64 2), align 8 +@test_bf3 = local_unnamed_addr global ptr getelementptr ([3 x i32], ptr @test_abc, i64 1, i64 1), align 8 +@.taddr.9 = private global i32 42, align 4 +@.taddr.10 = private global i8 99, align 1 +@.taddr.11 = private global ptr @.str, align 8 +@"main$x" = internal unnamed_addr global [3 x %variant] [%variant { ptr @.taddr.9, i64 ptrtoint (ptr @"ct$int" to i64) }, %variant { ptr @.taddr.10, i64 ptrtoint (ptr @"ct$char" to i64) }, %variant { ptr @.taddr.11, i64 ptrtoint (ptr @"ct$p$a3$char" to i64) }], align 16 + +define void @test_main() #0 { +entry: + %bf34 = alloca ptr, align 8 + %y = alloca [3 x %variant], align 16 + %taddr = alloca i32, align 4 + %taddr1 = alloca i8, align 1 + %taddr2 = alloca ptr, align 8 + store ptr getelementptr ([3 x i32], ptr @test_abc, i64 2, i64 1), ptr %bf34, align 8 + %0 = getelementptr inbounds [3 x %variant], ptr %y, i64 0, i64 0 + store i32 42, ptr %taddr, align 4 + %1 = insertvalue %variant undef, ptr %taddr, 0 + %2 = insertvalue %variant %1, i64 ptrtoint (ptr @"ct$int" to i64), 1 + store %variant %2, ptr %0, align 16 + %3 = getelementptr inbounds [3 x %variant], ptr %y, i64 0, i64 1 + store i8 99, ptr %taddr1, align 1 + %4 = insertvalue %variant undef, ptr %taddr1, 0 + %5 = insertvalue %variant %4, i64 ptrtoint (ptr @"ct$char" to i64), 1 + store %variant %5, ptr %3, align 16 + %6 = getelementptr inbounds [3 x %variant], ptr %y, i64 0, i64 2 + store ptr @.str.12, ptr %taddr2, align 8 + %7 = insertvalue %variant undef, ptr %taddr2, 0 + %8 = insertvalue %variant %7, i64 ptrtoint (ptr @"ct$p$a3$char" to i64), 1 + store %variant %8, ptr %6, align 16 + ret void +} diff --git a/test/test_suite2/arrays/global_init_array_out_of_range.c3 b/test/test_suite2/arrays/global_init_array_out_of_range.c3 new file mode 100644 index 000000000..7471b4927 --- /dev/null +++ b/test/test_suite2/arrays/global_init_array_out_of_range.c3 @@ -0,0 +1,2 @@ +int[3] abc; +int *bf3 = (&abc[4]) + 2; // #error: An index of '4' is out of range, a value between 0 and 2 was expected \ No newline at end of file