diff --git a/src/compiler/ast.c b/src/compiler/ast.c index accb42ff8..9b0e73684 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -112,6 +112,8 @@ const char *decl_var_to_string(VarDeclKind kind) { case VARDECL_CONST: return "const"; + case VARDECL_CONST_CT: + return "$const"; case VARDECL_GLOBAL: return "global"; case VARDECL_LOCAL: @@ -738,6 +740,7 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) DUMPTI(decl->var.type_info); switch (decl->var.kind) { + case VARDECL_CONST_CT: case VARDECL_CONST: case VARDECL_GLOBAL: case VARDECL_LOCAL: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index c2eacc94e..a244e160a 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -272,7 +272,7 @@ typedef struct typedef struct _VarDecl { - VarDeclKind kind : 3; + VarDeclKind kind : 4; bool constant : 1; bool failable : 1; bool unwrap : 1; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index b30ab3426..4a7368af1 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -508,7 +508,8 @@ typedef enum VARDECL_MEMBER = 4, VARDECL_LOCAL_CT = 5, VARDECL_LOCAL_CT_TYPE = 6, - VARDECL_ALIAS = 7, + VARDECL_CONST_CT = 7, + VARDECL_ALIAS = 8, } VarDeclKind; typedef enum diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 34f29e7fe..053d6e470 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -60,7 +60,9 @@ LLVMValueRef gencontext_emit_memclear(GenContext *context, LLVMValueRef ref, Typ static void gencontext_emit_global_variable_definition(GenContext *context, Decl *decl) { - assert(decl->var.kind == VARDECL_GLOBAL); + if (decl->var.kind == VARDECL_CONST_CT) return; + + assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST); // TODO fix name decl->ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->name); @@ -73,7 +75,8 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl { LLVMSetInitializer(decl->ref, LLVMConstNull(llvm_type(decl->type))); } - // If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1); + + LLVMSetGlobalConstant(decl->ref, decl->var.kind == VARDECL_CONST); switch (decl->visibility) { diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 84e29ad46..6acab106f 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -909,6 +909,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_CT_IDENT] = { parse_identifier, NULL, PREC_NONE }, [TOKEN_AT] = { parse_macro_ident, NULL, PREC_NONE }, [TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE }, + [TOKEN_CT_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE }, [TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE }, [TOKEN_REAL] = { parse_double, NULL, PREC_NONE }, [TOKEN_OR] = { NULL, parse_binary, PREC_LOGICAL }, diff --git a/src/compiler/parser.c b/src/compiler/parser.c index fe37e902a..f5fead99f 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -576,7 +576,7 @@ Decl *parse_decl(Context *context) /** * const_decl - * : 'const' CT_IDENT '=' const_expr ';' + * : 'const' CT_CONST_IDENT '=' const_expr ';' * | 'const' type IDENT '=' const_expr ';' * ; */ @@ -585,9 +585,12 @@ static inline Decl *parse_const_declaration(Context *context, Visibility visibil advance_and_verify(context, TOKEN_CONST); Decl *decl = DECL_NEW_VAR(NULL, VARDECL_CONST, visibility); + decl->span.loc = context->prev_tok; + // Parse the compile time constant. - if (TOKEN_IS(TOKEN_CT_IDENT)) + if (try_consume(context, TOKEN_CT_CONST_IDENT)) { + decl->var.kind = VARDECL_CONST_CT; if (!is_all_upper(decl->name)) { SEMA_TOKEN_ERROR(context->tok, "Compile time constants must be all upper characters."); @@ -596,10 +599,9 @@ static inline Decl *parse_const_declaration(Context *context, Visibility visibil } else { - if (token_is_any_type(context->tok.type)) - { - decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_decl); - } + decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_decl); + decl->name = TOKKSTR(context->tok); + decl->name_token = context->tok.id; if (!consume_const_name(context, "constant")) return poisoned_decl; } @@ -1681,6 +1683,17 @@ static inline Decl *parse_top_level(Context *context) TODO //sema_error_at(context->token->span.loc - 1, "Expected a top level declaration'."); return poisoned_decl; + case TOKEN_CT_CONST_IDENT: + if (context->next_tok.type == TOKEN_EQ) + { + SEMA_TOKEN_ERROR(context->tok, + "Did you forget a 'const' before the name of this compile time constant?"); + } + else + { + SEMA_TOKEN_ERROR(context->tok, "Compile time constant unexpectedly found."); + } + return poisoned_decl; default: // We could have included all fundamental types above, but do it here instead. if (token_is_type(context->tok.type)) diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 898247fa1..71fbed8a0 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -476,34 +476,6 @@ bool ixxbo(Context *context, Expr *left, Type *type) } -bool ussi(Context *context, Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) -{ - if (type->type_kind != TYPE_ENUM) return sema_type_mismatch(context, left, type, CAST_TYPE_EXPLICIT); - if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH(); - - if (left->expr_kind == EXPR_IDENTIFIER && left->identifier_expr.decl->decl_kind == DECL_ENUM_CONSTANT) - { - // TODO - Expr *value = left->identifier_expr.decl->enum_constant.expr; - assert(value->expr_kind == EXPR_CONST); -// assert(value->const_expr.type == CONST_INT); - left->const_expr.i = value->const_expr.i; - // TODO narrowing - } - insert_cast(left, CAST_ENUMLOW, canonical); - return true; -} - -bool sius(Context *context, Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) -{ - TODO -} - -bool uius(Context *context, Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) -{ - TODO -} - /** * Cast comptime, signed or unsigned -> pointer. * @return true unless the constant value evaluates to zero. @@ -592,6 +564,7 @@ bool enxi(Context *context, Expr* left, Type *from, Type *canonical, Type *type, return false; } // 3. Dispatch to the right cast: + insert_cast(left, CAST_ENUMLOW, enum_type); return xixi(context, left, enum_type_canonical, canonical, type, cast_type); } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 3b4651519..07ba5c329 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -591,6 +591,8 @@ static inline bool expr_is_constant_eval(Expr *expr) static inline bool sema_analyse_global(Context *context, Decl *decl) { + if (decl->var.kind == VARDECL_CONST_CT) return true; + if (!sema_resolve_type_info(context, decl->var.type_info)) return false; decl->type = decl->var.type_info->type; if (decl->var.init_expr) @@ -649,7 +651,6 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) default: eprintf("Decl %s %d\n", decl->name, decl->var.kind); UNREACHABLE - break; } } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 32a587730..96ae835ee 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -332,17 +332,25 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr { expr->failable = true; } - if (decl->decl_kind == DECL_VAR && decl->var.constant) + if (decl->decl_kind == DECL_VAR) { - assert(decl->var.init_expr && decl->var.init_expr->resolve_status == RESOLVE_DONE); - if (decl->var.failable) + switch (decl->var.kind) { - SEMA_ERROR(expr, "Constants may never be 'failable', please remove the '!'."); - return false; + case VARDECL_CONST: + assert(decl->var.init_expr && decl->var.init_expr->resolve_status == RESOLVE_DONE); + if (decl->var.failable) + { + SEMA_ERROR(expr, "Constants may never be 'failable', please remove the '!'."); + return false; + } + expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr)); + return true; + case VARDECL_CONST_CT: + expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr)); + return sema_analyse_expr(context, to, expr); + default: + break; } - // Todo, maybe make a copy? - expr_replace(expr, decl->var.init_expr); - return true; } assert(decl->type); expr->identifier_expr.decl = decl; @@ -2643,12 +2651,11 @@ static bool sema_expr_analyse_neg(Context *context, Type *to, Expr *expr, Expr * static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Expr *inner) { Type *canonical = inner->type->canonical; - if (!type_is_any_integer(canonical) && canonical != type_bool) + if (!type_is_integer(canonical) && canonical != type_bool) { SEMA_ERROR(expr, "Cannot bit negate '%s'.", type_to_error_string(inner->type)); return false; } - // The simple case, non-const. if (inner->expr_kind != EXPR_CONST) { @@ -2660,11 +2667,10 @@ static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Ex switch (expr->const_expr.kind) { case ALL_SIGNED_INTS: - case ALL_UNSIGNED_INTS: - bigint_negate_wrap(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize); + bigint_not(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize, true); break; - case TYPE_IXX: - bigint_negate(&expr->const_expr.i, &inner->const_expr.i); + case ALL_UNSIGNED_INTS: + bigint_not(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize, false); break; case TYPE_BOOL: expr->const_expr.b = !expr->const_expr.b; diff --git a/test/test_suite/constants/constant_paren.c3t b/test/test_suite/constants/constant_paren.c3t deleted file mode 100644 index 046a7d2c4..000000000 --- a/test/test_suite/constants/constant_paren.c3t +++ /dev/null @@ -1,7 +0,0 @@ -// #skip - -const uint AA = (~0); - -// #expect: constant_paren.ll - -#define test_AA (~0) diff --git a/test/test_suite/constants/constants.c3t b/test/test_suite/constants/constants.c3t new file mode 100644 index 000000000..5535d2245 --- /dev/null +++ b/test/test_suite/constants/constants.c3t @@ -0,0 +1,34 @@ +const byte AA = ~0; +const byte BB = 200 ; +const uint CC = ~0; +const uint DD = $FOO; + +const $FOO = ~0; + +uint x = AA; +uint z = CC; +byte w = $FOO; +ushort v = $FOO; +uint z2 = DD; + +func void test() +{ + int xx = $FOO; +} + +// #expect: constants.ll + +@AA = protected constant i8 -1 +@BB = protected constant i8 -56 +@CC = protected constant i32 -1 +@DD = protected constant i32 -1 +@x = protected global i32 255 +@z = protected global i32 -1 +@w = protected global i8 -1 +@v = protected global i16 -1 +@z2 = protected global i32 -1 + +entry: + %xx = alloca i32 + store i32 -1, i32* %xx + ret void \ No newline at end of file diff --git a/test/test_suite/expressions/casts/cast_enum_to_various.c3 b/test/test_suite/expressions/casts/cast_enum_to_various.c3 index 3fd6be19d..1bf1171d7 100644 --- a/test/test_suite/expressions/casts/cast_enum_to_various.c3 +++ b/test/test_suite/expressions/casts/cast_enum_to_various.c3 @@ -1,4 +1,6 @@ -// #skip + + + struct Struct { int x; diff --git a/test/test_suite/globals/misplaced_const.c3 b/test/test_suite/globals/misplaced_const.c3 new file mode 100644 index 000000000..3701d68ce --- /dev/null +++ b/test/test_suite/globals/misplaced_const.c3 @@ -0,0 +1,6 @@ +const $FOO = 3; +$BAR = 4; // #error: Did you forget a 'const' before the name of this compile time constant? + +const $BAZ = "ofke"; + +$FOOBAR; // #error: Compile time constant unexpectedly found \ No newline at end of file