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 }