From bb2aa6e27afb0d08684d2a2ca4d572b8d416a31b Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 3 Apr 2020 22:00:00 +0200 Subject: [PATCH 1/8] Fixup of enum types. --- resources/testfragments/super_simple.c3 | 27 ++++ src/compiler/casts.c | 1 + src/compiler/compiler_internal.h | 19 +++ src/compiler/llvm_codegen_expr.c | 33 ++--- src/compiler/llvm_codegen_type.c | 7 +- src/compiler/number.c | 51 +++++--- src/compiler/sema_expr.c | 157 +++++++++++++++--------- 7 files changed, 207 insertions(+), 88 deletions(-) diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 693bf7240..0cab6ecba 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -130,6 +130,33 @@ error OtherError FOO_BAR } +enum Inf +{ + A, + B, + C = 10000 +} + +enum Inf2 : byte +{ + A, + B, + C = 129, +} + +typedef Inf as BooInf; + +func void enumInferenceTest() +{ + Inf x = Inf.A; + x = BooInf.B; + x = A; + int x1 = 0; + bool y = x1 == x1; + Inf2 z = C; + if (z == Inf2.A) return; +} + func int jumptest() { if (1) goto LABELX; diff --git a/src/compiler/casts.c b/src/compiler/casts.c index fb97ea067..65c29fdf3 100644 --- a/src/compiler/casts.c +++ b/src/compiler/casts.c @@ -726,5 +726,6 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type) if (canonical->type_kind == TYPE_POINTER) return sapt(expr, from_type, canonical, to_type, cast_type); break; } + if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; return sema_type_mismatch(expr, canonical, cast_type); } diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 1dfaf6a3f..ce522c013 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -55,7 +55,11 @@ typedef struct char* chars; int len; } string; + Decl *enum_constant; + Decl *error_constant; }; + // Valid type kinds: + // bool, ints, floats, enum, error, string TypeKind kind; } ExprConst; @@ -1150,6 +1154,21 @@ static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; } static inline bool type_info_ok(TypeInfo *type_info) { return !type_info || type_info->kind != TYPE_INFO_POISON; } bool type_may_have_method_functions(Type *type); + +static inline Type *type_reduced(Type *type) +{ + Type *canonical = type->canonical; + if (canonical->type_kind == TYPE_ENUM) return canonical->decl->enums.type_info->type->canonical; + if (canonical->type_kind == TYPE_ERROR) TODO; + return canonical; +} + +static inline Type *type_reduced_from_expr(Expr *expr) +{ + return type_reduced(expr->type); +} + + static inline bool type_is_integer(Type *type) { assert(type == type->canonical); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index fdbb4d7a6..866b95d37 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -180,7 +180,7 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * static inline LLVMValueRef gencontext_emit_inc_dec_change(GenContext *context, bool use_mod, LLVMValueRef current_value, Expr *expr, int diff) { - Type *type = expr->type->canonical; + Type *type = type_reduced_from_expr(expr); LLVMTypeRef llvm_type = llvm_type(type); if (type->type_kind == TYPE_POINTER) @@ -221,6 +221,7 @@ static inline LLVMValueRef gencontext_emit_post_inc_dec(GenContext *context, Exp LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) { + Type *type = type_reduced_from_expr(expr->unary_expr.expr); switch (expr->unary_expr.operator) { case UNARYOP_ERROR: @@ -233,11 +234,11 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "negmod"); case UNARYOP_NEG: // TODO improve how unsigned numbers are negated. - if (type_is_float(expr->unary_expr.expr->type->canonical)) + if (type_is_float(type)) { return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg"); } - if (type_is_unsigned(expr->unary_expr.expr->type->canonical)) + if (type_is_unsigned(type)) { return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg"); } @@ -246,7 +247,7 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) case UNARYOP_ADDR: return gencontext_emit_address(context, expr->unary_expr.expr); case UNARYOP_DEREF: - return LLVMBuildLoad2(context->builder, llvm_type(expr->unary_expr.expr->type), gencontext_emit_expr(context, expr->unary_expr.expr), "deref"); + return LLVMBuildLoad2(context->builder, llvm_type(type), gencontext_emit_expr(context, expr->unary_expr.expr), "deref"); case UNARYOP_INC: return gencontext_emit_pre_inc_dec(context, expr->unary_expr.expr, 1, false); case UNARYOP_DEC: @@ -440,7 +441,6 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM { return gencontext_emit_logical_and_or(context, expr, binary_op); } - Type *type = expr->type->canonical; Expr *lhs = expr->binary_expr.left; Expr *rhs = expr->binary_expr.right; @@ -456,10 +456,10 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM } rhs_value = gencontext_emit_expr(context, rhs); - Type *lhs_type = expr->binary_expr.left->type->canonical; + Type *lhs_type = type_reduced_from_expr(lhs); if (type_is_integer(lhs_type) && binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ) { - return gencontext_emit_int_comparison(context, lhs_type, rhs->type->canonical, lhs_value, rhs_value, binary_op); + return gencontext_emit_int_comparison(context, lhs_type, type_reduced_from_expr(rhs), lhs_value, rhs_value, binary_op); } bool is_float = type_is_float(lhs_type); switch (binary_op) @@ -520,24 +520,24 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM case BINARYOP_BIT_XOR: return LLVMBuildXor(context->builder, lhs_value, rhs_value, "xor"); case BINARYOP_EQ: - assert(!type_is_integer(lhs_type)); // Unordered? + assert(type_is_float(lhs_type)); return LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq"); case BINARYOP_NE: - assert(!type_is_integer(lhs_type)); // Unordered? + assert(type_is_float(lhs_type)); return LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq"); case BINARYOP_GE: - assert(!type_is_integer(lhs_type)); + assert(type_is_float(lhs_type)); return LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge"); case BINARYOP_GT: - assert(!type_is_integer(lhs_type)); + assert(type_is_float(lhs_type)); return LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt"); case BINARYOP_LE: - assert(!type_is_integer(lhs_type)); + assert(type_is_float(lhs_type)); return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le"); case BINARYOP_LT: - assert(!type_is_integer(lhs_type)); + assert(type_is_float(lhs_type)); return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt"); case BINARYOP_AND: case BINARYOP_OR: @@ -666,7 +666,7 @@ static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *e LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) { - LLVMTypeRef type = llvm_type(expr->type); + LLVMTypeRef type = llvm_type(type_reduced_from_expr(expr)); switch (expr->const_expr.kind) { case ALL_INTS: @@ -695,6 +695,11 @@ LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) 0)); return global_name; } + case TYPE_ERROR: + // TODO emit as u128? u64? + TODO + case TYPE_ENUM: + return gencontext_emit_expr(context, expr->const_expr.enum_constant->enum_constant.expr); default: UNREACHABLE } diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index b7cac074c..bab92199c 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -160,10 +160,13 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type) return type->backend_type = llvm_get_type(context, type->canonical); case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ENUM: - case TYPE_ERROR: case TYPE_ERROR_UNION: return type->backend_type = llvm_type_from_decl(context, type->decl); + case TYPE_ENUM: + return type->backend_type = llvm_get_type(context, type->decl->enums.type_info->type); + case TYPE_ERROR: + // TODO: u128? u64? + TODO case TYPE_FUNC: return type->backend_type = llvm_func_type(context, type); case TYPE_VOID: diff --git a/src/compiler/number.c b/src/compiler/number.c index ee2be3aba..21775d95a 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -34,19 +34,6 @@ static int type_bits[TYPE_U64 + 1] = { [TYPE_I64] = 64, }; -static int64_t int_type_max[TYPE_I64 + 1] = { - [TYPE_I8] = 0x7F, - [TYPE_I16] = 0x7FFF, - [TYPE_I32] = 0x7FFFFFFFLL, - [TYPE_I64] = 0x7FFFFFFFFFFFFFFFLL, - }; - -static int64_t int_type_min[TYPE_I64 + 1] = { - [TYPE_I8] = -0x80, - [TYPE_I16] = -0x8000L, - [TYPE_I32] = -0x80000000L, - [TYPE_I64] = -0x8000000000000000LL, -}; void expr_const_fprint(FILE *__restrict file, ExprConst *expr) { @@ -74,6 +61,12 @@ void expr_const_fprint(FILE *__restrict file, ExprConst *expr) case TYPE_FXX: fprintf(file, "%Lf", expr->f); break; + case TYPE_ENUM: + fprintf(file, "%s", expr->enum_constant->name); + break; + case TYPE_ERROR: + fprintf(file, "%s", expr->error_constant->name); + break; default: UNREACHABLE } @@ -172,6 +165,7 @@ static inline bool compare_fps(long double left, long double right, BinaryOp op) bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op) { + bool is_eq; switch (left->kind) { case TYPE_BOOL: @@ -183,10 +177,30 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp case TYPE_POINTER: return true; case TYPE_STRING: - TODO + if (left->string.len != right->string.len) + { + is_eq = false; + break; + } + if (right->string.chars == left->string.chars) + { + is_eq = true; + break; + } + is_eq = strncmp(left->string.chars, right->string.chars, left->string.len); + break; + case TYPE_ERROR: + assert(left->error_constant->type == right->error_constant->type); + is_eq = left->error_constant == right->error_constant; + break; + case TYPE_ENUM: + assert(left->enum_constant->type == right->enum_constant->type); + return expr_const_compare(&left->enum_constant->enum_constant.expr->const_expr, &right->enum_constant->enum_constant.expr->const_expr, op); default: UNREACHABLE } + assert(op == BINARYOP_EQ || op == BINARYOP_NE); + return op == BINARYOP_EQ == is_eq; } bool expr_const_int_overflowed(const ExprConst *expr) @@ -239,6 +253,15 @@ const char *expr_const_to_error_string(const ExprConst *expr) case TYPE_FXX: asprintf(&buff, "%Lf", expr->f); return buff; + case TYPE_ENUM: + asprintf(&buff, "%s.%s", expr->enum_constant->type->name, expr->enum_constant->name); + return buff; + case TYPE_ERROR: + asprintf(&buff, "%s.%s", expr->error_constant->type->name, expr->error_constant->name); + return buff; + case TYPE_STRING: + asprintf(&buff, "\"%*.s\"", expr->string.len, expr->string.chars); + return buff; default: UNREACHABLE } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index cf9afc8da..0473133b7 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -114,15 +114,75 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e return true; } + +static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, Decl *decl) +{ + VECEACH(decl->enums.values, i) + { + Decl *enum_constant = decl->enums.values[i]; + if (enum_constant->name == name) + { + assert(enum_constant->resolve_status == RESOLVE_DONE); + expr->type = enum_constant->type; + expr->const_expr.kind = TYPE_ENUM; + expr->const_expr.enum_constant = enum_constant; + expr->expr_kind = EXPR_CONST; + return true; + } + } + return false; +} + +static inline bool sema_expr_analyse_error_constant(Expr *expr, const char *name, Decl *decl) +{ + VECEACH(decl->error.error_constants, i) + { + Decl *error_constant = decl->error.error_constants[i]; + if (error_constant->name == name) + { + assert(error_constant->resolve_status == RESOLVE_DONE); + expr->type = decl->type; + expr->expr_kind = EXPR_CONST; + expr->const_expr.kind = TYPE_ERROR; + expr->const_expr.error_constant = decl; + return true; + } + } + return false; +} + +static inline bool find_possible_inferred_identifier(Type *to, Expr *expr) +{ + if (to->canonical->type_kind != TYPE_ENUM && to->canonical->type_kind != TYPE_ERROR) return false; + Decl *parent_decl = to->canonical->decl; + switch (parent_decl->decl_kind) + { + case DECL_ENUM: + return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.identifier, parent_decl); + case DECL_ERROR: + return sema_expr_analyse_error_constant(expr, expr->identifier_expr.identifier, parent_decl); + case DECL_UNION: + case DECL_STRUCT: + return false; + default: + UNREACHABLE + } + +} static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr *expr) { - // TODO what about struct functions Decl *ambiguous_decl; Decl *decl = sema_resolve_symbol(context, expr->identifier_expr.identifier, expr->identifier_expr.path, &ambiguous_decl); + + if (!decl && !expr->identifier_expr.path && to) + { + if (find_possible_inferred_identifier(to, expr)) return true; + } + if (!decl) { - SEMA_ERROR(expr, "Unknown symbol '%s'.", expr->identifier_expr.identifier); + SEMA_ERROR(expr, "The symbol '%s' could not be found.", expr->identifier_expr.identifier); return false; } @@ -163,6 +223,7 @@ static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Type *to, static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr *expr) { TODO } static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *expr) { TODO }; + static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr *expr, Decl *decl) { Expr **args =expr->call_expr.arguments; @@ -284,41 +345,6 @@ static inline bool sema_expr_analyse_method_function(Context *context, Expr *exp return false; } -static inline bool sema_expr_analyse_enum_constant(Context *context, Expr *expr, Decl *decl) -{ - const char *name = expr->type_access.name.string; - VECEACH(decl->enums.values, i) - { - Decl *enum_constant = decl->enums.values[i]; - if (enum_constant->name == name) - { - assert(enum_constant->resolve_status == RESOLVE_DONE); - expr_replace(expr, enum_constant->enum_constant.expr); - return true; - } - } - SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, name); - return false; -} - -static inline bool sema_expr_analyse_error_constant(Context *context, Expr *expr, Decl *decl) -{ - const char *name = expr->type_access.name.string; - VECEACH(decl->error.error_constants, i) - { - Decl *error_constant = decl->error.error_constants[i]; - if (error_constant->name == name) - { - assert(error_constant->resolve_status == RESOLVE_DONE); - expr->type = decl->type; - expr->expr_kind = EXPR_CONST; - expr_const_set_int(&expr->const_expr, decl->error_constant.value, TYPE_U32); - return true; - } - } - SEMA_ERROR(expr, "'%s' has no error type '%s'.", decl->name, name); - return false; -} static Decl *strukt_recursive_search_member(Decl *strukt, const char *name, int *index) { @@ -336,6 +362,7 @@ static Decl *strukt_recursive_search_member(Decl *strukt, const char *name, int return NULL; } + static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *expr) { if (!sema_analyse_expr(context, NULL, expr->access_expr.parent)) return false; @@ -389,21 +416,38 @@ static inline bool sema_expr_analyse_type_access(Context *context, Type *to, Exp { TypeInfo *type_info = expr->type_access.type; if (!sema_resolve_type_info(context, type_info)) return false; - if (!type_may_have_method_functions(type_info->type)) + Type *canonical = type_info->type->canonical; + if (!type_may_have_method_functions(canonical)) { SEMA_ERROR(expr, "'%s' does not have method functions.", type_to_error_string(type_info->type)); return false; } - Decl *decl = type_info->type->decl; + Decl *decl = canonical->decl; // TODO add more constants that can be inspected? // e.g. SomeEnum.values, MyUnion.x.offset etc? switch (decl->decl_kind) { case DECL_ENUM: - if (expr->type_access.name.type == TOKEN_CONST_IDENT) return sema_expr_analyse_enum_constant(context, expr, decl); + if (expr->type_access.name.type == TOKEN_CONST_IDENT) + { + if (!sema_expr_analyse_enum_constant(expr, expr->type_access.name.string, decl)) + { + SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, expr->type_access.name.string); + return false; + } + return true; + } break; case DECL_ERROR: - if (expr->type_access.name.type == TOKEN_CONST_IDENT) return sema_expr_analyse_error_constant(context, expr, decl); + if (expr->type_access.name.type == TOKEN_CONST_IDENT) + { + if (!sema_expr_analyse_error_constant(expr, expr->type_access.name.string, decl)) + { + SEMA_ERROR(expr, "'%s' has no error type '%s'.", decl->name, expr->type_access.name.string); + return false; + } + return true; + } break; case DECL_UNION: case DECL_STRUCT: @@ -831,6 +875,8 @@ static bool binary_arithmetic_promotion(Expr *left, Expr *right, Type *left_type */ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { + // TODO enums + bool is_mod = expr->binary_expr.operator == BINARYOP_SUB_MOD; // 1. Analyse a and b. Do not push down if this is a -% @@ -926,6 +972,8 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * */ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { + // TODO enums + bool is_mod = expr->binary_expr.operator == BINARYOP_ADD_MOD; // 1. Promote everything to the recipient type – if possible @@ -1446,11 +1494,15 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp Type *max = type_find_max_type(left_type, right_type); // 4. If no common type, then that's an error: - if (!max) goto ERR; + if (!max) + { + SEMA_ERROR(expr, "'%s' and '%s' are different types and cannot be compared.", + type_to_error_string(left->type), type_to_error_string(right->type)); + }; // 5. Most types can do equality, but not all can do comparison, // so we need to check that as well. - if (is_equality_type_op) + if (!is_equality_type_op) { switch (max->type_kind) { @@ -1496,21 +1548,7 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp // 7. Do constant folding. if (both_const(left, right)) { - switch (left->const_expr.kind) - { - case TYPE_BOOL: - SEMA_ERROR(expr, "Cannot compare booleans, convert them into integers first."); - return false; - case ALL_FLOATS: - case ALL_INTS: - expr->const_expr.b = expr_const_compare(&left->const_expr, &right->const_expr, expr->binary_expr.operator); - return true; - case TYPE_STRING: - SEMA_ERROR(expr, "Cannot compare strings."); - return false; - default: - UNREACHABLE - } + expr->const_expr.b = expr_const_compare(&left->const_expr, &right->const_expr, expr->binary_expr.operator); expr->const_expr.kind = TYPE_BOOL; expr->expr_kind = EXPR_CONST; } @@ -1678,6 +1716,9 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr * case TYPE_STRING: expr->const_expr.b = !inner->const_expr.string.len; break; + case TYPE_ERROR: + case TYPE_ENUM: + TODO default: UNREACHABLE } From bbef467317970c79f604ee0a431920e48a625e6e Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 4 Apr 2020 01:28:15 +0200 Subject: [PATCH 2/8] Codegen of enum and error --- resources/testfragments/super_simple.c3 | 1 + src/compiler/compiler_internal.h | 13 ++--------- src/compiler/llvm_codegen.c | 2 +- src/compiler/llvm_codegen_expr.c | 20 ++++++----------- src/compiler/llvm_codegen_stmt.c | 2 +- src/compiler/llvm_codegen_type.c | 15 ++++++++----- src/compiler/sema_decls.c | 2 +- src/compiler/sema_expr.c | 2 +- src/compiler/types.c | 30 ++++++++++++++++--------- 9 files changed, 43 insertions(+), 44 deletions(-) diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 0cab6ecba..a6055f864 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -148,6 +148,7 @@ typedef Inf as BooInf; func void enumInferenceTest() { + OtherError e = OtherError.FOO_BAR; Inf x = Inf.A; x = BooInf.B; x = A; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index ce522c013..76c17a881 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -30,8 +30,6 @@ typedef struct _Expr Expr; typedef struct _Module Module; typedef struct _Type Type; -typedef bool(*CastFunc)(Expr *, Type *, Type *, Type *, CastType cast_type); - typedef struct _BigInt { unsigned digit_count; @@ -888,14 +886,7 @@ extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; extern Type *type_compint, *type_compfloat; extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong; extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong; - -extern Type t_i8, t_i16, t_i32, t_i64, t_isz, t_ixx; -extern Type t_u1, t_u8, t_u16, t_u32, t_u64, t_usz, t_uxx; -extern Type t_f32, t_f64, t_fxx; -extern Type t_u0, t_str; -extern Type t_cus, t_cui, t_cul, t_cull; -extern Type t_cs, t_ci, t_cl, t_cll; -extern Type t_voidstar; +extern Type *type_typeid, *type_error; extern const char *main_name; @@ -1159,7 +1150,7 @@ static inline Type *type_reduced(Type *type) { Type *canonical = type->canonical; if (canonical->type_kind == TYPE_ENUM) return canonical->decl->enums.type_info->type->canonical; - if (canonical->type_kind == TYPE_ERROR) TODO; + if (canonical->type_kind == TYPE_ERROR) return type_error->canonical; return canonical; } diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index f638cfc17..aa22ae8d2 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -46,7 +46,7 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl } // TODO fix name - decl->var.backend_ref = LLVMAddGlobal(context->module, decl->type->backend_type, decl->name); + decl->var.backend_ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->name); // If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 866b95d37..f07715644 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -227,7 +227,7 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) case UNARYOP_ERROR: FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator); case UNARYOP_NOT: - return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), LLVMConstInt(type_bool->backend_type, 1, 0), "not"); + return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), LLVMConstInt(llvm_type(type_bool), 1, 0), "not"); case UNARYOP_BITNEG: return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot"); case UNARYOP_NEGMOD: @@ -286,7 +286,7 @@ static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *ex // Generate phi gencontext_emit_block(context, phi_block); - LLVMValueRef phi = LLVMBuildPhi(context->builder, type_bool->backend_type, "val"); + LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val"); // Simplify for LLVM by entering the constants we already know of. LLVMValueRef result_on_skip = LLVMConstInt(LLVMInt1TypeInContext(context->context), op == BINARYOP_AND ? 0 : 1, false); @@ -616,7 +616,7 @@ LLVMValueRef gencontext_emit_elvis_expr(GenContext *context, Expr *expr) // Generate phi gencontext_emit_block(context, phi_block); - LLVMValueRef phi = LLVMBuildPhi(context->builder, expr->type->backend_type, "val"); + LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val"); LLVMValueRef logic_values[2] = { lhs, rhs }; LLVMBasicBlockRef blocks[2] = { current_block, rhs_block }; @@ -649,20 +649,15 @@ LLVMValueRef gencontext_emit_ternary_expr(GenContext *context, Expr *expr) // Generate phi gencontext_emit_block(context, phi_block); - LLVMValueRef phi = LLVMBuildPhi(context->builder, expr->type->backend_type, "val"); + LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val"); - LLVMValueRef logicValues[2] = { lhs, rhs }; + LLVMValueRef logic_values[2] = { lhs, rhs }; LLVMBasicBlockRef blocks[2] = { lhs_block, rhs_block }; - LLVMAddIncoming(phi, logicValues, blocks, 2); + LLVMAddIncoming(phi, logic_values, blocks, 2); return phi; } -static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *expr) -{ - return LLVMBuildLoad2(context->builder, expr->identifier_expr.decl->type->canonical->backend_type, - expr->identifier_expr.decl->var.backend_ref, expr->identifier_expr.decl->name); -} LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) { @@ -696,8 +691,7 @@ LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) return global_name; } case TYPE_ERROR: - // TODO emit as u128? u64? - TODO + return LLVMConstInt(llvm_type(type_error), expr->const_expr.error_constant->error_constant.value, false); case TYPE_ENUM: return gencontext_emit_expr(context, expr->const_expr.enum_constant->enum_constant.expr); default: diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 92bcc8310..428e1dde6 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -23,7 +23,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) { Decl *decl = ast->declare_stmt; - decl->var.backend_ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name); + decl->var.backend_ref = gencontext_emit_alloca(context, llvm_type(type_reduced(decl->type)), decl->name); // TODO NRVO // TODO debug info /* diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index bab92199c..896d6c0c2 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -155,18 +155,21 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type) { case TYPE_POISONED: case TYPE_META_TYPE: + case TYPE_ENUM: + case TYPE_ERROR: UNREACHABLE; case TYPE_TYPEDEF: return type->backend_type = llvm_get_type(context, type->canonical); + case TYPE_ERROR_UNION: + { + LLVMTypeRef types[2]; + types[0] = llvm_get_type(context, type_typeid->canonical); + types[1] = llvm_get_type(context, type_error->canonical); + return type->backend_type = LLVMStructType(types, 2, false); + } case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ERROR_UNION: return type->backend_type = llvm_type_from_decl(context, type->decl); - case TYPE_ENUM: - return type->backend_type = llvm_get_type(context, type->decl->enums.type_info->type); - case TYPE_ERROR: - // TODO: u128? u64? - TODO case TYPE_FUNC: return type->backend_type = llvm_func_type(context, type); case TYPE_VOID: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 78450603c..f0f96ec59 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -31,7 +31,7 @@ static inline bool sema_analyse_error(Context *context __unused, Decl *decl) break; } } - constant->error_constant.value = i; + constant->error_constant.value = i + 1; constant->resolve_status = RESOLVE_DONE; } return success; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 0473133b7..fdf5541de 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -144,7 +144,7 @@ static inline bool sema_expr_analyse_error_constant(Expr *expr, const char *name expr->type = decl->type; expr->expr_kind = EXPR_CONST; expr->const_expr.kind = TYPE_ERROR; - expr->const_expr.error_constant = decl; + expr->const_expr.error_constant = error_constant; return true; } } diff --git a/src/compiler/types.c b/src/compiler/types.c index c5cf89e77..97477ec91 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -4,22 +4,29 @@ #include "compiler_internal.h" -Type *type_bool, *type_void, *type_string, *type_voidptr; -Type *type_float, *type_double; +static Type t_u0, t_str, t_u1, t_i8, t_i16, t_i32, t_i64, t_ixx; +static Type t_u8, t_u16, t_u32, t_u64; +static Type t_f32, t_f64, t_fxx; +static Type t_usz, t_isz; +static Type t_cus, t_cui, t_cul, t_cull; +static Type t_cs, t_ci, t_cl, t_cll; +static Type t_voidstar, t_typeid; +static Type t_err; + +Type *type_bool = &t_u1; +Type *type_void = &t_u0; +Type *type_string = &t_str; +Type *type_voidptr = &t_voidstar; +Type *type_float = &t_f32; +Type *type_double = &t_f64; +Type *type_error = &t_err; +Type *type_typeid = &t_typeid; Type *type_char, *type_short, *type_int, *type_long, *type_isize; Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; Type *type_compint, *type_compfloat; Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong; Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong; -Type t_u0, t_str; -Type t_u1, t_i8, t_i16, t_i32, t_i64, t_ixx; -Type t_u8, t_u16, t_u32, t_u64, t_uxx; -Type t_f32, t_f64, t_fxx; -Type t_usz, t_isz; -Type t_cus, t_cui, t_cul, t_cull; -Type t_cs, t_ci, t_cl, t_cll; -Type t_voidstar; #define META_OFFSET 0 #define PTR_OFFSET 1 @@ -421,6 +428,9 @@ type_create(#_name, &_shortname, &type_ ## _name, _type, _bits, target->align_mi type_create_alias("c_short", &t_cs, &type_c_short, type_signed_int_by_bitsize(target->width_c_short)); type_create_alias("c_int", &t_ci, &type_c_int, type_signed_int_by_bitsize(target->width_c_int)); + // TODO fix error size + type_create_alias("error", &t_err, &type_error, type_signed_int_by_bitsize(target->width_c_int)); + type_create_alias("typeid", &t_typeid, &type_typeid, type_signed_int_by_bitsize(target->width_pointer)); type_create_alias("c_long", &t_cl, &type_c_long, type_signed_int_by_bitsize(target->width_c_long)); type_create_alias("c_longlong", &t_cll, &type_c_longlong, type_signed_int_by_bitsize(target->width_c_long_long)); From 336e6cf47d2eb3f3ddefb63b6ef5633a1faa28b4 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 4 Apr 2020 01:35:20 +0200 Subject: [PATCH 3/8] Type cleanup --- src/compiler/types.c | 64 ++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/compiler/types.c b/src/compiler/types.c index 97477ec91..d3fe193e5 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -21,11 +21,26 @@ Type *type_float = &t_f32; Type *type_double = &t_f64; Type *type_error = &t_err; Type *type_typeid = &t_typeid; -Type *type_char, *type_short, *type_int, *type_long, *type_isize; -Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; -Type *type_compint, *type_compfloat; -Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong; -Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong; +Type *type_char = &t_i8; +Type *type_short = &t_i16; +Type *type_int = &t_i32; +Type *type_long = &t_i64; +Type *type_isize = &t_isz; +Type *type_byte = &t_u8; +Type *type_ushort = &t_u16; +Type *type_uint = &t_u32; +Type *type_ulong = &t_u64; +Type *type_usize = &t_usz; +Type *type_compint = &t_ixx; +Type *type_compfloat = &t_fxx; +Type *type_c_short = &t_cs; +Type *type_c_int = &t_ci; +Type *type_c_long = &t_cl; +Type *type_c_longlong = &t_cll; +Type *type_c_ushort = &t_cus; +Type *type_c_uint = &t_cui; +Type *type_c_ulong = &t_cul; +Type *type_c_ulonglong = &t_cull; #define META_OFFSET 0 @@ -353,7 +368,7 @@ Type *type_get_array(Type *arr_type, uint64_t len) return type_create_array(arr_type, len, false); } -static void type_create(const char *name, Type *location, Type **ptr, TypeKind kind, unsigned bitsize, +static void type_create(const char *name, Type *location, TypeKind kind, unsigned bitsize, unsigned align, unsigned pref_align) { *location = (Type) { @@ -367,17 +382,15 @@ static void type_create(const char *name, Type *location, Type **ptr, TypeKind k }; location->name = name; location->canonical = location; - *ptr = location; } -static void type_create_alias(const char *name, Type *location, Type **ptr, Type *canonical) +static void type_create_alias(const char *name, Type *location, Type *canonical) { *location = (Type) { .type_kind = TYPE_TYPEDEF, .name = name, .canonical = canonical }; - *ptr = location; } @@ -390,7 +403,7 @@ void builtin_setup(Target *target) type_string.type_kind = TYPE_STRING; */ #define DEF_TYPE(_name, _shortname, _type, _bits, _align) \ -type_create(#_name, &_shortname, &type_ ## _name, _type, _bits, target->align_min_ ## _align, target->align_ ## _align) +type_create(#_name, &_shortname, _type, _bits, target->align_min_ ## _align, target->align_ ## _align) DEF_TYPE(bool, t_u1, TYPE_BOOL, 1, byte); DEF_TYPE(float, t_f32, TYPE_F32, 32, float); @@ -411,29 +424,28 @@ type_create(#_name, &_shortname, &type_ ## _name, _type, _bits, target->align_mi #undef DEF_TYPE - type_create("void*", &t_voidstar, &type_voidptr, TYPE_POINTER, target->width_pointer, target->align_min_pointer, target->align_pointer); + type_create("void*", &t_voidstar, TYPE_POINTER, target->width_pointer, target->align_min_pointer, target->align_pointer); create_type_cache(type_void); type_void->type_cache[0] = &t_voidstar; t_voidstar.pointer = type_void; - type_create("compint", &t_ixx, &type_compint, TYPE_IXX, 32, 0, 0); - type_create("compfloat", &t_fxx, &type_compfloat, TYPE_FXX, 64, 0, 0); + type_create("compint", &t_ixx, TYPE_IXX, 32, 0, 0); + type_create("compfloat", &t_fxx, TYPE_FXX, 64, 0, 0); - type_create_alias("usize", &t_usz, &type_usize, type_unsigned_int_by_bitsize(target->width_pointer)); - type_create_alias("isize", &t_isz, &type_isize, type_signed_int_by_bitsize(target->width_pointer)); + type_create_alias("usize", &t_usz, type_unsigned_int_by_bitsize(target->width_pointer)); + type_create_alias("isize", &t_isz, type_signed_int_by_bitsize(target->width_pointer)); - type_create_alias("c_ushort", &t_cus, &type_c_ushort, type_unsigned_int_by_bitsize(target->width_c_short)); - type_create_alias("c_uint", &t_cui, &type_c_uint, type_unsigned_int_by_bitsize(target->width_c_int)); - type_create_alias("c_ulong", &t_cul, &type_c_ulong, type_unsigned_int_by_bitsize(target->width_c_long)); - type_create_alias("c_ulonglong", &t_cull, &type_c_ulonglong, type_unsigned_int_by_bitsize(target->width_c_long_long)); + type_create_alias("c_ushort", &t_cus, type_unsigned_int_by_bitsize(target->width_c_short)); + type_create_alias("c_uint", &t_cui, type_unsigned_int_by_bitsize(target->width_c_int)); + type_create_alias("c_ulong", &t_cul, type_unsigned_int_by_bitsize(target->width_c_long)); + type_create_alias("c_ulonglong", &t_cull, type_unsigned_int_by_bitsize(target->width_c_long_long)); - type_create_alias("c_short", &t_cs, &type_c_short, type_signed_int_by_bitsize(target->width_c_short)); - type_create_alias("c_int", &t_ci, &type_c_int, type_signed_int_by_bitsize(target->width_c_int)); + type_create_alias("c_short", &t_cs, type_signed_int_by_bitsize(target->width_c_short)); + type_create_alias("c_int", &t_ci, type_signed_int_by_bitsize(target->width_c_int)); // TODO fix error size - type_create_alias("error", &t_err, &type_error, type_signed_int_by_bitsize(target->width_c_int)); - type_create_alias("typeid", &t_typeid, &type_typeid, type_signed_int_by_bitsize(target->width_pointer)); - type_create_alias("c_long", &t_cl, &type_c_long, type_signed_int_by_bitsize(target->width_c_long)); - type_create_alias("c_longlong", &t_cll, &type_c_longlong, type_signed_int_by_bitsize(target->width_c_long_long)); - + type_create_alias("error", &t_err, type_signed_int_by_bitsize(target->width_c_int)); + type_create_alias("typeid", &t_typeid, type_signed_int_by_bitsize(target->width_pointer)); + type_create_alias("c_long", &t_cl, type_signed_int_by_bitsize(target->width_c_long)); + type_create_alias("c_longlong", &t_cll, type_signed_int_by_bitsize(target->width_c_long_long)); } From 60c60a3205f7f1bbe76599b9e1bfbdaa9b42d3b4 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 5 Apr 2020 11:31:05 +0200 Subject: [PATCH 4/8] Enum fixes --- resources/testfragments/super_simple.c3 | 53 ++++++++++----------- resources/tests/enum_errors.c3 | 24 ++++++++++ resources/tests/enum_ok.c3 | 6 +++ src/compiler/casts.c | 63 ++++++++++++++++++++++--- src/compiler/compiler_internal.h | 4 +- src/compiler/llvm_codegen_expr.c | 4 -- src/compiler/llvm_codegen_type.c | 2 +- src/compiler/number.c | 7 --- src/compiler/parse_expr.c | 2 + src/compiler/parser.c | 4 +- src/compiler/sema_decls.c | 4 +- src/compiler/sema_expr.c | 6 +-- src/compiler/sema_stmts.c | 56 +++++++++++++--------- src/compiler/sema_types.c | 2 +- src/compiler/types.c | 1 + 15 files changed, 160 insertions(+), 78 deletions(-) create mode 100644 resources/tests/enum_errors.c3 create mode 100644 resources/tests/enum_ok.c3 diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index a6055f864..9e937fdad 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -38,7 +38,9 @@ struct Teob int oekfeo; } -enum EnumTest : long +typedef long as Frob; + +enum EnumTestAlias : Frob { VALUE1 = 4, VALUE2 @@ -69,37 +71,15 @@ enum EnumWithData : ushort (int a, char[] x, long b = 4) TEST2(12, "world") } - -/* ERRORS - -enum EnumWithErrorData : int (int -{ - TEST -} - -enum EnumWithErrorWithMissingName : int (int) -{ - TEST -} - +/* enum EnumTestNoOverflowAfterULong : ulong { VALUE = 0xFFFF_FFFF_FFFF_FFFE, VALUE_NO_EXCEED } -enum EnumTestOverflow -{ - VALUE = 0x80000000, -} -enum EnumTestOverflowAfter -{ - VALUE = 0x80000000 - 1, - VALUE_EXCEED -} - enum EnumTestOverflowAfterLong : long { VALUE = 0x7FFF_FFFF_FFFF_FFFF, @@ -112,12 +92,14 @@ enum EnumTestOverflowAfterULong : ulong VALUE_EXCEED } -enum EnumTestErrorType : float +enum EnumTestOverflowAfter { - VALUE_BOOM -} + VALUE = 0x80000000 - 1, + VALUE_EXCEED +}*/ + + -*/ error Error { @@ -156,6 +138,21 @@ func void enumInferenceTest() bool y = x1 == x1; Inf2 z = C; if (z == Inf2.A) return; + if (z == 1) return; + z = 2; + switch (z) + { + case Inf2.A: + x1++; + return; + case B: + return; + case 111: + x1 += 1; + return; + default: + return; + } } func int jumptest() diff --git a/resources/tests/enum_errors.c3 b/resources/tests/enum_errors.c3 new file mode 100644 index 000000000..7836596c9 --- /dev/null +++ b/resources/tests/enum_errors.c3 @@ -0,0 +1,24 @@ + +enum EnumWithErrorWithMissingName : int (int) +// @error The function parameter must be named +{ + TEST +} + +enum EnumWithErrorData : int (int +// @error Unexpected end of parameter list +{ + TEST +} + +enum EnumTestOverflow +{ + VALUE = 0x80000000, +// @error does not fit into 'int' +} + +enum EnumTestErrorType : float +// @error The enum type must be an integer type not 'float' +{ + VALUE_BOOM +} \ No newline at end of file diff --git a/resources/tests/enum_ok.c3 b/resources/tests/enum_ok.c3 new file mode 100644 index 000000000..47bee6e57 --- /dev/null +++ b/resources/tests/enum_ok.c3 @@ -0,0 +1,6 @@ +enum EnumTest : long +{ + VALUE1 = 4, + VALUE2 +} + diff --git a/src/compiler/casts.c b/src/compiler/casts.c index 65c29fdf3..53a8e4542 100644 --- a/src/compiler/casts.c +++ b/src/compiler/casts.c @@ -302,13 +302,24 @@ bool ixxxi(Expr *left, Type *canonical, Type *type, CastType cast_type) return true; } +/** + * Convert from compile time int to any signed or unsigned int + * @return true unless the conversion was lossy. + */ +bool ixxen(Expr *left, Type *canonical, Type *type, CastType cast_type) +{ + assert(canonical->type_kind == TYPE_ENUM); + canonical = canonical->decl->enums.type_info->type->canonical; + return ixxxi(left, canonical, type, cast_type); +} + /** * Cast signed int -> signed int * @return true if this is a widening, an explicit cast or if it is an implicit assign add */ -bool sisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +bool sisi(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type) { - bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize; + bool is_narrowing = from_canonical->builtin.bytesize > canonical->builtin.bytesize; if (is_narrowing && cast_type != CAST_TYPE_EXPLICIT) { @@ -330,9 +341,9 @@ bool sisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ * Cast unsigned int -> unsigned int * @return true if this was not a narrowing implicit assign or narrowing implicit assign add */ -bool uiui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +bool uiui(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type) { - bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize; + bool is_narrowing = from_canonical->builtin.bytesize > canonical->builtin.bytesize; if (is_narrowing && cast_type != CAST_TYPE_EXPLICIT) { @@ -355,9 +366,9 @@ bool uiui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ * Cast unsigned int -> signed int * @return true if this is an explicit cast or if it is an implicit assign add or if it is a widening cast. */ -bool uisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) +bool uisi(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type) { - bool is_widening = from->builtin.bytesize < canonical->builtin.bytesize; + bool is_widening = from_canonical->builtin.bytesize < canonical->builtin.bytesize; if (!is_widening && cast_type != CAST_TYPE_EXPLICIT) { @@ -505,9 +516,45 @@ bool usus(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ return true; } +bool xixi(Expr *left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type) +{ + assert(from_canonical->canonical == from_canonical); + switch (from_canonical->type_kind) + { + case TYPE_IXX: + return ixxxi(left, canonical, type, cast_type); + case ALL_SIGNED_INTS: + if (type_is_unsigned(canonical)) return siui(left, canonical, type, cast_type); + return sisi(left, from_canonical, canonical, type, cast_type); + case ALL_UNSIGNED_INTS: + if (type_is_unsigned(canonical)) return uiui(left, from_canonical, canonical, type, cast_type); + return uisi(left, from_canonical, canonical, type, cast_type); + default: + UNREACHABLE + } +} + bool enxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) { - TODO + Type *enum_type = from->decl->enums.type_info->type; + Type *enum_type_canonical = enum_type->canonical; + // 1. If the underlying type is the same, this is just setting the type. + if (canonical == enum_type_canonical) + { + left->type = type; + return true; + } + // 2. See if we can convert to the target type. + if (cast_type != CAST_TYPE_EXPLICIT && type_find_max_type(enum_type_canonical, canonical) != canonical) + { + if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; + SEMA_ERROR(left, "Cannot implictly convert '%s' with underlying type of '%s' to '%s'," + " use an explicit cast if this is what you want.", type_to_error_string(from), + type_to_error_string(enum_type_canonical), type_to_error_string(canonical)); + return false; + } + // 3. Dispatch to the right cast: + return xixi(left, enum_type_canonical, canonical, type, cast_type); } bool erxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) { @@ -632,6 +679,7 @@ CastKind cast_to_bool_kind(Type *type) UNREACHABLE } + bool cast(Expr *expr, Type *to_type, CastType cast_type) { Type *from_type = expr->type->canonical; @@ -656,6 +704,7 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type) if (type_is_float(canonical)) return ixxfp(expr, canonical, to_type, cast_type); if (canonical == type_bool) return ixxbo(expr, to_type); if (canonical->type_kind == TYPE_POINTER) return xipt(expr, from_type, canonical, to_type, cast_type); + if (canonical->type_kind == TYPE_ENUM) return ixxen(expr, canonical, to_type, cast_type); break; case TYPE_I8: case TYPE_I16: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 76c17a881..3360123cf 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -57,7 +57,7 @@ typedef struct Decl *error_constant; }; // Valid type kinds: - // bool, ints, floats, enum, error, string + // bool, ints, floats, string TypeKind kind; } ExprConst; @@ -317,7 +317,6 @@ typedef struct { FunctionSignature function_signature; TypeInfo *type_info; - Type *type; }; } TypedefDecl; @@ -1140,6 +1139,7 @@ Type *type_find_max_type(Type *type, Type *other); static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; } static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_I64; } static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind <= TYPE_U64; } +static inline bool type_kind_is_any_integer(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_IXX; } static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64; } static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64; } static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; } diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index f07715644..a339bc32c 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -690,10 +690,6 @@ LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) 0)); return global_name; } - case TYPE_ERROR: - return LLVMConstInt(llvm_type(type_error), expr->const_expr.error_constant->error_constant.value, false); - case TYPE_ENUM: - return gencontext_emit_expr(context, expr->const_expr.enum_constant->enum_constant.expr); default: UNREACHABLE } diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 896d6c0c2..749801279 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -38,7 +38,7 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl } case DECL_TYPEDEF: - return llvm_get_type(context, decl->typedef_decl.type); + return llvm_get_type(context, decl->typedef_decl.type_info->type); case DECL_STRUCT: { LLVMTypeRef *types = NULL; diff --git a/src/compiler/number.c b/src/compiler/number.c index 21775d95a..97b758edf 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -189,13 +189,6 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp } is_eq = strncmp(left->string.chars, right->string.chars, left->string.len); break; - case TYPE_ERROR: - assert(left->error_constant->type == right->error_constant->type); - is_eq = left->error_constant == right->error_constant; - break; - case TYPE_ENUM: - assert(left->enum_constant->type == right->enum_constant->type); - return expr_const_compare(&left->enum_constant->enum_constant.expr->const_expr, &right->enum_constant->enum_constant.expr->const_expr, op); default: UNREACHABLE } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 1c2d0e1a8..381118072 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -324,6 +324,8 @@ static Expr *parse_access_expr(Context *context, Expr *left) access_expr->access_expr.parent = left; access_expr->access_expr.sub_element = context->tok; TRY_CONSUME_OR(TOKEN_IDENT, "Expected identifier", &poisoned_expr); + access_expr->span = left->span; + access_expr->span.end_loc = access_expr->access_expr.sub_element.span.end_loc; return access_expr; } diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 8b94f2a77..eb4691057 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -891,7 +891,7 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili { if (context->tok.type != TOKEN_COMMA && context->tok.type != TOKEN_RPAREN) { - SEMA_TOKEN_ERROR(context->tok, "Unexpected end of the parameter list, did you forget an ')'?"); + sema_error_at(context->prev_tok_end, "Unexpected end of the parameter list, did you forget an ')'?"); return false; } SEMA_ERROR(type, "The function parameter must be named."); @@ -1955,6 +1955,7 @@ void parse_file(Context *context) static Expr *parse_type_access(Context *context, TypeInfo *type) { Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE_ACCESS, context->tok); + expr->span = type->span; expr->type_access.type = type; advance_and_verify(context, TOKEN_DOT); @@ -1966,6 +1967,7 @@ static Expr *parse_type_access(Context *context, TypeInfo *type) case TOKEN_IDENT: case TOKEN_CONST_IDENT: advance(context); + RANGE_EXTEND_PREV(expr); return expr; default: SEMA_TOKEN_ERROR(context->tok, "Expected a function name, macro, or constant."); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index f0f96ec59..68bb9cde3 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -235,7 +235,7 @@ static inline bool sema_analyse_typedef(Context *context, Decl *decl) return true; } if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false; - decl->type->canonical = decl->typedef_decl.type_info->type; + decl->type->canonical = decl->typedef_decl.type_info->type->canonical; // Do we need anything else? return true; } @@ -246,7 +246,7 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) if (!sema_resolve_type_info(context, decl->enums.type_info)) return false; Type *type = decl->enums.type_info->type; - Type *canonical = decl->enums.type_info->type; + Type *canonical = type->canonical; // Require an integer type if (!type_is_integer(canonical)) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index fdf5541de..ab86eb7d7 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -124,8 +124,7 @@ static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, { assert(enum_constant->resolve_status == RESOLVE_DONE); expr->type = enum_constant->type; - expr->const_expr.kind = TYPE_ENUM; - expr->const_expr.enum_constant = enum_constant; + expr->const_expr = enum_constant->enum_constant.expr->const_expr; expr->expr_kind = EXPR_CONST; return true; } @@ -143,8 +142,7 @@ static inline bool sema_expr_analyse_error_constant(Expr *expr, const char *name assert(error_constant->resolve_status == RESOLVE_DONE); expr->type = decl->type; expr->expr_kind = EXPR_CONST; - expr->const_expr.kind = TYPE_ERROR; - expr->const_expr.error_constant = error_constant; + expr_const_set_int(&expr->const_expr, error_constant->error_constant.value, type_error->canonical->type_kind); return true; } } diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 0220ef830..e87ba53db 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -550,29 +550,40 @@ static bool sema_analyse_ct_if_stmt(Context *context, Ast *statement) } } - +/** + * Cast the case expression to the switch type and ensure it is constant. + * + * @return true if the analysis succeeds. + */ static bool sema_analyse_case_expr(Context *context, Type* to_type, Ast *case_stmt) { + assert(to_type); Expr *case_expr = case_stmt->case_stmt.expr; - // TODO handle enums - // TODO string expr - if (!sema_analyse_expr_of_required_type(context, to_type, case_expr)) return false; + + // 1. Try to do implicit conversion to the correct type. + if (!sema_analyse_expr(context, to_type, case_expr)) return false; + + // 2. Skip continued analysis if it's not constant. if (case_expr->expr_kind != EXPR_CONST) { - SEMA_ERROR(case_expr, "This must be a constant expression."); + SEMA_ERROR(case_expr, "A case value must always be constant at compile time."); return false; } - if (!cast_to_runtime(case_expr)) return false; + Type *case_type = case_expr->type->canonical; + Type *to_type_canonical = to_type->canonical; - if (case_expr->const_expr.kind == TYPE_BOOL) return true; + // 3. If we already have the same type we're done. + if (to_type_canonical == case_type) return true; - if (!type_is_integer(case_expr->type)) + // 4. Otherwise check if we have an enum receiving type and a number on + // in the case. In that case we do an implicit conversion. + if (to_type_canonical->type_kind == TYPE_ENUM && type_is_any_integer(case_expr->type)) { - SEMA_ERROR(case_expr, "The 'case' value must be a boolean or integer constant."); - return false; + return cast(case_expr, to_type, CAST_TYPE_EXPLICIT); } - return true; + + return cast_implicit(case_expr, to_type); } @@ -614,15 +625,23 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) bool success = sema_analyse_cond(context, cond, false); Type *switch_type = ast_cond_type(cond)->canonical; - if (switch_type == type_bool || !type_is_integer(switch_type)) + switch (switch_type->type_kind) { - SEMA_ERROR(cond, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type)); - return false; + case ALL_INTS: + assert(switch_type->type_kind != TYPE_IXX); + case TYPE_BOOL: + case TYPE_ERROR: + case TYPE_META_TYPE: + case TYPE_ENUM: + case TYPE_STRING: + break; + default: + SEMA_ERROR(cond, "It is not possible to switch over '%s'.", type_to_error_string(switch_type)); + return false; } Ast *default_case = NULL; assert(context->current_scope->defers.start == context->current_scope->defers.end); - // TODO enum, exhaustive cases. ExitType prev_exit = context->current_scope->exit; bool exhaustive = false; ExitType lowest_exit = EXIT_NONE; @@ -693,15 +712,10 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) } context_pop_defers_and_replace_ast(context, statement); if (lowest_exit <= EXIT_BREAK) lowest_exit = prev_exit; + // Check exhaustive use. context->current_scope->exit = exhaustive ? lowest_exit : EXIT_NONE; context_pop_scope(context); if (!success) return false; - // Is this a typeless switch value? - if (switch_type->type_kind == TYPE_IXX) - { - - TODO - } return success; } diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 7b23699f3..127469761 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -93,7 +93,7 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) return type_info_poison(type_info); } DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string); - type_info->type = decl->type; + type_info->type = decl->typedef_decl.type_info->type; type_info->resolve_status = RESOLVE_DONE; return true; case DECL_POISONED: diff --git a/src/compiler/types.c b/src/compiler/types.c index d3fe193e5..0b8e6cb1c 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -643,6 +643,7 @@ Type *type_find_max_type(Type *type, Type *other) case TYPE_U16: case TYPE_U32: case TYPE_U64: + if (other->type_kind == TYPE_ENUM) return type_find_max_type(type, other->decl->enums.type_info->type->canonical); case TYPE_F32: case TYPE_F64: case TYPE_FXX: From 400c38b95bf3db9fd5c1920f6a60e47d9dfff687 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 6 Apr 2020 13:45:58 +0200 Subject: [PATCH 5/8] Use correct sizes and alignments. Fix designated initializers and literals. Added todo --- missing.txt | 65 ++++++++ resources/testfragments/super_simple.c3 | 71 ++++---- resources/tests/comments_ok.c3 | 13 ++ resources/tests/enum_ok.c3 | 25 +++ resources/tests/error_decl_ok.c3 | 12 ++ resources/tests/structs_ok.c3 | 0 resources/tests/typedef_errors.c3 | 5 + resources/tests/typedefs_ok.c3 | 14 ++ src/compiler/ast.c | 27 ++- src/compiler/compiler_internal.h | 34 +++- src/compiler/enums.h | 3 +- src/compiler/llvm_codegen_expr.c | 57 +++++-- src/compiler/llvm_codegen_stmt.c | 4 +- src/compiler/llvm_codegen_type.c | 2 +- src/compiler/parse_expr.c | 3 +- src/compiler/parser.c | 8 +- src/compiler/sema_decls.c | 55 +++++- src/compiler/sema_expr.c | 212 ++++++++++++++++++------ src/compiler/target.c | 2 + src/compiler/target.h | 18 +- src/compiler/types.c | 68 +++++--- src/target_info/target_info.c | 1 + src/utils/lib.h | 1 + 23 files changed, 537 insertions(+), 163 deletions(-) create mode 100644 missing.txt create mode 100644 resources/tests/comments_ok.c3 create mode 100644 resources/tests/error_decl_ok.c3 create mode 100644 resources/tests/structs_ok.c3 create mode 100644 resources/tests/typedef_errors.c3 create mode 100644 resources/tests/typedefs_ok.c3 diff --git a/missing.txt b/missing.txt new file mode 100644 index 000000000..65f37dd17 --- /dev/null +++ b/missing.txt @@ -0,0 +1,65 @@ +Things missing: + +* Attributes +- All types: @noreflect +- Struct: @packed, @aligned, @opaque +- Enums: @distinct, @noreflect +- Unions: @packed, @aligned, @opaque +- Functions: @inline, @reflect, @noreturn, @section, @unused, @used, @interrupt, @naked, @convention() +- Calls: @noinline +- Variables, parameters: @unused +- Constants, globals: @unused, @used, @section + +* Designated initializer +- Array initializer +- Array range initializer { [1..2] = 2 } + +* Initializers +- Array initializers +- Union initializers + +* Asserts +- assert, $assert +- @unreachable + +* Types +- Vararrays +- Strings +- Array +- Slice +- Values: size, alignment, name, qualifiedName +- Functions: offsetof +- Distinct types +- Simd types? +- Complex types? +- Subtype casts +- Bitstruct +- Enumset +- Typeid +- Union usage + +* Expressions +- Disallow x >= 0 and x < 0 on unsigned types unless in a macro. + +* Switch +- String switch +- Range case + +* Functions +- Varargs +- C ABI +- Safe varargs + +* Pre-post conditions +- Breakdown here + +* Error handling +- Error unions +- Catch/try +- Function return channel + +* Enum +- Values: min, max, array +- Functions: fomOrdinal, ordinal, fromName, name, fromFullName, fullName, fromQualifiedName, qualifiedName, (), fromValue() + + diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 9e937fdad..811c1a86f 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -2,16 +2,6 @@ module bar; typedef int as Bob; -/* hello *//* there */ -/+ why /+ you /* lucky +/ +/ -// Whut -// Here -// -//--- -/* - Hello - */ - struct Test { int a; @@ -38,32 +28,6 @@ struct Teob int oekfeo; } -typedef long as Frob; - -enum EnumTestAlias : Frob -{ - VALUE1 = 4, - VALUE2 -} - -enum EnumTestDefault -{ - VALUE, - VALUE2 -} - -enum EnumTestNoOverflowAfterLong : long -{ - VALUE = 0x7FFF_FFFF_FFFF_FFFE, - VALUE_NO_EXCEED -} - -enum EnumTestSmall : ushort -{ - VALUE = 0xFF, - VALUE2 = 0xFFFF -} - enum EnumWithData : ushort (int a, char[] x, long b = 4) { // Currently the args are ignored TODO! @@ -105,8 +69,8 @@ error Error { BLURB, NO_SUCH_FILE, - } + error OtherError { FOO_BAR @@ -128,6 +92,39 @@ enum Inf2 : byte typedef Inf as BooInf; +struct TestStruct +{ + int a; +} + +struct TestStruct2 +{ + TestStruct a; + char xx; + TestStruct b; + int c; +} + +union TestUnion +{ + int a; + double f; +} + + +func TestStruct structTest(int i) +{ + + TestUnion tu; +/* TestStruct foo = { i }; + TestStruct foo2 = { a = i };*/ + TestStruct foo3 = TestStruct { i }; + return foo3; +/* TestStruct2 bar = { c = 2 }; + int x = 3 * i; + TestStruct2 bar2 = { b.a = x, a.a = x + 1 }; + return bar2;*/ +} func void enumInferenceTest() { OtherError e = OtherError.FOO_BAR; diff --git a/resources/tests/comments_ok.c3 b/resources/tests/comments_ok.c3 new file mode 100644 index 000000000..4e22e310f --- /dev/null +++ b/resources/tests/comments_ok.c3 @@ -0,0 +1,13 @@ +module comments; +/* Span *//* style */ + +/+ Nested /+ Errors /* Inside +/ +/ +// Single line +/* + Multiline span style + */ + +func void test() +{ + return; +} \ No newline at end of file diff --git a/resources/tests/enum_ok.c3 b/resources/tests/enum_ok.c3 index 47bee6e57..4fc723771 100644 --- a/resources/tests/enum_ok.c3 +++ b/resources/tests/enum_ok.c3 @@ -4,3 +4,28 @@ enum EnumTest : long VALUE2 } +typedef long as Frob; + +enum EnumTestAlias : Frob +{ + VALUE1 = 4, + VALUE2 +} + +enum EnumTestDefault +{ + VALUE, + VALUE2 +} + +enum EnumTestNoOverflowAfterLong : long +{ + VALUE = 0x7FFF_FFFF_FFFF_FFFE, + VALUE_NO_EXCEED +} + +enum EnumTestSmall : ushort +{ + VALUE = 0xFF, + VALUE2 = 0xFFFF +} diff --git a/resources/tests/error_decl_ok.c3 b/resources/tests/error_decl_ok.c3 new file mode 100644 index 000000000..e9ced8885 --- /dev/null +++ b/resources/tests/error_decl_ok.c3 @@ -0,0 +1,12 @@ +module errors; + +error TheError +{ + FOO_MISSING, + NO_SUCH_FILE, +} + +error OtherError +{ + BAR_OVERFLOWED +} diff --git a/resources/tests/structs_ok.c3 b/resources/tests/structs_ok.c3 new file mode 100644 index 000000000..e69de29bb diff --git a/resources/tests/typedef_errors.c3 b/resources/tests/typedef_errors.c3 new file mode 100644 index 000000000..49180f6d2 --- /dev/null +++ b/resources/tests/typedef_errors.c3 @@ -0,0 +1,5 @@ +module typedefs; + +typedef Loop as Loop2; +typedef Loop2 as Loop3; +typedef Loop3 as Loop; diff --git a/resources/tests/typedefs_ok.c3 b/resources/tests/typedefs_ok.c3 new file mode 100644 index 000000000..c1a2c397e --- /dev/null +++ b/resources/tests/typedefs_ok.c3 @@ -0,0 +1,14 @@ +module typedefs; + +// Standard case +typedef int as Foo; + +// Nested resolution +typedef AType as BType; +typedef int as AType; + +enum Bar : BType +{ + A, + B +} diff --git a/src/compiler/ast.c b/src/compiler/ast.c index d2be7fee5..1a0437c3e 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -551,12 +551,6 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) fprint_expr_common(file, expr, indent + 1); fprint_type_info_recursive(file, expr->type_access.type, indent + 1); break; - case EXPR_STRUCT_VALUE: - fprintf_indented(file, indent, "(structvalue\n"); - fprint_expr_common(file, expr, indent + 1); - fprint_type_info_recursive(file, expr->struct_value_expr.type, indent + 1); - fprint_expr_recursive(file, expr->struct_value_expr.init_expr, indent + 1); - break; case EXPR_ACCESS: fprintf_indented(file, indent, "(access .%s\n", expr->access_expr.sub_element.string); fprint_expr_common(file, expr, indent + 1); @@ -595,12 +589,27 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) fprint_expr_recursive(file, expr->ternary_expr.else_expr, indent + 1); break; case EXPR_INITIALIZER_LIST: - fprintf_indented(file, indent, "(initializerlist\n"); + fprintf_indented(file, indent, "(initializerlist "); + switch (expr->expr_initializer.init_type) + { + case INITIALIZER_UNKNOWN: + fprintf(file, "not-analyzed\n"); + break; + case INITIALIZER_ZERO: + fprintf(file, "zero\n"); + break; + case INITIALIZER_NORMAL: + fprintf(file, "normal\n"); + break; + case INITIALIZER_DESIGNATED: + fprintf(file, "designated\n"); + break; + } fprint_expr_common(file, expr, indent + 1); { - VECEACH(expr->initializer_expr, i) + VECEACH(expr->expr_initializer.initializer_expr, i) { - fprint_expr_recursive(file, expr->initializer_expr[i], indent + 1); + fprint_expr_recursive(file, expr->expr_initializer.initializer_expr[i], indent + 1); } } break; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 3360123cf..7ee85e311 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -137,7 +137,7 @@ typedef struct { unsigned char bitsize; unsigned char bytesize; - unsigned char min_alignment; + unsigned char abi_alignment; unsigned char pref_alignment; } TypeBuiltin; @@ -226,7 +226,7 @@ typedef struct typedef struct { - uint32_t alignment; + uint32_t abi_alignment; uint64_t size; Decl **members; } StructDecl; @@ -500,6 +500,32 @@ typedef struct Ast **stmts; } ExprFuncBlock; +typedef enum +{ + INITIALIZER_UNKNOWN, + INITIALIZER_ZERO, + INITIALIZER_DESIGNATED, + INITIALIZER_NORMAL +} InitializerType; + +typedef struct +{ + InitializerType init_type; + Expr** initializer_expr; +} ExprInitializer; + +typedef struct _DesignatedInitPath +{ + Decl *decl; + struct _DesignatedInitPath *sub_path; +} DesignatedInitPath; + +typedef struct +{ + DesignatedInitPath path; + Expr *value; +} ExprDesignatedInit; + struct _Expr { ExprKind expr_kind : 8; @@ -507,6 +533,7 @@ struct _Expr SourceRange span; Type *type; union { + ExprDesignatedInit designated_init_expr; ExprCast cast_expr; ExprConst const_expr; ExprStructValue struct_value_expr; @@ -522,7 +549,7 @@ struct _Expr ExprAccess access_expr; ExprIdentifier identifier_expr; ExprType type_expr; - Expr** initializer_expr; + ExprInitializer expr_initializer; Expr** expression_list; ExprScope expr_scope; ExprFuncBlock expr_block; @@ -1133,6 +1160,7 @@ bool type_is_subtype(Type *type, Type *possible_subtype); Type *type_find_common_ancestor(Type *left, Type *right); const char *type_to_error_string(Type *type); size_t type_size(Type *canonical); +size_t type_abi_alignment(Type *canonical); void type_append_signature_name(Type *type, char *dst, size_t *offset); Type *type_find_max_type(Type *type, Type *other); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 746625013..0940beec4 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -223,14 +223,13 @@ typedef enum EXPR_SIZEOF, EXPR_SUBSCRIPT, EXPR_ACCESS, - EXPR_STRUCT_VALUE, - EXPR_STRUCT_INIT_VALUES, EXPR_INITIALIZER_LIST, EXPR_EXPRESSION_LIST, EXPR_CAST, EXPR_SCOPED_EXPR, EXPR_MACRO_EXPR, EXPR_EXPR_BLOCK, + EXPR_DESIGNATED_INIT, } ExprKind; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index a339bc32c..de51838fb 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -101,12 +101,11 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) case EXPR_POST_UNARY: case EXPR_TYPE_ACCESS: case EXPR_CALL: - case EXPR_STRUCT_VALUE: - case EXPR_STRUCT_INIT_VALUES: case EXPR_INITIALIZER_LIST: case EXPR_EXPRESSION_LIST: case EXPR_CAST: case EXPR_MACRO_EXPR: + case EXPR_DESIGNATED_INIT: UNREACHABLE } UNREACHABLE @@ -740,30 +739,55 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont return value; } + + static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) { LLVMTypeRef type = llvm_type(expr->type); LLVMValueRef value = LLVMGetUndef(type); - if (!vec_size(expr->initializer_expr)) + if (expr->expr_initializer.init_type == INITIALIZER_ZERO) { LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp"); value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false), - LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.alignment); - return ref; + LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); + return value; } - VECEACH(expr->initializer_expr, i) + Expr **elements = expr->expr_initializer.initializer_expr; + + if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) { - LLVMValueRef init_value = gencontext_emit_expr(context, expr->initializer_expr[i]); - value = LLVMBuildInsertValue(context->builder, value, init_value, i, "literal"); + VECEACH(elements, i) + { + LLVMValueRef init_value = gencontext_emit_expr(context, elements[i]); + value = LLVMBuildInsertValue(context->builder, value, init_value, i, "literal"); + } + return value; } - return value; -} -static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *context, Expr *expr) -{ - TODO + LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp"); + value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false), + LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); + + VECEACH(elements, i) + { + Expr *element = elements[i]; + LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value); + Decl *parent = expr->type->decl; + DesignatedInitPath *path = &element->designated_init_expr.path; + LLVMValueRef subref = ref; + assert(element->expr_kind == EXPR_DESIGNATED_INIT); + while (path) + { + subref = LLVMBuildStructGEP2(context->builder, llvm_type(parent->type), subref, path->decl->var.id, "access"); + parent = path->decl; + path = path->sub_path; + } + LLVMBuildStore(context->builder, sub_value, subref); + } + + return ref; } static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr *expr) @@ -803,6 +827,9 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { case EXPR_POISONED: UNREACHABLE + case EXPR_DESIGNATED_INIT: + // This is handled inside of initializer setup + UNREACHABLE case EXPR_EXPR_BLOCK: return gencontext_emit_expr_block(context, expr); case EXPR_SCOPED_EXPR: @@ -831,10 +858,6 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) return gencontext_emit_call_expr(context, expr); case EXPR_ACCESS: return gencontext_emit_access_expr(context, expr); - case EXPR_STRUCT_VALUE: - return gencontext_emit_struct_value_expr(context, expr); - case EXPR_STRUCT_INIT_VALUES: - return gencontext_emit_struct_init_values_expr(context, expr); case EXPR_INITIALIZER_LIST: return gencontext_emit_initializer_list_expr(context, expr); case EXPR_EXPRESSION_LIST: diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 428e1dde6..20da33bab 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -44,11 +44,11 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) { Expr *expr = decl->var.init_expr; // Quick path for empty initializer list - if (expr->expr_kind == EXPR_INITIALIZER_LIST && vec_size(expr->initializer_expr) == 0) + if (expr->expr_kind == EXPR_INITIALIZER_LIST && expr->expr_initializer.init_type == INITIALIZER_ZERO) { LLVMBuildMemSet(context->builder, decl->var.backend_ref, LLVMConstInt(llvm_type(type_byte), 0, false), LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), - expr->type->decl->strukt.alignment); + expr->type->decl->strukt.abi_alignment); return decl->var.backend_ref; } diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 749801279..6df4a31b6 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -226,5 +226,5 @@ void llvm_set_struct_size_alignment(Decl *decl) { LLVMTypeRef type = llvm_get_type(LLVMGetGlobalContext(), decl->type); decl->strukt.size = LLVMStoreSizeOfType(target_data_layout(), type); - decl->strukt.alignment = LLVMPreferredAlignmentOfType(target_data_layout(), type); + decl->strukt.abi_alignment = LLVMPreferredAlignmentOfType(target_data_layout(), type); } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 381118072..2632df310 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -249,8 +249,9 @@ Expr *parse_initializer(Context *context) Expr *parse_initializer_list(Context *context) { Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, context->tok); + initializer_list->expr_initializer.init_type = INITIALIZER_UNKNOWN; CONSUME_OR(TOKEN_LBRACE, &poisoned_expr); - if (!parse_param_list(context, &initializer_list->initializer_expr, false)) return &poisoned_expr; + if (!parse_param_list(context, &initializer_list->expr_initializer.initializer_expr, false)) return &poisoned_expr; CONSUME_OR(TOKEN_RBRACE, &poisoned_expr); return initializer_list; } diff --git a/src/compiler/parser.c b/src/compiler/parser.c index eb4691057..b4528ac34 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -1033,7 +1033,9 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent) decl_poison(other); decl_poison(member); } + unsigned index = vec_size(parent->strukt.members); parent->strukt.members = VECADD(parent->strukt.members, member); + member->var.id = index; advance(context); if (context->tok.type != TOKEN_COMMA) break; } @@ -1994,11 +1996,7 @@ Expr *parse_type_identifier_with_path(Context *context, Path *path) RANGE_EXTEND_PREV(type); if (context->tok.type == TOKEN_LBRACE) { - Expr *expr = EXPR_NEW_TOKEN(EXPR_STRUCT_VALUE, context->tok); - expr->struct_value_expr.type = type; - expr->struct_value_expr.init_expr = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr); - - return expr; + return TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr); } EXPECT_OR(TOKEN_DOT, &poisoned_expr); return parse_type_access(context, type); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 68bb9cde3..359599e07 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -40,6 +40,8 @@ static inline bool sema_analyse_error(Context *context __unused, Decl *decl) static inline bool sema_analyse_struct_member(Context *context, Decl *decl) { + assert(decl->resolve_status == RESOLVE_NOT_DONE); + decl->resolve_status = RESOLVE_RUNNING; if (decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION) { DEBUG_LOG("Beginning analysis of inner struct/union"); @@ -74,6 +76,7 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl) } decl->type = decl->var.type_info->type; assert(decl->var.type_info->type); + decl->resolve_status = RESOLVE_DONE; return true; } @@ -430,6 +433,52 @@ static inline bool sema_analyse_generic(Context *context, Decl *decl) return true; } +static inline void sema_set_struct_size(Decl *decl) +{ + // TODO packed + uint64_t size = 0; + uint64_t alignment = 0; + VECEACH(decl->strukt.members, i) + { + Decl *member = decl->strukt.members[i]; + Type *canonical = member->type->canonical; + uint64_t member_size = type_size(canonical); + uint64_t member_alignment = type_abi_alignment(canonical); + assert(member_size > 0); + // Add padding. + if (member_alignment && (size % member_alignment)) + { + size += member_alignment - size % member_alignment; + } + // Add size. + size += member_size; + if (member_alignment > alignment) alignment = member_alignment; + } + decl->strukt.abi_alignment = alignment; + if (alignment && size % alignment) + { + size += alignment - size % alignment; + } + decl->strukt.size = size; +} + +static inline void sema_set_union_size(Decl *decl) +{ + uint64_t size = 0; + uint64_t alignment = 0; + VECEACH(decl->strukt.members, i) + { + Decl *member = decl->strukt.members[i]; + Type *canonical = member->type->canonical; + uint64_t member_size = type_size(canonical); + uint64_t member_alignment = type_abi_alignment(canonical); + if (member_size > size) size = member_size; + if (member_alignment > alignment) alignment = member_alignment; + } + decl->strukt.abi_alignment = alignment; + decl->strukt.size = size; +} + bool sema_analyse_decl(Context *context, Decl *decl) { if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); @@ -450,9 +499,13 @@ bool sema_analyse_decl(Context *context, Decl *decl) if (!sema_analyse_throws(context, decl)) return decl_poison(decl); break; case DECL_STRUCT: + if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl); + sema_set_struct_size(decl); + decl_set_external_name(decl); + break; case DECL_UNION: if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl); - llvm_set_struct_size_alignment(decl); + sema_set_union_size(decl); decl_set_external_name(decl); break; case DECL_FUNC: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index ab86eb7d7..61f7015fb 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -280,15 +280,6 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr } } -static inline bool sema_expr_analyse_struct_value(Context *context, Type *to, Expr *expr) -{ - TODO -} - -static inline bool sema_expr_analyse_struct_init_values(Context *context, Type *to, Expr *expr) -{ - TODO -} static inline bool sema_expr_analyse_subscript(Context *context, Type *to, Expr *expr) { @@ -492,6 +483,7 @@ static Decl *sema_analyse_init_identifier(Context *context, Decl *strukt, Expr * expr->resolve_status = RESOLVE_RUNNING; expr->identifier_expr.decl = sema_analyse_init_identifier_string(context, strukt, expr->identifier_expr.identifier); expr->resolve_status = RESOLVE_DONE; + expr->type = expr->identifier_expr.decl->type; return expr->identifier_expr.decl; } @@ -507,6 +499,7 @@ static Decl *sema_analyse_init_access(Context *context, Decl *strukt, Expr *acce } decl = access_expr->access_expr.ref = sema_analyse_init_identifier_string(context, decl->type->decl, access_expr->access_expr.sub_element.string); access_expr->resolve_status = RESOLVE_DONE; + access_expr->type = decl->type; return decl; } @@ -534,74 +527,189 @@ static Decl *sema_analyse_init_path(Context *context, Decl *strukt, Expr *expr) } } +static bool sema_expr_analyse_designated_init(Context *context, DesignatedInitPath *path, Decl *top, Expr *path_expr); -typedef enum +static bool sema_expr_analyse_designated_init_ident(Context *context, DesignatedInitPath *path, Decl *top, const char *name) { - INIT_SEMA_ERROR, - INIT_SEMA_NOT_FOUND, - INIT_SEMA_OK -} InitSemaResult; - -static InitSemaResult sema_expr_analyse_struct_named_initializer_list(Context *context, Decl *assigned, Expr *expr_list) -{ - VECEACH(expr_list->initializer_expr, i) + // 3. Loop through the members. + Decl **members = top->strukt.members; + VECEACH(members, i) { - Expr *expr = expr_list->initializer_expr[i]; + Decl *member = members[i]; + if (member->name == name) + { + // 4. If found, set this to the current path. + (*path) = (DesignatedInitPath) { .decl = member, .sub_path = NULL }; + + // 5. And we're done! + return true; + } + // 6. We might encounter anonymous members. Treat this as a possible "match" + if (!member->name) + { + DesignatedInitPath anon_path; + bool found = sema_expr_analyse_designated_init_ident(context, &anon_path, member, name); + if (found) + { + // 7. If found, create a copy path. + DesignatedInitPath *path_copy = malloc_arena(sizeof(DesignatedInitPath)); + *path_copy = anon_path; + (*path) = (DesignatedInitPath) { .decl = member, .sub_path = path_copy }; + return true; + } + } + } + return false; + +} +static bool sema_expr_analyse_designated_init(Context *context, DesignatedInitPath *path, Decl *top, Expr *path_expr) +{ + // Our expression will look like this for a.b[3].c = ... + // access(subscript(access(identifier))), now we need to reverse that. + switch (path_expr->expr_kind) + { + case EXPR_IDENTIFIER: + + // Resolving for an identifier: + // 1. Can't be a path attached. + if (path_expr->identifier_expr.path) return false; + + // 2. Ensure it's a union or struct + if (!decl_is_struct_type(top)) return false; + + return sema_expr_analyse_designated_init_ident(context, path, top, path_expr->identifier_expr.identifier); + + case EXPR_ACCESS: + + // Resolve for access: + + // 1. Resolve the parent path: + if (!sema_expr_analyse_designated_init(context, path, top, path_expr->access_expr.parent)) return false; + + // 2. Our new top is now lowest init path. + while (path->sub_path) + { + path = path->sub_path; + } + top = path->decl; + + // 3. Do an analysis with the identifier: + path->sub_path = malloc_arena(sizeof(DesignatedInitPath)); + return sema_expr_analyse_designated_init_ident(context, + path->sub_path, + top->type->decl, + path_expr->access_expr.sub_element.string); + case EXPR_SUBSCRIPT: + + // Resolve for subscript: + + // 1. Resolve the parent path: + if (!sema_expr_analyse_designated_init(context, path, top, path_expr->subscript_expr.expr)) return false; + + // 2. Our new top is now lowest init path. + while (path->sub_path) + { + path = path->sub_path; + } + top = path->decl; + + TODO // Analyse index etc. + default: + return false; + } +} + +static bool sema_expr_analyse_struct_designated_initializer_list(Context *context, Decl *assigned, Expr *expr_list) +{ + assert(expr_list->expr_initializer.init_type == INITIALIZER_UNKNOWN); + VECEACH(expr_list->expr_initializer.initializer_expr, i) + { + Expr *expr = expr_list->expr_initializer.initializer_expr[i]; if (expr->expr_kind != EXPR_BINARY && expr->binary_expr.operator != BINARYOP_ASSIGN) { if (i != 0) { SEMA_ERROR(expr, "Named and non-named initializers are not allowed together, please choose one or the other."); - return INIT_SEMA_ERROR; + return false; } // If there is an unexpected expression and no previous element then this is a normal initializer list. - return INIT_SEMA_NOT_FOUND; + expr_list->expr_initializer.init_type = INITIALIZER_NORMAL; + return true; } - Expr *path = expr->binary_expr.left; + Expr *path_expr = expr->binary_expr.left; Expr *value = expr->binary_expr.right; - Decl *result = sema_analyse_init_path(context, assigned, path); - if (!result) + DesignatedInitPath path; + if (!sema_expr_analyse_designated_init(context, &path, assigned, path_expr)) { if (i != 0) { - SEMA_ERROR(path, "Unexpected element when initializing '%s', did you get the name right?", assigned->name); - return INIT_SEMA_ERROR; + SEMA_ERROR(path_expr, "Unexpected element when initializing '%s', did you get the name right?", assigned->name); + return false; } - return INIT_SEMA_NOT_FOUND; + expr_list->expr_initializer.init_type = INITIALIZER_NORMAL; + return true; } - if (!sema_analyse_expr_of_required_type(context, result->type, value)) return INIT_SEMA_ERROR; + // Walk down to the last decl. + DesignatedInitPath *init_path = &path; + while (init_path->sub_path) init_path = init_path->sub_path; + + if (!sema_analyse_expr_of_required_type(context, init_path->decl->type, value)) return false; + + // Destruct the expression and replace. + expr->designated_init_expr.value = value; // Do this first! + expr->resolve_status = RESOLVE_DONE; + expr->designated_init_expr.path = path; + expr->expr_kind = EXPR_DESIGNATED_INIT; + expr->type = init_path->decl->type; } - return INIT_SEMA_OK; + expr_list->expr_initializer.init_type = INITIALIZER_DESIGNATED; + return true; } static inline bool sema_expr_analyse_struct_initializer_list(Context *context, Type *assigned, Expr *expr) { + expr->type = assigned; + Decl **members = assigned->decl->strukt.members; unsigned size = vec_size(members); - // Zero size init will initialize to empty. - if (size == 0) return true; - InitSemaResult result = sema_expr_analyse_struct_named_initializer_list(context, assigned->decl, expr); - if (result == INIT_SEMA_ERROR) return false; - if (result == INIT_SEMA_OK) + // Zero size init will initialize to empty. + if (size == 0) { - TODO + expr->expr_initializer.init_type = INITIALIZER_ZERO; + return true; } + + if (!sema_expr_analyse_struct_designated_initializer_list(context, assigned->decl, expr)) return false; + + // If we already parsed this. + if (expr->expr_initializer.init_type == INITIALIZER_DESIGNATED) return true; + + Expr **elements = expr->expr_initializer.initializer_expr; + + assert(expr->expr_initializer.init_type == INITIALIZER_NORMAL); + if (assigned->type_kind == TYPE_UNION) { - SEMA_ERROR(expr->initializer_expr[0], "Initializer list for unions must use named initializers, e.g. { a = 4 }"); + SEMA_ERROR(elements[0], "Initializer list for unions must use named initializers, e.g. { a = 4 }"); return false; } - if (size < vec_size(expr->initializer_expr)) + + VECEACH(elements, i) { - SEMA_ERROR(expr->initializer_expr[size], "Too many elements in initializer, expected only %d.", size); + if (i >= size) + { + SEMA_ERROR(elements[size], "Too many elements in initializer, expected only %d.", size); + return false; + } + if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i])) return false; + } + + if (size > vec_size(elements)) + { + SEMA_ERROR(elements[vec_size(elements) - 1], "Too few elements in initializer, expected %d.", size); return false; } - VECEACH(expr->initializer_expr, i) - { - if (!sema_analyse_expr_of_required_type(context, members[i]->type, expr->initializer_expr[i])) return false; - } - expr->type = assigned; return true; } @@ -1910,6 +2018,7 @@ static Expr *expr_shallow_copy(Expr *source) return copy; } + static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr **expr_list); static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr); static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source); @@ -1968,6 +2077,9 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex Expr *expr = expr_shallow_copy(source_expr); switch (source_expr->expr_kind) { + case EXPR_DESIGNATED_INIT: + // This type of expression is only created after analysis. + UNREACHABLE case EXPR_EXPR_BLOCK: ast_copy_list_from_macro(context, macro, &expr->expr_block.stmts); return expr; @@ -2017,15 +2129,8 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex case EXPR_ACCESS: EXPR_COPY(expr->access_expr.parent); return expr; - case EXPR_STRUCT_VALUE: - expr->struct_value_expr.type = type_info_copy_from_macro(context, macro, expr->struct_value_expr.type); - EXPR_COPY(expr->struct_value_expr.init_expr); - return expr; - case EXPR_STRUCT_INIT_VALUES: - TODO - return expr; case EXPR_INITIALIZER_LIST: - expr->initializer_expr = expr_copy_expr_list_from_macro(context, macro, expr->initializer_expr); + expr->expr_initializer.initializer_expr = expr_copy_expr_list_from_macro(context, macro, expr->expr_initializer.initializer_expr); return expr; case EXPR_EXPRESSION_LIST: expr->expression_list = expr_copy_expr_list_from_macro(context, macro, expr->expression_list); @@ -2426,6 +2531,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_POISONED: return false; case EXPR_SCOPED_EXPR: + case EXPR_DESIGNATED_INIT: UNREACHABLE case EXPR_EXPR_BLOCK: return sema_expr_analyse_expr_block(context, to, expr); @@ -2457,10 +2563,6 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * return sema_expr_analyse_subscript(context, to, expr); case EXPR_ACCESS: return sema_expr_analyse_access(context, to, expr); - case EXPR_STRUCT_VALUE: - return sema_expr_analyse_struct_value(context, to, expr); - case EXPR_STRUCT_INIT_VALUES: - return sema_expr_analyse_struct_init_values(context, to, expr); case EXPR_INITIALIZER_LIST: return sema_expr_analyse_initializer_list(context, to, expr); case EXPR_CAST: diff --git a/src/compiler/target.c b/src/compiler/target.c index 3b9a8039b..db35a19b2 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -109,6 +109,7 @@ void target_setup() LLVMTypeRef float_type = LLVMFloatType(); LLVMTypeRef double_type = LLVMDoubleType(); LLVMTypeRef quad_type = LLVMFP128Type(); + LLVMTypeRef pointer_type = LLVMPointerType(int_type, 0); build_target.align_byte = LLVMABIAlignmentOfType(build_target.llvm_data_layout, byte_type); build_target.align_short = LLVMABIAlignmentOfType(build_target.llvm_data_layout, short_type); build_target.align_int = LLVMABIAlignmentOfType(build_target.llvm_data_layout, int_type); @@ -116,6 +117,7 @@ void target_setup() build_target.align_f128 = LLVMABIAlignmentOfType(build_target.llvm_data_layout, quad_type); build_target.align_double = LLVMABIAlignmentOfType(build_target.llvm_data_layout, double_type); build_target.align_float = LLVMABIAlignmentOfType(build_target.llvm_data_layout, float_type); + build_target.align_pointer = LLVMABIAlignmentOfType(build_target.llvm_data_layout, pointer_type); build_target.little_endian = LLVMByteOrder(build_target.llvm_data_layout) == LLVMLittleEndian; build_target.width_c_short = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_SHORT); build_target.width_c_int = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_INT); diff --git a/src/compiler/target.h b/src/compiler/target.h index d7c8058a7..89cf5b10a 100644 --- a/src/compiler/target.h +++ b/src/compiler/target.h @@ -151,15 +151,15 @@ typedef struct bool asm_supported; bool float_128; bool float_16; - unsigned align_min_pointer; - unsigned align_min_byte; - unsigned align_min_short; - unsigned align_min_int; - unsigned align_min_long; - unsigned align_min_half; - unsigned align_min_float; - unsigned align_min_double; - unsigned align_min_f128; + unsigned align_pref_pointer; + unsigned align_pref_byte; + unsigned align_pref_short; + unsigned align_pref_int; + unsigned align_pref_long; + unsigned align_pref_half; + unsigned align_pref_float; + unsigned align_pref_double; + unsigned align_pref_f128; unsigned align_pointer; unsigned align_byte; unsigned align_short; diff --git a/src/compiler/types.c b/src/compiler/types.c index 0b8e6cb1c..cdcc088cd 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -198,29 +198,18 @@ size_t type_size(Type *canonical) case TYPE_META_TYPE: return 0; case TYPE_ENUM: - return type_size(canonical->decl->enums.type_info->type->canonical); + return canonical->decl->enums.type_info->type->canonical->builtin.bytesize; + case TYPE_ERROR: + return type_error->canonical->builtin.bytesize; case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ERROR: - TODO + return canonical->decl->strukt.size; case TYPE_VOID: return 1; case TYPE_BOOL: - case TYPE_I8: - case TYPE_I16: - case TYPE_I32: - case TYPE_I64: - case TYPE_U8: - case TYPE_U16: - case TYPE_U32: - case TYPE_U64: - case TYPE_F32: - case TYPE_F64: + case ALL_INTS: + case ALL_FLOATS: return canonical->builtin.bytesize; - case TYPE_IXX: - return 8; - case TYPE_FXX: - return 8; case TYPE_FUNC: case TYPE_POINTER: case TYPE_VARARRAY: @@ -233,7 +222,44 @@ size_t type_size(Type *canonical) case TYPE_ERROR_UNION: TODO } - TODO + UNREACHABLE +} + +size_t type_abi_alignment(Type *canonical) +{ + assert(canonical && canonical->canonical == canonical); + switch (canonical->type_kind) + { + case TYPE_POISONED: + case TYPE_TYPEDEF: + case TYPE_VOID: + UNREACHABLE; + case TYPE_META_TYPE: + return 0; + case TYPE_ENUM: + return canonical->decl->enums.type_info->type->canonical->builtin.abi_alignment; + case TYPE_ERROR: + return type_error->canonical->builtin.abi_alignment; + case TYPE_STRUCT: + case TYPE_UNION: + return canonical->decl->strukt.abi_alignment; + case TYPE_BOOL: + case ALL_INTS: + case ALL_FLOATS: + return canonical->builtin.abi_alignment; + case TYPE_FUNC: + case TYPE_POINTER: + case TYPE_VARARRAY: + case TYPE_STRING: + return t_usz.canonical->builtin.abi_alignment; + case TYPE_ARRAY: + return type_abi_alignment(canonical->array.base); + case TYPE_SUBARRAY: + TODO + case TYPE_ERROR_UNION: + TODO + } + UNREACHABLE } static inline void create_type_cache(Type *canonical_type) @@ -375,7 +401,7 @@ static void type_create(const char *name, Type *location, TypeKind kind, unsigne .type_kind = kind, .builtin.bytesize = (bitsize + 7) / 8, .builtin.bitsize = bitsize, - .builtin.min_alignment = align, + .builtin.abi_alignment = align, .builtin.pref_alignment = pref_align, .name = name, .canonical = location, @@ -403,7 +429,7 @@ void builtin_setup(Target *target) type_string.type_kind = TYPE_STRING; */ #define DEF_TYPE(_name, _shortname, _type, _bits, _align) \ -type_create(#_name, &_shortname, _type, _bits, target->align_min_ ## _align, target->align_ ## _align) +type_create(#_name, &_shortname, _type, _bits, target->align_ ## _align, target->align_pref_ ## _align) DEF_TYPE(bool, t_u1, TYPE_BOOL, 1, byte); DEF_TYPE(float, t_f32, TYPE_F32, 32, float); @@ -424,7 +450,7 @@ type_create(#_name, &_shortname, _type, _bits, target->align_min_ ## _align, tar #undef DEF_TYPE - type_create("void*", &t_voidstar, TYPE_POINTER, target->width_pointer, target->align_min_pointer, target->align_pointer); + type_create("void*", &t_voidstar, TYPE_POINTER, target->width_pointer, target->align_pref_pointer, target->align_pointer); create_type_cache(type_void); type_void->type_cache[0] = &t_voidstar; t_voidstar.pointer = type_void; diff --git a/src/target_info/target_info.c b/src/target_info/target_info.c index 6b0a93802..0a3186237 100644 --- a/src/target_info/target_info.c +++ b/src/target_info/target_info.c @@ -42,6 +42,7 @@ TargetInfo target_info_new() .asm_supported = false, .float_128 = false, .float_16 = false, + .align_pointer = 8, .align_byte = 8, .align_c_int = 32, .align_c_long = 32, diff --git a/src/utils/lib.h b/src/utils/lib.h index eb73ba974..c86aabbf7 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -267,6 +267,7 @@ static inline void* _expand(void *vec, size_t element_size) #define VECEACH(_vec, _index) \ for (unsigned _index = 0, __vecsize = vec_size(_vec); _index < __vecsize; _index++) + #define VECNEW(_type, _capacity) ((_type *)(_vec_new(sizeof(_type), _capacity) + 1)) #define VECADD(_vec, _value) \ ({ \ From 96c8c77e892bbfdbe476a5bf9b97a0c504adfea0 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 6 Apr 2020 23:48:51 +0200 Subject: [PATCH 6/8] Work on unions and anonymous structs/unions. --- missing.txt | 11 +- resources/testfragments/super_simple.c3 | 59 ++++++++-- src/compiler/ast.c | 1 - src/compiler/compiler_internal.h | 30 ++++- src/compiler/llvm_codegen_expr.c | 150 +++++++++++++++--------- src/compiler/llvm_codegen_type.c | 24 ++-- src/compiler/parser.c | 13 +- src/compiler/sema_decls.c | 103 ++++++++-------- src/compiler/sema_expr.c | 17 +-- 9 files changed, 265 insertions(+), 143 deletions(-) diff --git a/missing.txt b/missing.txt index 65f37dd17..e22a5a02c 100644 --- a/missing.txt +++ b/missing.txt @@ -1,14 +1,15 @@ Things missing: * Attributes -- All types: @noreflect +- All types: @noreflect, @deprecated - Struct: @packed, @aligned, @opaque - Enums: @distinct, @noreflect - Unions: @packed, @aligned, @opaque - Functions: @inline, @reflect, @noreturn, @section, @unused, @used, @interrupt, @naked, @convention() -- Calls: @noinline +- Calls: @noinline, @inline - Variables, parameters: @unused - Constants, globals: @unused, @used, @section +- Labels: @unused * Designated initializer - Array initializer @@ -17,6 +18,7 @@ Things missing: * Initializers - Array initializers - Union initializers +- Initializers with anonymous members * Asserts - assert, $assert @@ -36,7 +38,10 @@ Things missing: - Bitstruct - Enumset - Typeid -- Union usage + +* Struct / union +- Cast to union? +- Structural typed anonymous structs and casts to them. * Expressions - Disallow x >= 0 and x < 0 on unsigned types unless in a macro. diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 811c1a86f..d397b426a 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -106,24 +106,67 @@ struct TestStruct2 } union TestUnion +{ + int a; + double f; + TestStruct2 e; +} +union SimpleUnion { int a; double f; } - -func TestStruct structTest(int i) +struct AnonStruct { + int a; + struct sune + { + int b; + int c; + } + struct + { + int b1; + int c1; + } + union + { + int b2; + int c2; + } + int x; +} - TestUnion tu; -/* TestStruct foo = { i }; - TestStruct foo2 = { a = i };*/ +func void testAnonStruct() +{ + AnonStruct s = { b2 = 3, b1 = 7, sune.b = 1 }; + s.sune.b = 1; + s.b1 = 2; + s.b2 = 3; + s.c2 = 4; + +} + +func void testUnion() +{ + SimpleUnion s; + s.a = 1; + s.f = 1.0; + //s = { f = 1.0 }; + TestUnion tu = { e = TestStruct2 { c = 1 } }; + tu.e = TestStruct2 { c = 1 }; +} + +func TestStruct2 structTest(int i) +{ + TestStruct foo = { i }; + TestStruct foo2 = { a = i }; TestStruct foo3 = TestStruct { i }; - return foo3; -/* TestStruct2 bar = { c = 2 }; + TestStruct2 bar = { c = 2 }; int x = 3 * i; TestStruct2 bar2 = { b.a = x, a.a = x + 1 }; - return bar2;*/ + return bar2; } func void enumInferenceTest() { diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 1a0437c3e..657a2cb18 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -8,7 +8,6 @@ static void fprint_asts_recursive(FILE *file, Ast **asts, int indent); Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility) { - assert(name.string); Decl *decl = CALLOCS(Decl); decl->decl_kind = decl_kind; decl->name_span = name.span; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 7ee85e311..ddce8d6a3 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -227,6 +227,7 @@ typedef struct typedef struct { uint32_t abi_alignment; + uint32_t id; uint64_t size; Decl **members; } StructDecl; @@ -237,7 +238,11 @@ typedef struct _VarDecl unsigned id : 16; VarDeclKind kind : 3; TypeInfo *type_info; - Expr *init_expr; + union + { + Expr *init_expr; + Decl *parent; + }; void *backend_ref; void *backend_debug_ref; } VarDecl; @@ -364,7 +369,11 @@ typedef struct _Decl { struct { - Decl** method_functions; + union + { + Decl* parent_struct; + Decl** method_functions; + }; union { ErrorDecl error; @@ -460,8 +469,6 @@ typedef struct Token sub_element; Decl *ref; }; - // TODO cleanup - int index; } ExprAccess; typedef struct @@ -997,7 +1004,6 @@ bool cast_to_runtime(Expr *expr); void cast_to_smallest_runtime(Expr *expr); void llvm_codegen(Context *context); -void llvm_set_struct_size_alignment(Decl *decl); bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr); @@ -1182,6 +1188,20 @@ static inline Type *type_reduced(Type *type) return canonical; } +static inline bool type_is_structlike(Type *type) +{ + assert(type->canonical = type); + switch (type->type_kind) + { + case TYPE_UNION: + case TYPE_STRUCT: + return true; + default: + return false; + + } +} + static inline Type *type_reduced_from_expr(Expr *expr) { return type_reduced(expr->type); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index de51838fb..d9e5239d4 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -54,10 +54,38 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E } } +static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member) +{ + unsigned index; + Decl *current_parent; + if (decl_is_struct_type(member)) + { + index = member->strukt.id; + current_parent = member->parent_struct; + } + else + { + index = member->var.id; + current_parent = member->var.parent; + } + assert(current_parent); + if (parent != current_parent) + { + value = gencontext_emit_member_addr(context, value, parent, current_parent); + } + + if (current_parent->decl_kind == DECL_UNION) + { + return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), ""); + } + return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, index, ""); +} static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr *expr) { - LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent); - return LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, ""); + Expr *parent = expr->access_expr.parent; + LLVMValueRef value = gencontext_emit_address(context, parent); + Decl *member = expr->access_expr.ref; + return gencontext_emit_member_addr(context, value, parent->type->canonical->decl, member); } LLVMValueRef gencontext_emit_scoped_expr(GenContext *context, Expr *expr) @@ -177,6 +205,70 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical); } +static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) +{ + LLVMTypeRef type = llvm_type(expr->type); + LLVMValueRef ref = gencontext_emit_alloca(context, type, "literal"); + + if (expr->expr_initializer.init_type == INITIALIZER_ZERO) + { + LLVMBuildMemSet(context->builder, + ref, + LLVMConstInt(llvm_type(type_byte), 0, false), + LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), + expr->type->decl->strukt.abi_alignment); + return ref; + } + + Expr **elements = expr->expr_initializer.initializer_expr; + + bool is_union = expr->type->canonical->type_kind == TYPE_UNION; + if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) + { + assert(!is_union); + VECEACH(elements, i) + { + Expr *element = elements[i]; + LLVMValueRef init_value = gencontext_emit_expr(context, element); + LLVMValueRef subref = LLVMBuildStructGEP2(context->builder, type, ref, i, ""); + LLVMBuildStore(context->builder, init_value, subref); + } + return ref; + } + + + // Clear the temp. + LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false), + LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); + + + VECEACH(elements, i) + { + Expr *element = elements[i]; + LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value); + Decl *parent = expr->type->decl; + DesignatedInitPath *path = &element->designated_init_expr.path; + LLVMValueRef subref = ref; + assert(element->expr_kind == EXPR_DESIGNATED_INIT); + // If this is a union, we down a step to begin with. + if (is_union) + { + subref = LLVMBuildBitCast(context->builder, ref, LLVMPointerType(llvm_type(path->decl->type), 0), "unioncast"); + parent = path->decl; + path = path->sub_path; + } + while (path) + { + subref = LLVMBuildStructGEP2(context->builder, llvm_type(parent->type), subref, path->decl->var.id, "access"); + parent = path->decl; + path = path->sub_path; + } + LLVMBuildStore(context->builder, sub_value, subref); + } + + return ref; +} + static inline LLVMValueRef gencontext_emit_inc_dec_change(GenContext *context, bool use_mod, LLVMValueRef current_value, Expr *expr, int diff) { Type *type = type_reduced_from_expr(expr); @@ -580,10 +672,6 @@ static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr) if (binary_op == BINARYOP_ASSIGN) { LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left); - if (expr->binary_expr.right->expr_kind == EXPR_INITIALIZER_LIST) - { - return gencontext_emit_initialization_from_expr(context, addr, expr->binary_expr.right); - } LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right); LLVMBuildStore(context->builder, value, addr); return value; @@ -725,7 +813,7 @@ static inline LLVMValueRef gencontext_emit_access_expr(GenContext *context, Expr { // Improve, add string description to the access? LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent); - LLVMValueRef val = LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, ""); + LLVMValueRef val = LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, expr->access_expr.ref->var.id, ""); return LLVMBuildLoad2(context->builder, gencontext_get_llvm_type(context, expr->type), val, ""); } @@ -741,54 +829,6 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont -static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) -{ - LLVMTypeRef type = llvm_type(expr->type); - LLVMValueRef value = LLVMGetUndef(type); - - if (expr->expr_initializer.init_type == INITIALIZER_ZERO) - { - LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp"); - value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false), - LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); - return value; - } - - Expr **elements = expr->expr_initializer.initializer_expr; - - if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) - { - VECEACH(elements, i) - { - LLVMValueRef init_value = gencontext_emit_expr(context, elements[i]); - value = LLVMBuildInsertValue(context->builder, value, init_value, i, "literal"); - } - return value; - } - - LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp"); - value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false), - LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); - - VECEACH(elements, i) - { - Expr *element = elements[i]; - LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value); - Decl *parent = expr->type->decl; - DesignatedInitPath *path = &element->designated_init_expr.path; - LLVMValueRef subref = ref; - assert(element->expr_kind == EXPR_DESIGNATED_INIT); - while (path) - { - subref = LLVMBuildStructGEP2(context->builder, llvm_type(parent->type), subref, path->decl->var.id, "access"); - parent = path->decl; - path = path->sub_path; - } - LLVMBuildStore(context->builder, sub_value, subref); - } - - return ref; -} static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr *expr) { diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 6df4a31b6..e8e66bc21 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -52,20 +52,28 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl } case DECL_UNION: { - LLVMTypeRef max_type = NULL; + Decl *max_type = NULL; unsigned long long max_size = 0; VECEACH(decl->strukt.members, i) { - LLVMTypeRef type = llvm_get_type(context, decl->strukt.members[i]->type); - unsigned long long size = LLVMStoreSizeOfType(target_data_layout(), type); + Decl *member = decl->strukt.members[i]; + unsigned size = type_size(member->type); if (size > max_size || !max_type) { max_size = size; - max_type = type; + max_type = member; } } LLVMTypeRef type = LLVMStructCreateNamed(context, decl->external_name); - LLVMStructSetBody(type, &max_type, 1, false); + if (max_type) + { + LLVMTypeRef type_ref = llvm_get_type(context, max_type->type); + LLVMStructSetBody(type, &type_ref, 1, false); + } + else + { + LLVMStructSetBody(type, NULL, 0, true); + } return type; } case DECL_ENUM: @@ -222,9 +230,3 @@ LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type) return llvm_get_type(context->context, type); } -void llvm_set_struct_size_alignment(Decl *decl) -{ - LLVMTypeRef type = llvm_get_type(LLVMGetGlobalContext(), decl->type); - decl->strukt.size = LLVMStoreSizeOfType(target_data_layout(), type); - decl->strukt.abi_alignment = LLVMPreferredAlignmentOfType(target_data_layout(), type); -} diff --git a/src/compiler/parser.c b/src/compiler/parser.c index b4528ac34..d04af80a0 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -989,11 +989,11 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent) Decl *member; if (context->next_tok.type != TOKEN_IDENT) { - Token name_replacement = context->tok; - name_replacement.string = NULL; - member = decl_new_with_type(name_replacement, decl_kind, parent->visibility); - advance(context); - } + Token name_replacement = context->tok; + name_replacement.string = NULL; + member = decl_new_with_type(name_replacement, decl_kind, parent->visibility); + advance(context); + } else { advance(context); @@ -1010,6 +1010,8 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent) advance_and_verify(context, TOKEN_IDENT); } if (!parse_attributes(context, member)) return false; + member->parent_struct = parent; + member->strukt.id = vec_size(parent->strukt.members); parent->strukt.members = VECADD(parent->strukt.members, member); if (!parse_struct_body(context, member, context->tok.type == TOKEN_IDENT ? member : visible_parent)) { @@ -1036,6 +1038,7 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent) unsigned index = vec_size(parent->strukt.members); parent->strukt.members = VECADD(parent->strukt.members, member); member->var.id = index; + member->var.parent = parent; advance(context); if (context->tok.type != TOKEN_COMMA) break; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 359599e07..12ead3405 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -38,6 +38,53 @@ static inline bool sema_analyse_error(Context *context __unused, Decl *decl) } +static inline void sema_set_struct_size(Decl *decl) +{ + // TODO packed + uint64_t size = 0; + uint64_t alignment = 0; + VECEACH(decl->strukt.members, i) + { + Decl *member = decl->strukt.members[i]; + Type *canonical = member->type->canonical; + uint64_t member_size = type_size(canonical); + uint64_t member_alignment = type_abi_alignment(canonical); + assert(member_size > 0); + // Add padding. + if (member_alignment && (size % member_alignment)) + { + size += member_alignment - size % member_alignment; + } + // Add size. + size += member_size; + if (member_alignment > alignment) alignment = member_alignment; + } + decl->strukt.abi_alignment = alignment; + if (alignment && size % alignment) + { + size += alignment - size % alignment; + } + decl->strukt.size = size; +} + +static inline void sema_set_union_size(Decl *decl) +{ + uint64_t size = 0; + uint64_t alignment = 0; + VECEACH(decl->strukt.members, i) + { + Decl *member = decl->strukt.members[i]; + Type *canonical = member->type->canonical; + uint64_t member_size = type_size(canonical); + uint64_t member_alignment = type_abi_alignment(canonical); + if (member_size > size) size = member_size; + if (member_alignment > alignment) alignment = member_alignment; + } + decl->strukt.abi_alignment = alignment; + decl->strukt.size = size; +} + + static inline bool sema_analyse_struct_member(Context *context, Decl *decl) { assert(decl->resolve_status == RESOLVE_NOT_DONE); @@ -63,12 +110,19 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl) decl_poison(decl); } } + if (decl->decl_kind == DECL_UNION) + { + sema_set_union_size(decl); + } + else + { + sema_set_struct_size(decl); + } DEBUG_LOG("Analysis complete."); return decl_ok(decl); } assert(decl->decl_kind == DECL_VAR); assert(decl->var.kind == VARDECL_MEMBER); - assert(!decl->var.init_expr); if (!sema_resolve_type_info(context, decl->var.type_info)) { decl_poison(decl); @@ -92,7 +146,7 @@ static inline bool sema_analyse_struct_union(Context *context, Decl *decl) decl_poison(decl); continue; } - if (!sema_analyse_struct_member(context, decl->strukt.members[i])) + if (!sema_analyse_struct_member(context, member)) { if (decl_ok(decl)) { @@ -433,51 +487,6 @@ static inline bool sema_analyse_generic(Context *context, Decl *decl) return true; } -static inline void sema_set_struct_size(Decl *decl) -{ - // TODO packed - uint64_t size = 0; - uint64_t alignment = 0; - VECEACH(decl->strukt.members, i) - { - Decl *member = decl->strukt.members[i]; - Type *canonical = member->type->canonical; - uint64_t member_size = type_size(canonical); - uint64_t member_alignment = type_abi_alignment(canonical); - assert(member_size > 0); - // Add padding. - if (member_alignment && (size % member_alignment)) - { - size += member_alignment - size % member_alignment; - } - // Add size. - size += member_size; - if (member_alignment > alignment) alignment = member_alignment; - } - decl->strukt.abi_alignment = alignment; - if (alignment && size % alignment) - { - size += alignment - size % alignment; - } - decl->strukt.size = size; -} - -static inline void sema_set_union_size(Decl *decl) -{ - uint64_t size = 0; - uint64_t alignment = 0; - VECEACH(decl->strukt.members, i) - { - Decl *member = decl->strukt.members[i]; - Type *canonical = member->type->canonical; - uint64_t member_size = type_size(canonical); - uint64_t member_alignment = type_abi_alignment(canonical); - if (member_size > size) size = member_size; - if (member_alignment > alignment) alignment = member_alignment; - } - decl->strukt.abi_alignment = alignment; - decl->strukt.size = size; -} bool sema_analyse_decl(Context *context, Decl *decl) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 61f7015fb..efae32883 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -335,17 +335,19 @@ static inline bool sema_expr_analyse_method_function(Context *context, Expr *exp } -static Decl *strukt_recursive_search_member(Decl *strukt, const char *name, int *index) +static Decl *strukt_recursive_search_member(Decl *strukt, const char *name) { VECEACH(strukt->strukt.members, i) { - (*index)++; Decl *member = strukt->strukt.members[i]; if (member->name == name) return member; - if (!member->name && decl_is_struct_type(member)) + if (!member->name && type_is_structlike(member->type->canonical)) { - Decl *result = strukt_recursive_search_member(member, name, index); - if (result) return result; + Decl *result = strukt_recursive_search_member(member->type->canonical->decl, name); + if (result) + { + return result; + } } } return NULL; @@ -380,8 +382,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex default: UNREACHABLE } - int index = -1; - Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string, &index); + Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string); if (!member) { SEMA_TOKEN_ERROR(expr->access_expr.sub_element, "There is no element '%s.%s'.", decl->name, expr->access_expr.sub_element.string); @@ -397,7 +398,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex expr->access_expr.parent = deref; } expr->type = member->type; - expr->access_expr.index = index; + expr->access_expr.ref = member; return true; } From bb806716e48d8704ff243535621a96b9cc1999cc Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 8 Apr 2020 21:17:03 +0200 Subject: [PATCH 7/8] Unions and structs, including setting them should now work. --- resources/testfragments/super_simple.c3 | 8 + src/compiler/ast.c | 4 + src/compiler/compiler_internal.h | 1 + src/compiler/enums.h | 2 +- src/compiler/llvm_codegen_expr.c | 89 +++++---- src/compiler/parse_expr.c | 6 +- src/compiler/sema_expr.c | 253 ++++++++---------------- src/compiler/sema_stmts.c | 2 +- 8 files changed, 159 insertions(+), 206 deletions(-) diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index d397b426a..50cd0d882 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -138,9 +138,13 @@ struct AnonStruct int x; } + func void testAnonStruct() { + AnonStruct s = { b2 = 3, b1 = 7, sune.b = 1 }; + AnonStruct foo; + s.sune.b = 1; s.b1 = 2; s.b2 = 3; @@ -153,6 +157,9 @@ func void testUnion() SimpleUnion s; s.a = 1; s.f = 1.0; + s = { 1 }; + int x = 2; + s = { (x = 2) }; //s = { f = 1.0 }; TestUnion tu = { e = TestStruct2 { c = 1 } }; tu.e = TestStruct2 { c = 1 }; @@ -168,6 +175,7 @@ func TestStruct2 structTest(int i) TestStruct2 bar2 = { b.a = x, a.a = x + 1 }; return bar2; } + func void enumInferenceTest() { OtherError e = OtherError.FOO_BAR; diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 657a2cb18..46c466f84 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -560,6 +560,10 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) fprint_expr_common(file, expr, indent + 1); fprint_type_info_recursive(file, expr->type_expr.type, indent + 1); break; + case EXPR_GROUP: + fprintf_indented(file, indent, "(group\n"); + fprint_expr_recursive(file, expr->group_expr, indent + 1); + break; case EXPR_CALL: fprintf_indented(file, indent, "(call\n"); fprint_expr_common(file, expr, indent + 1); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index ddce8d6a3..bccdbd7d0 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -844,6 +844,7 @@ typedef struct _Context Decl **ct_ifs; Ast **defers; Decl *active_function_for_analysis; + Decl *active_type_for_analysis; Decl **last_local; Ast **labels; Ast **gotos; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 0940beec4..67656e9c6 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -220,6 +220,7 @@ typedef enum EXPR_IDENTIFIER, EXPR_TYPE_ACCESS, EXPR_CALL, + EXPR_GROUP, EXPR_SIZEOF, EXPR_SUBSCRIPT, EXPR_ACCESS, @@ -229,7 +230,6 @@ typedef enum EXPR_SCOPED_EXPR, EXPR_MACRO_EXPR, EXPR_EXPR_BLOCK, - EXPR_DESIGNATED_INIT, } ExprKind; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index d9e5239d4..467242eca 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -31,20 +31,17 @@ static inline LLVMValueRef gencontext_emit_sub_int(GenContext *context, Type *ty ? LLVMBuildNUWSub(context->builder, left, right, "usub") : LLVMBuildNSWSub(context->builder, left, right, "sub"); } - -static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, Expr *expr) +static inline LLVMValueRef gencontext_emit_subscript_addr_from_value(GenContext *context, LLVMValueRef parent, Type *parent_type, Expr *index_expr) { - LLVMValueRef index = gencontext_emit_expr(context, expr->subscript_expr.index); - Type *type = expr->subscript_expr.expr->type->canonical; - switch (type->type_kind) + assert(parent_type->canonical == parent_type); + LLVMValueRef index = gencontext_emit_expr(context, index_expr); + switch (parent_type->type_kind) { - case TYPE_ARRAY: - TODO case TYPE_POINTER: return LLVMBuildGEP2(context->builder, - llvm_type(type->pointer), - gencontext_emit_expr(context, expr->subscript_expr.expr), - &index, 1, "[]"); + llvm_type(parent_type->pointer), + parent, &index, 1, "[]"); + case TYPE_ARRAY: case TYPE_VARARRAY: case TYPE_SUBARRAY: case TYPE_STRING: @@ -52,6 +49,12 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E default: UNREACHABLE } + +} +static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, Expr *expr) +{ + LLVMValueRef parent = gencontext_emit_expr(context, expr->subscript_expr.expr); + return gencontext_emit_subscript_addr_from_value(context, parent, expr->subscript_expr.expr->type->canonical, expr->subscript_expr.index); } static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member) @@ -76,10 +79,12 @@ static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRe if (current_parent->decl_kind == DECL_UNION) { - return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), ""); + return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), "unionref"); } - return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, index, ""); + return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, index, "structref"); } + + static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr *expr) { Expr *parent = expr->access_expr.parent; @@ -119,6 +124,8 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) return gencontext_emit_subscript_addr(context, expr); case EXPR_SCOPED_EXPR: return gencontext_emit_scoped_expr_address(context, expr); + case EXPR_GROUP: + return gencontext_emit_address(context, expr->group_expr); case EXPR_CONST: case EXPR_TYPE: case EXPR_POISONED: @@ -204,6 +211,28 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * LLVMValueRef rhs = gencontext_emit_expr(context, expr->cast_expr.expr); return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical); } +static inline LLVMValueRef gencontext_emit_designated_initializer(GenContext *context, Type *parent_type, LLVMValueRef parent, Expr *expr) +{ + assert(parent_type == parent_type->canonical); + switch (expr->expr_kind) + { + case EXPR_SUBSCRIPT: + if (expr->subscript_expr.expr) + { + parent = gencontext_emit_designated_initializer(context, parent_type, parent, expr->subscript_expr.expr); + parent_type = expr->subscript_expr.expr->type->canonical; + } + return gencontext_emit_subscript_addr_from_value(context, parent, parent_type, expr->subscript_expr.index); + case EXPR_ACCESS: + parent = gencontext_emit_designated_initializer(context, parent_type, parent, expr->access_expr.parent); + parent_type = expr->subscript_expr.expr->type->canonical; + return gencontext_emit_member_addr(context, parent, parent_type->decl, expr->access_expr.ref); + case EXPR_IDENTIFIER: + return gencontext_emit_member_addr(context, parent, parent_type->decl, expr->identifier_expr.decl); + default: + UNREACHABLE + } +} static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) { @@ -225,7 +254,14 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *con bool is_union = expr->type->canonical->type_kind == TYPE_UNION; if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) { - assert(!is_union); + if (is_union) + { + assert(vec_size(elements) == 1); + LLVMValueRef init_value = gencontext_emit_expr(context, elements[0]); + LLVMValueRef u = LLVMBuildBitCast(context->builder, ref, LLVMPointerType(llvm_type(elements[0]->type->canonical), 0), ""); + LLVMBuildStore(context->builder, init_value, u); + return ref; + } VECEACH(elements, i) { Expr *element = elements[i]; @@ -245,27 +281,10 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *con VECEACH(elements, i) { Expr *element = elements[i]; - LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value); - Decl *parent = expr->type->decl; - DesignatedInitPath *path = &element->designated_init_expr.path; - LLVMValueRef subref = ref; - assert(element->expr_kind == EXPR_DESIGNATED_INIT); - // If this is a union, we down a step to begin with. - if (is_union) - { - subref = LLVMBuildBitCast(context->builder, ref, LLVMPointerType(llvm_type(path->decl->type), 0), "unioncast"); - parent = path->decl; - path = path->sub_path; - } - while (path) - { - subref = LLVMBuildStructGEP2(context->builder, llvm_type(parent->type), subref, path->decl->var.id, "access"); - parent = path->decl; - path = path->sub_path; - } - LLVMBuildStore(context->builder, sub_value, subref); + LLVMValueRef sub_value = gencontext_emit_expr(context, element->binary_expr.right); + LLVMValueRef sub_ref = gencontext_emit_designated_initializer(context, expr->type->canonical, ref, element->binary_expr.left); + LLVMBuildStore(context->builder, sub_value, sub_ref); } - return ref; } @@ -828,8 +847,6 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont } - - static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr *expr) { LLVMValueRef old_ret_out = context->return_out; @@ -896,6 +913,8 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) return gencontext_load_expr(context, gencontext_emit_address(context, expr)); case EXPR_CALL: return gencontext_emit_call_expr(context, expr); + case EXPR_GROUP: + return gencontext_emit_expr(context, expr->group_expr); case EXPR_ACCESS: return gencontext_emit_access_expr(context, expr); case EXPR_INITIALIZER_LIST: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 2632df310..bb05e6890 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -208,10 +208,12 @@ static Expr *parse_ternary_expr(Context *context, Expr *left_side) static Expr *parse_grouping_expr(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); + Expr *expr = expr_new(EXPR_GROUP, context->tok.span); advance_and_verify(context, TOKEN_LPAREN); - Expr *right = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); + expr->group_expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); - return right; + RANGE_EXTEND_PREV(expr); + return expr; } /** diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index efae32883..8894fca68 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -49,6 +49,8 @@ static bool expr_is_ltype(Expr *expr) return expr->unary_expr.operator == UNARYOP_DEREF; case EXPR_ACCESS: return expr_is_ltype(expr->access_expr.parent); + case EXPR_GROUP: + return expr_is_ltype(expr->group_expr); case EXPR_SUBSCRIPT: return true; default: @@ -172,7 +174,6 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr Decl *ambiguous_decl; Decl *decl = sema_resolve_symbol(context, expr->identifier_expr.identifier, expr->identifier_expr.path, &ambiguous_decl); - if (!decl && !expr->identifier_expr.path && to) { if (find_possible_inferred_identifier(to, expr)) return true; @@ -353,6 +354,13 @@ static Decl *strukt_recursive_search_member(Decl *strukt, const char *name) return NULL; } +static inline bool sema_expr_analyse_group(Context *context, Type *to, Expr *expr) +{ + if (!sema_analyse_expr(context, to, expr->group_expr)) return false; + *expr = *expr->group_expr; + return true; +} + static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *expr) { @@ -482,10 +490,12 @@ static Decl *sema_analyse_init_identifier(Context *context, Decl *strukt, Expr * { assert(expr->resolve_status == RESOLVE_NOT_DONE); expr->resolve_status = RESOLVE_RUNNING; - expr->identifier_expr.decl = sema_analyse_init_identifier_string(context, strukt, expr->identifier_expr.identifier); + Decl *res = sema_analyse_init_identifier_string(context, strukt, expr->identifier_expr.identifier); + if (!res) return NULL; + expr->identifier_expr.decl = res; expr->resolve_status = RESOLVE_DONE; - expr->type = expr->identifier_expr.decl->type; - return expr->identifier_expr.decl; + expr->type = res->type; + return res; } static Decl *sema_analyse_init_access(Context *context, Decl *strukt, Expr *access_expr) @@ -528,190 +538,92 @@ static Decl *sema_analyse_init_path(Context *context, Decl *strukt, Expr *expr) } } -static bool sema_expr_analyse_designated_init(Context *context, DesignatedInitPath *path, Decl *top, Expr *path_expr); - -static bool sema_expr_analyse_designated_init_ident(Context *context, DesignatedInitPath *path, Decl *top, const char *name) +static bool sema_expr_analyse_struct_designated_initializer(Context *context, Decl *assigned, Expr *initializer) { - // 3. Loop through the members. - Decl **members = top->strukt.members; - VECEACH(members, i) + Expr **init_expressions = initializer->expr_initializer.initializer_expr; + + VECEACH(init_expressions, i) { - Decl *member = members[i]; - if (member->name == name) + Expr *expr = init_expressions[i]; + + // 1. Ensure that're seeing expr = expr on the top level. + if (expr->expr_kind != EXPR_BINARY || expr->binary_expr.operator != BINARYOP_ASSIGN) { - // 4. If found, set this to the current path. - (*path) = (DesignatedInitPath) { .decl = member, .sub_path = NULL }; - - // 5. And we're done! - return true; - } - // 6. We might encounter anonymous members. Treat this as a possible "match" - if (!member->name) - { - DesignatedInitPath anon_path; - bool found = sema_expr_analyse_designated_init_ident(context, &anon_path, member, name); - if (found) - { - // 7. If found, create a copy path. - DesignatedInitPath *path_copy = malloc_arena(sizeof(DesignatedInitPath)); - *path_copy = anon_path; - (*path) = (DesignatedInitPath) { .decl = member, .sub_path = path_copy }; - return true; - } - } - } - return false; - -} -static bool sema_expr_analyse_designated_init(Context *context, DesignatedInitPath *path, Decl *top, Expr *path_expr) -{ - // Our expression will look like this for a.b[3].c = ... - // access(subscript(access(identifier))), now we need to reverse that. - switch (path_expr->expr_kind) - { - case EXPR_IDENTIFIER: - - // Resolving for an identifier: - // 1. Can't be a path attached. - if (path_expr->identifier_expr.path) return false; - - // 2. Ensure it's a union or struct - if (!decl_is_struct_type(top)) return false; - - return sema_expr_analyse_designated_init_ident(context, path, top, path_expr->identifier_expr.identifier); - - case EXPR_ACCESS: - - // Resolve for access: - - // 1. Resolve the parent path: - if (!sema_expr_analyse_designated_init(context, path, top, path_expr->access_expr.parent)) return false; - - // 2. Our new top is now lowest init path. - while (path->sub_path) - { - path = path->sub_path; - } - top = path->decl; - - // 3. Do an analysis with the identifier: - path->sub_path = malloc_arena(sizeof(DesignatedInitPath)); - return sema_expr_analyse_designated_init_ident(context, - path->sub_path, - top->type->decl, - path_expr->access_expr.sub_element.string); - case EXPR_SUBSCRIPT: - - // Resolve for subscript: - - // 1. Resolve the parent path: - if (!sema_expr_analyse_designated_init(context, path, top, path_expr->subscript_expr.expr)) return false; - - // 2. Our new top is now lowest init path. - while (path->sub_path) - { - path = path->sub_path; - } - top = path->decl; - - TODO // Analyse index etc. - default: + SEMA_ERROR(expr, "Expected an initializer on the format 'foo = 123' here."); + return false; + } + Expr *path = expr->binary_expr.left; + if (!sema_analyse_init_path(context, assigned, path)) + { + SEMA_ERROR(path, "This is not a valid member of '%s'.", type_to_error_string(assigned->type)); return false; - } -} - -static bool sema_expr_analyse_struct_designated_initializer_list(Context *context, Decl *assigned, Expr *expr_list) -{ - assert(expr_list->expr_initializer.init_type == INITIALIZER_UNKNOWN); - VECEACH(expr_list->expr_initializer.initializer_expr, i) - { - Expr *expr = expr_list->expr_initializer.initializer_expr[i]; - if (expr->expr_kind != EXPR_BINARY && expr->binary_expr.operator != BINARYOP_ASSIGN) - { - if (i != 0) - { - SEMA_ERROR(expr, "Named and non-named initializers are not allowed together, please choose one or the other."); - return false; - } - // If there is an unexpected expression and no previous element then this is a normal initializer list. - expr_list->expr_initializer.init_type = INITIALIZER_NORMAL; - return true; } - Expr *path_expr = expr->binary_expr.left; Expr *value = expr->binary_expr.right; - DesignatedInitPath path; - if (!sema_expr_analyse_designated_init(context, &path, assigned, path_expr)) - { - if (i != 0) - { - SEMA_ERROR(path_expr, "Unexpected element when initializing '%s', did you get the name right?", assigned->name); - return false; - } - expr_list->expr_initializer.init_type = INITIALIZER_NORMAL; - return true; - } - // Walk down to the last decl. - DesignatedInitPath *init_path = &path; - while (init_path->sub_path) init_path = init_path->sub_path; - - if (!sema_analyse_expr_of_required_type(context, init_path->decl->type, value)) return false; - - // Destruct the expression and replace. - expr->designated_init_expr.value = value; // Do this first! - expr->resolve_status = RESOLVE_DONE; - expr->designated_init_expr.path = path; - expr->expr_kind = EXPR_DESIGNATED_INIT; - expr->type = init_path->decl->type; + if (!sema_analyse_expr_of_required_type(context, path->type, value)) return false; + expr->type = path->type; } - expr_list->expr_initializer.init_type = INITIALIZER_DESIGNATED; + initializer->expr_initializer.init_type = INITIALIZER_DESIGNATED; return true; } -static inline bool sema_expr_analyse_struct_initializer_list(Context *context, Type *assigned, Expr *expr) +static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, Decl *assigned, Expr *initializer) +{ + Expr **elements = initializer->expr_initializer.initializer_expr; + Decl **members = assigned->strukt.members; + initializer->expr_initializer.init_type = INITIALIZER_NORMAL; + unsigned size = vec_size(elements); + unsigned expected_members = vec_size(members); + + // For struct number of members must be the same as the size of the struct. + + assert(size > 0); + if (expected_members == 0) + { + SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty."); + return false; + } + + bool is_union = assigned->decl_kind == DECL_UNION; + expected_members = is_union ? 1 : expected_members; + VECEACH(elements, i) + { + if (i >= expected_members) + { + SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members); + return false; + } + if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i])) return false; + } + if (expected_members > size) + { + SEMA_ERROR(elements[size - 1], "Few elements in initializer, there should be elements after this one."); + return false; + } + return true; +} + +static inline bool sema_expr_analyse_struct_initializer(Context *context, Type *assigned, Expr *expr) { expr->type = assigned; - Decl **members = assigned->decl->strukt.members; - unsigned size = vec_size(members); + Expr **init_expressions = expr->expr_initializer.initializer_expr; - // Zero size init will initialize to empty. - if (size == 0) + // 1. Zero size init will initialize to empty. + if (vec_size(init_expressions) == 0) { expr->expr_initializer.init_type = INITIALIZER_ZERO; return true; } - if (!sema_expr_analyse_struct_designated_initializer_list(context, assigned->decl, expr)) return false; - - // If we already parsed this. - if (expr->expr_initializer.init_type == INITIALIZER_DESIGNATED) return true; - - Expr **elements = expr->expr_initializer.initializer_expr; - - assert(expr->expr_initializer.init_type == INITIALIZER_NORMAL); - - if (assigned->type_kind == TYPE_UNION) + // 2. Check if we might have a designated initializer + // this means that in this case we're actually not resolving macros here. + if (init_expressions[0]->expr_kind == EXPR_BINARY && init_expressions[0]->binary_expr.operator == BINARYOP_ASSIGN) { - SEMA_ERROR(elements[0], "Initializer list for unions must use named initializers, e.g. { a = 4 }"); - return false; + return sema_expr_analyse_struct_designated_initializer(context, assigned->decl, expr); } - VECEACH(elements, i) - { - if (i >= size) - { - SEMA_ERROR(elements[size], "Too many elements in initializer, expected only %d.", size); - return false; - } - if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i])) return false; - } - - if (size > vec_size(elements)) - { - SEMA_ERROR(elements[vec_size(elements) - 1], "Too few elements in initializer, expected %d.", size); - return false; - } - return true; + // 3. Otherwise use the plain initializer. + return sema_expr_analyse_struct_plain_initializer(context, assigned->decl, expr); } static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to, Expr *expr) @@ -723,7 +635,9 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to { case TYPE_STRUCT: case TYPE_UNION: - if (decl_is_struct_type(assigned->decl)) return sema_expr_analyse_struct_initializer_list(context, assigned, expr); + if (decl_is_struct_type(assigned->decl)) return sema_expr_analyse_struct_initializer(context, + assigned, + expr); break; case TYPE_ARRAY: TODO @@ -2127,6 +2041,9 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex EXPR_COPY(expr->subscript_expr.expr); EXPR_COPY(expr->subscript_expr.index); return expr; + case EXPR_GROUP: + EXPR_COPY(expr->group_expr->group_expr); + return expr; case EXPR_ACCESS: EXPR_COPY(expr->access_expr.parent); return expr; @@ -2562,6 +2479,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * return sema_expr_analyse_sizeof(context, to, expr); case EXPR_SUBSCRIPT: return sema_expr_analyse_subscript(context, to, expr); + case EXPR_GROUP: + return sema_expr_analyse_group(context, to, expr); case EXPR_ACCESS: return sema_expr_analyse_access(context, to, expr); case EXPR_INITIALIZER_LIST: diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index e87ba53db..015eac40c 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -20,7 +20,7 @@ void context_push_scope_with_flags(Context *context, ScopeFlags flags) context->current_scope->local_decl_start = context->last_local; context->current_scope->defers.start = parent_defer; context->current_scope->defers.end = parent_defer; - if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK)) + if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK | SCOPE_NEXT)) { context->current_scope->flags = flags; } From f53b378b0c19306ffd7a3d175ec590370c59ff39 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 9 Apr 2020 13:47:23 +0200 Subject: [PATCH 8/8] Adding trap on debug builds. --- missing.txt | 2 + resources/testfragments/super_simple.c3 | 4 + src/build/build_options.c | 1 + src/build/build_options.h | 1 + src/compiler/compiler.c | 1 + src/compiler/compiler_internal.h | 1 + src/compiler/llvm_codegen.c | 31 +++++++ src/compiler/llvm_codegen_expr.c | 106 +++++++++++++++++++++--- src/compiler/llvm_codegen_internal.h | 10 +++ src/compiler/llvm_codegen_stmt.c | 12 +++ 10 files changed, 158 insertions(+), 11 deletions(-) diff --git a/missing.txt b/missing.txt index e22a5a02c..597e4dcfa 100644 --- a/missing.txt +++ b/missing.txt @@ -45,6 +45,8 @@ Things missing: * Expressions - Disallow x >= 0 and x < 0 on unsigned types unless in a macro. +- Range check arrays on debug +- Allow negating int if assigned to a larger type. E.g short x = 1; int y = -x; * Switch - String switch diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 50cd0d882..a2037ca35 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -217,6 +217,7 @@ func int borok() throws func void testNoReturn() { int i = 0; + i = -i; } func int testReturn() @@ -513,7 +514,10 @@ func int testPointers(int x) } func int main(int x) { + printf("Helo!\n"); int efd = 9; + uint fefoek = 1; + long fefoek = -fefoek; int okfe = 1; return 1; switch (int bobe = okfe > 0 ? 1 : 0) diff --git a/src/build/build_options.c b/src/build/build_options.c index 6b9cf9296..d8ac764ea 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -316,6 +316,7 @@ void parse_arguments(int argc, const char *argv[]) build_options.optimization_level = OPTIMIZATION_NOT_SET; build_options.size_optimization_level = SIZE_OPTIMIZATION_NOT_SET; build_options.debug_info = false; + build_options.debug_mode = false; build_options.command = COMMAND_MISSING; build_options.symtab_size = DEFAULT_SYMTAB_SIZE; build_options.files = NULL; diff --git a/src/build/build_options.h b/src/build/build_options.h index 7c1f9645a..14a716b2b 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -103,6 +103,7 @@ typedef struct OptimizationLevel optimization_level; SizeOptimizationLevel size_optimization_level; bool debug_info; + bool debug_mode; bool emit_llvm; bool emit_bitcode; } BuildOptions; diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 4561ebcc0..02304d28c 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -95,6 +95,7 @@ void compiler_compile(BuildTarget *target) } if (diagnostics.errors > 0) exit(EXIT_FAILURE); + llvm_codegen_setup(); VECEACH(contexts, i) { Context *context = contexts[i]; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index bccdbd7d0..a1ff2938a 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1005,6 +1005,7 @@ bool cast_to_runtime(Expr *expr); void cast_to_smallest_runtime(Expr *expr); void llvm_codegen(Context *context); +void llvm_codegen_setup(); bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index aa22ae8d2..49cb4463b 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -155,8 +155,39 @@ static int get_inlining_threshold(void) return 250; } } + + +static inline unsigned lookup_intrinsic(const char *name) +{ + return LLVMLookupIntrinsicID(name, strlen(name)); +} + +static bool intrinsics_setup = false; +unsigned ssub_overflow_intrinsic_id; +unsigned usub_overflow_intrinsic_id; +unsigned sadd_overflow_intrinsic_id; +unsigned uadd_overflow_intrinsic_id; +unsigned smul_overflow_intrinsic_id; +unsigned umul_overflow_intrinsic_id; +unsigned trap_intrinsic_id; + +void llvm_codegen_setup() +{ + assert(intrinsics_setup == false); + ssub_overflow_intrinsic_id = lookup_intrinsic("llvm.ssub.with.overflow"); + usub_overflow_intrinsic_id = lookup_intrinsic("llvm.usub.with.overflow"); + sadd_overflow_intrinsic_id = lookup_intrinsic("llvm.sadd.with.overflow"); + uadd_overflow_intrinsic_id = lookup_intrinsic("llvm.uadd.with.overflow"); + smul_overflow_intrinsic_id = lookup_intrinsic("llvm.smul.with.overflow"); + umul_overflow_intrinsic_id = lookup_intrinsic("llvm.umul.with.overflow"); + trap_intrinsic_id = lookup_intrinsic("llvm.trap"); + + intrinsics_setup = true; +} + void llvm_codegen(Context *context) { + assert(intrinsics_setup); GenContext gen_context; gencontext_init(&gen_context, context); gencontext_begin_module(&gen_context); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 467242eca..6bad86e85 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -13,7 +13,26 @@ static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *ty return LLVMBuildAdd(context->builder, left, right, "add_mod"); } - // TODO insert trap + if (build_options.debug_mode) + { + LLVMTypeRef type_to_use = llvm_type(type->canonical); + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef args[2] = { left, right }; + assert(type->canonical == type); + LLVMValueRef add_res; + if (type_is_unsigned(type)) + { + add_res = gencontext_emit_call_intrinsic(context, uadd_overflow_intrinsic_id, types, args, 2); + } + else + { + add_res = gencontext_emit_call_intrinsic(context, sadd_overflow_intrinsic_id, types, args, 2); + } + LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Addition overflow"); + return result; + } return type_is_unsigned_integer(type) ? LLVMBuildNUWAdd(context->builder, left, right, "uadd") : LLVMBuildNSWAdd(context->builder, left, right, "add"); @@ -26,7 +45,28 @@ static inline LLVMValueRef gencontext_emit_sub_int(GenContext *context, Type *ty return LLVMBuildSub(context->builder, left, right, "sub_mod"); } - // TODO insert trap + if (build_options.debug_mode) + { + LLVMTypeRef type_to_use = llvm_type(type); + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef args[2] = { left, right }; + assert(type->canonical == type); + LLVMValueRef add_res; + if (type_is_unsigned(type)) + { + add_res = gencontext_emit_call_intrinsic(context, usub_overflow_intrinsic_id, types, args, 2); + } + else + { + add_res = gencontext_emit_call_intrinsic(context, ssub_overflow_intrinsic_id, types, args, 2); + } + LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Subtraction overflow"); + return result; + } + + return type_is_unsigned_integer(type) ? LLVMBuildNUWSub(context->builder, left, right, "usub") : LLVMBuildNSWSub(context->builder, left, right, "sub"); @@ -211,6 +251,8 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * LLVMValueRef rhs = gencontext_emit_expr(context, expr->cast_expr.expr); return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical); } + + static inline LLVMValueRef gencontext_emit_designated_initializer(GenContext *context, Type *parent_type, LLVMValueRef parent, Expr *expr) { assert(parent_type == parent_type->canonical); @@ -234,6 +276,12 @@ static inline LLVMValueRef gencontext_emit_designated_initializer(GenContext *co } } +/** + * Emit a Foo { .... } literal. + * + * Improve: Direct assign in the case where this is assigning to a variable. + * Improve: Create constant initializer for the constant case and do a memcopy + */ static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) { LLVMTypeRef type = llvm_type(expr->type); @@ -343,17 +391,27 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) case UNARYOP_NEGMOD: return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "negmod"); case UNARYOP_NEG: - // TODO improve how unsigned numbers are negated. if (type_is_float(type)) { return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg"); } - if (type_is_unsigned(type)) + assert(!type_is_unsigned(type)); { - return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg"); + LLVMValueRef to_negate = gencontext_emit_expr(context, expr->unary_expr.expr); + LLVMValueRef zero = LLVMConstInt(llvm_type(expr->unary_expr.expr->type->canonical), 0, false); + if (build_options.debug_mode) + { + LLVMTypeRef type_to_use = llvm_type(type->canonical); + LLVMValueRef args[2] = { zero, to_negate }; + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, ssub_overflow_intrinsic_id, types, args, 2); + LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Signed negation overflow"); + return result; + } + return LLVMBuildNSWSub(context->builder, zero, to_negate, "neg"); } - // TODO insert trap - return LLVMBuildNSWNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg"); case UNARYOP_ADDR: return gencontext_emit_address(context, expr->unary_expr.expr); case UNARYOP_DEREF: @@ -578,15 +636,33 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM UNREACHABLE case BINARYOP_MULT: if (is_float) return LLVMBuildFMul(context->builder, lhs_value, rhs_value, "fmul"); - // TODO insert trap if (type_is_unsigned_integer(lhs_type)) { + if (build_options.debug_mode) + { + LLVMTypeRef type_to_use = llvm_type(lhs_type); + LLVMValueRef args[2] = { lhs_value, rhs_value }; + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, umul_overflow_intrinsic_id, types, args, 2); + LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Unsigned multiplication overflow"); + return result; + } return LLVMBuildNUWMul(context->builder, lhs_value, rhs_value, "umul"); } - else + if (build_options.debug_mode) { - return LLVMBuildNSWMul(context->builder, lhs_value, rhs_value, "mul"); + LLVMTypeRef type_to_use = llvm_type(lhs_type); + LLVMValueRef args[2] = { lhs_value, rhs_value }; + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, smul_overflow_intrinsic_id, types, args, 2); + LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Signed multiplication overflow"); + return result; } + return LLVMBuildNSWMul(context->builder, lhs_value, rhs_value, "mul"); case BINARYOP_MULT_MOD: return LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul"); case BINARYOP_SUB: @@ -651,7 +727,6 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt"); case BINARYOP_AND: case BINARYOP_OR: - UNREACHABLE case BINARYOP_ASSIGN: case BINARYOP_MULT_ASSIGN: case BINARYOP_MULT_MOD_ASSIGN: @@ -878,6 +953,15 @@ static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr return return_out; } +LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types, + LLVMValueRef *values, unsigned arg_count) +{ + LLVMValueRef decl = LLVMGetIntrinsicDeclaration(context->module, intrinsic_id, types, arg_count); + LLVMTypeRef type = LLVMIntrinsicGetType(context->context, intrinsic_id, types, arg_count); + return LLVMBuildCall2(context->builder, type, decl, values, arg_count, ""); +} + + LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { switch (expr->expr_kind) diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 9e114946e..e460d61b6 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -68,10 +68,20 @@ typedef struct bool did_call_stack_save : 1; } GenContext; +extern unsigned sadd_overflow_intrinsic_id; +extern unsigned uadd_overflow_intrinsic_id; +extern unsigned ssub_overflow_intrinsic_id; +extern unsigned usub_overflow_intrinsic_id; +extern unsigned smul_overflow_intrinsic_id; +extern unsigned umul_overflow_intrinsic_id; +extern unsigned trap_intrinsic_id; void gencontext_begin_module(GenContext *context); void gencontext_end_module(GenContext *context); void gencontext_emit_stmt(GenContext *context, Ast *ast); +LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types, + LLVMValueRef *values, unsigned arg_count); +void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name); void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end); LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr); LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 20da33bab..9f41a100e 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -600,6 +600,18 @@ void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast) gencontext_emit_defer(context, ast->scoped_stmt.defers.start, ast->scoped_stmt.defers.end); } +void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name) +{ + LLVMBasicBlockRef panic_block = gencontext_create_free_block(context, "panic"); + LLVMBasicBlockRef ok_block = gencontext_create_free_block(context, "checkok"); + gencontext_emit_cond_br(context, value, panic_block, ok_block); + gencontext_emit_block(context, panic_block); + gencontext_emit_call_intrinsic(context, trap_intrinsic_id, NULL, NULL, 0); + gencontext_emit_br(context, ok_block); + gencontext_emit_block(context, ok_block); +} + + void gencontext_emit_stmt(GenContext *context, Ast *ast) { switch (ast->ast_kind)