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: