From 1ea56251838bcfe977a5a56b75a6bb7e4c0106c6 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 7 Dec 2022 16:25:08 +0100 Subject: [PATCH] CT variables now follow CT scopes. It's now allowed to mutate CT variables in deeper runtime scopes. --- lib/std/core/mem.c3 | 9 +- lib/std/core/string_iterator.c3 | 2 - lib/std/math_i128.c3 | 36 +++-- src/compiler/ast.c | 7 + src/compiler/compiler_internal.h | 8 +- src/compiler/enums.h | 10 +- src/compiler/sema_decls.c | 2 - src/compiler/sema_expr.c | 15 -- src/compiler/sema_internal.h | 2 + src/compiler/sema_name_resolution.c | 33 +++- src/compiler/sema_stmts.c | 171 ++++++++++++-------- src/compiler/semantic_analyser.c | 16 +- src/version.h | 2 +- test/test_suite/compile_time/ct_checks.c3t | 4 +- test/test_suite/macros/hash_ident.c3 | 3 +- test/test_suite/stdlib/map.c3t | 2 +- test/test_suite2/compile_time/ct_checks.c3t | 4 +- test/test_suite2/macros/hash_ident.c3 | 3 +- test/test_suite2/stdlib/map.c3t | 2 +- 19 files changed, 212 insertions(+), 119 deletions(-) diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 8f3adc7df..398db9f68 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -81,16 +81,17 @@ macro bool equals(a, b, isz len = -1, usz $align = 0) $endif; if (!len) return true; + var $Type; $switch ($align): $case 1: - var $Type = char; + $Type = char; $case 2: - var $Type = ushort; + $Type = ushort; $case 4: - var $Type = uint; + $Type = uint; $case 8: $default: - var $Type = ulong; + $Type = ulong; $endswitch; var $step = $Type.sizeof; usz end = len / $step; diff --git a/lib/std/core/string_iterator.c3 b/lib/std/core/string_iterator.c3 index 201fecff0..51a9e662c 100644 --- a/lib/std/core/string_iterator.c3 +++ b/lib/std/core/string_iterator.c3 @@ -1,7 +1,5 @@ module std::core::string::iterator; - - struct StringIterator { char[] utf8; diff --git a/lib/std/math_i128.c3 b/lib/std/math_i128.c3 index 5aa26b06c..cdddf7fae 100644 --- a/lib/std/math_i128.c3 +++ b/lib/std/math_i128.c3 @@ -82,26 +82,27 @@ fn int128 __fixsfti(float a) @weak @extname("__fixsfti") = fixint(a); private macro float_from_i128($Type, a) { + var $Rep; $switch ($Type): $case double: - var $Rep = ulong; + $Rep = ulong; const MANT_DIG = DOUBLE_MANT_DIG; const SIGNIFICANT_BITS = 52; const EXP_BIAS = 1023; const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64; const SIGN_BIT = 1u64 << 63; $case float: - var $Rep = uint; + $Rep = uint; const MANT_DIG = FLOAT_MANT_DIG; const EXP_BIAS = 127; const SIGNIFICANT_BITS = 23; const MANTISSA_MASK = 0x7F_FFFFu32; const SIGN_BIT = 1u32 << 31; $case float16: - var $Rep = ushort; + $Rep = ushort; const MANT_DIG = HALF_MANT_DIG; $case float128: - var $Rep = uint128; + $Rep = uint128; const MANT_DIG = QUAD_MANT_DIG; $endswitch; if (a == 0) return ($Type)0; @@ -140,24 +141,25 @@ private macro float_from_i128($Type, a) private macro float_from_u128($Type, a) { + var $Rep; $switch ($Type): $case double: - var $Rep = ulong; + $Rep = ulong; const MANT_DIG = DOUBLE_MANT_DIG; const SIGNIFICANT_BITS = 52; const EXP_BIAS = 1023; const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64; $case float: - var $Rep = uint; + $Rep = uint; const MANT_DIG = FLOAT_MANT_DIG; const EXP_BIAS = 127; const SIGNIFICANT_BITS = 23; const MANTISSA_MASK = 0x7F_FFFFu32; $case float16: - var $Rep = ushort; + $Rep = ushort; const MANT_DIG = HALF_MANT_DIG; $case float128: - var $Rep = uint128; + $Rep = uint128; const MANT_DIG = QUAD_MANT_DIG; $endswitch; if (a == 0) return ($Type)0; @@ -194,21 +196,22 @@ private macro float_from_u128($Type, a) private macro fixuint(a) { + var $Rep; $switch ($typeof(a)): $case double: - var $Rep = ulong; + $Rep = ulong; const EXPONENT_BITS = 11; const SIGNIFICANT_BITS = 52; $case float: - var $Rep = uint; + $Rep = uint; const EXPONENT_BITS = 8; const SIGNIFICANT_BITS = 23; $case float16: - var $Rep = ushort; + $Rep = ushort; const EXPONENT_BITS = 5; const SIGNIFICANT_BITS = 10; $case float128: - var $Rep = uint128; + $Rep = uint128; const EXPONENT_BITS = 15; const SIGNIFICANT_BITS = 112; $endswitch; @@ -237,21 +240,22 @@ private macro fixuint(a) private macro fixint(a) { + var $Rep; $switch ($typeof(a)): $case double: - var $Rep = ulong; + $Rep = ulong; const EXPONENT_BITS = 11; const SIGNIFICANT_BITS = 52; $case float: - var $Rep = uint; + $Rep = uint; const EXPONENT_BITS = 8; const SIGNIFICANT_BITS = 23; $case float16: - var $Rep = ushort; + $Rep = ushort; const EXPONENT_BITS = 5; const SIGNIFICANT_BITS = 10; $case float128: - var $Rep = uint128; + $Rep = uint128; const EXPONENT_BITS = 15; const SIGNIFICANT_BITS = 112; $endswitch; diff --git a/src/compiler/ast.c b/src/compiler/ast.c index e26843ba7..6cf38d780 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -35,6 +35,13 @@ Decl *decl_new(DeclKind decl_kind, const char *name, SourceSpan span, Visibility return decl; } +bool decl_is_ct_var(Decl *decl) +{ + if (decl->decl_kind != DECL_VAR) return false; + return decl_var_kind_is_ct(decl->var.kind); + UNREACHABLE; +} + Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, Visibility visibility) { Decl *decl = decl_calloc(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 05a470a38..91540683b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -441,7 +441,6 @@ typedef struct VarDecl_ struct SemaContext_ *context; SourceSpan span; } hash_var; - unsigned scope_depth; // CT var struct { void *backend_debug_ref; @@ -1627,6 +1626,7 @@ typedef struct SemaContext_ Ast **returns_cache; Expr **macro_varargs; Decl **macro_params; + Decl** ct_locals; }; Type *rtype; struct SemaContext_ *yield_context; @@ -2082,6 +2082,7 @@ INLINE const char *decl_get_extname(Decl *decl); static inline Decl *decl_raw(Decl *decl); static inline DeclKind decl_from_token(TokenType type); static inline bool decl_is_local(Decl *decl); +bool decl_is_ct_var(Decl *decl); Decl *decl_find_enum_constant(Decl *decl, const char *name); AlignSize decl_find_member_offset(Decl *decl, Decl *member); @@ -3162,6 +3163,11 @@ INLINE bool expr_is_const(Expr *expr) return expr->expr_kind == EXPR_CONST; } +INLINE bool decl_var_kind_is_ct(VarDeclKind kind) +{ + return kind >= VARDECL_FIRST_CT && kind <= VARDECL_LAST_CT; +} + static inline bool decl_is_local(Decl *decl) { if (decl->decl_kind != DECL_VAR) return false; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 6c5212ef8..2261198fb 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -702,15 +702,17 @@ typedef enum VARDECL_PARAM, VARDECL_MEMBER, VARDECL_BITMEMBER, - VARDECL_PARAM_CT, - VARDECL_PARAM_CT_TYPE, VARDECL_PARAM_REF, VARDECL_PARAM_EXPR, - VARDECL_LOCAL_CT, - VARDECL_LOCAL_CT_TYPE, VARDECL_UNWRAPPED, VARDECL_ERASE, VARDECL_REWRAPPED, + VARDECL_PARAM_CT, + VARDECL_PARAM_CT_TYPE, + VARDECL_LOCAL_CT, + VARDECL_LOCAL_CT_TYPE, + VARDECL_FIRST_CT = VARDECL_PARAM_CT, + VARDECL_LAST_CT = VARDECL_LOCAL_CT_TYPE, } VarDeclKind; typedef enum diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index d17191b86..c30d90d4b 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2365,10 +2365,8 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) UNREACHABLE } - decl->var.scope_depth = context->active_scope.depth; return sema_add_local(context, decl); FAIL: - decl->var.scope_depth = context->active_scope.depth; sema_add_local(context, decl); return decl_poison(decl); } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index ae0349e2f..8b4ce348e 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1641,10 +1641,6 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s } param->var.init_expr = args[i]; VarDeclKind kind = param->var.kind; - if (kind == VARDECL_PARAM_CT_TYPE || kind == VARDECL_PARAM_CT) - { - param->var.scope_depth = context->active_scope.depth + 1; - } } Decl **body_params = call_expr->call_expr.body_arguments; @@ -3928,11 +3924,6 @@ static inline bool sema_binary_analyse_ct_identifier_lvalue(SemaContext *context return expr_poison(expr); } - if (decl->var.scope_depth < context->active_scope.depth) - { - SEMA_ERROR(expr, "Cannot modify '%s' inside of a runtime scope.", decl->name); - return false; - } expr->ct_ident_expr.decl = decl; expr->resolve_status = RESOLVE_DONE; return true; @@ -5367,12 +5358,6 @@ static inline bool sema_expr_analyse_ct_incdec(SemaContext *context, Expr *expr, return false; } - if (var->var.scope_depth < context->active_scope.depth) - { - SEMA_ERROR(expr, "Cannot modify '%s' inside of a runtime scope.", var->name); - return false; - } - Expr *end_value = expr_copy(start_value); // Make the change. diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 83db2c76b..370051dcc 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -54,6 +54,8 @@ TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref, void sema_context_init(SemaContext *context, CompilationUnit *unit); void sema_context_destroy(SemaContext *context); +unsigned sema_context_push_ct_stack(SemaContext *context); +void sema_context_pop_ct_stack(SemaContext *context, unsigned old_state); bool sema_analyse_function_body(SemaContext *context, Decl *func); bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts); diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 842ec148b..d44f618c4 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -385,8 +385,18 @@ static Decl *sema_resolve_path_symbol(SemaContext *context, NameResolve *name_re return decl ? decl : sema_find_decl_in_global_new(unit, &global_context.symbols, global_context.module_list, name_resolve, false); } +static inline Decl *sema_find_ct_local(SemaContext *context, const char *symbol) +{ + Decl **locals = context->ct_locals; + FOREACH_BEGIN(Decl *cur, locals) + if (cur->name == symbol) return cur; + FOREACH_END(); + return NULL; +} + static inline Decl *sema_find_local(SemaContext *context, const char *symbol) { + if (symbol[0] == '$') return sema_find_ct_local(context, symbol); Decl **locals = context->locals; if (!locals || !context->active_scope.current_local) return NULL; int64_t first = 0; @@ -793,6 +803,7 @@ Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path, static inline void sema_append_local(SemaContext *context, Decl *decl) { + assert(!decl_is_ct_var(decl)); Decl ***locals = &context->locals; size_t locals_size = vec_size(*locals); size_t current_local = context->active_scope.current_local; @@ -811,13 +822,32 @@ static inline void sema_append_local(SemaContext *context, Decl *decl) context->active_scope.current_local++; } +INLINE bool sema_add_ct_local(SemaContext *context, Decl *decl) +{ + assert(decl_is_ct_var(decl)); + + Decl *other = sema_find_ct_local(context, decl->name); + if (other) + { + sema_shadow_error(decl, other); + decl_poison(decl); + decl_poison(other); + return false; + } + decl->resolve_status = RESOLVE_DONE; + vec_add(context->ct_locals, decl); + return true; +} + bool sema_add_local(SemaContext *context, Decl *decl) { CompilationUnit *current_unit = decl->unit = context->unit; // Ignore synthetic locals. if (!decl->name) return true; - if (decl->decl_kind == DECL_VAR && decl->var.shadow) goto ADD_VAR; + bool is_var = decl->decl_kind == DECL_VAR; + if (is_var && decl_var_kind_is_ct(decl->var.kind)) return sema_add_ct_local(context, decl); + if (is_var && decl->var.shadow) goto ADD_VAR; Decl *other = sema_find_local(context, decl->name); assert(!other || other->unit->module); @@ -834,6 +864,7 @@ ADD_VAR: return true; } + void sema_unwrap_var(SemaContext *context, Decl *decl) { Decl *alias = decl_copy(decl); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 987b4c8d8..1e56c71a6 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1829,9 +1829,14 @@ static inline bool sema_analyse_then_overwrite(SemaContext *context, Ast *statem static inline bool sema_analyse_ct_if_stmt(SemaContext *context, Ast *statement) { + unsigned ct_context = sema_context_push_ct_stack(context); int res = sema_check_comp_time_bool(context, statement->ct_if_stmt.expr); - if (res == -1) return false; - if (res) return sema_analyse_then_overwrite(context, statement, statement->ct_if_stmt.then); + if (res == -1) goto FAILED; + if (res) + { + if (sema_analyse_then_overwrite(context, statement, statement->ct_if_stmt.then)) goto SUCCESS; + goto FAILED; + } Ast *elif = astptrzero(statement->ct_if_stmt.elif); while (1) { @@ -1839,20 +1844,31 @@ static inline bool sema_analyse_ct_if_stmt(SemaContext *context, Ast *statement) { // Turn into NOP! statement->ast_kind = AST_NOP_STMT; - return true; + goto SUCCESS; } // We found else, then just replace with that. if (elif->ast_kind == AST_CT_ELSE_STMT) { - return sema_analyse_then_overwrite(context, statement, elif->ct_else_stmt); + if (sema_analyse_then_overwrite(context, statement, elif->ct_else_stmt)) goto SUCCESS; + goto FAILED; } assert(elif->ast_kind == AST_CT_IF_STMT); res = sema_check_comp_time_bool(context, elif->ct_if_stmt.expr); - if (res == -1) return false; - if (res) return sema_analyse_then_overwrite(context, statement, elif->ct_if_stmt.then); + if (res == -1) goto FAILED; + if (res) + { + if (sema_analyse_then_overwrite(context, statement, elif->ct_if_stmt.then)) goto SUCCESS; + goto FAILED; + } elif = astptrzero(elif->ct_if_stmt.elif); } +SUCCESS: + sema_context_pop_ct_stack(context, ct_context); + return true; +FAILED: + sema_context_pop_ct_stack(context, ct_context); + return false; } static inline bool sema_analyse_compound_statement_no_scope(SemaContext *context, Ast *compound_statement) @@ -2083,9 +2099,10 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statement) { + unsigned ct_context = sema_context_push_ct_stack(context); // Evaluate the switch statement Expr *cond = exprptr(statement->ct_switch_stmt.cond); - if (!sema_analyse_ct_expr(context, cond)) return false; + if (!sema_analyse_ct_expr(context, cond)) goto FAILED; // If we have a type, then we do different evaluation // compared to when it is a value. @@ -2105,7 +2122,7 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem FALLTHROUGH; default: SEMA_ERROR(cond, "Only types, strings, integers, floats and booleans may be used with '$switch'."); - return false; + goto FAILED; } ExprConst *switch_expr_const = &cond->const_expr; @@ -2132,39 +2149,39 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem if (!type_is_integer(type) && !type_is_float(type)) { SEMA_ERROR(to_expr, "$case ranges are only allowed for floats and integers."); - return false; + goto FAILED; } } if (is_type) { - if (!sema_analyse_ct_expr(context, expr)) return false; + if (!sema_analyse_ct_expr(context, expr)) goto FAILED; if (expr->type != type_typeid) { SEMA_ERROR(expr, "A type was expected here not %s.", type_quoted_error_string(expr->type)); - return false; + goto FAILED; } } else { - if (!sema_analyse_expr_rhs(context, type, expr, false)) return false; - if (to_expr && !sema_analyse_expr_rhs(context, type, to_expr, false)) return false; + if (!sema_analyse_expr_rhs(context, type, expr, false)) goto FAILED; + if (to_expr && !sema_analyse_expr_rhs(context, type, to_expr, false)) goto FAILED; } if (!expr_is_const(expr)) { SEMA_ERROR(expr, "The $case must have a constant expression."); - return false; + goto FAILED; } if (to_expr && !expr_is_const(to_expr)) { SEMA_ERROR(to_expr, "The $case must have a constant expression."); - return false; + goto FAILED; } ExprConst *const_expr = &expr->const_expr; ExprConst *const_to_expr = to_expr ? &to_expr->const_expr : const_expr; if (to_expr && expr_const_compare(const_expr, const_to_expr, BINARYOP_GT)) { SEMA_ERROR(to_expr, "The end of a range must be less or equal to the beginning."); - return false; + goto FAILED; } // Check that it is unique. for (unsigned j = 0; j < i; j++) @@ -2177,7 +2194,7 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem { SEMA_ERROR(stmt, "'%s' appears more than once.", expr_const_to_error_string(const_expr)); SEMA_NOTE(cases[j]->case_stmt.expr, "The previous $case was here."); - return false; + goto FAILED; } } if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr)) @@ -2191,7 +2208,7 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem { SEMA_ERROR(stmt, "More than one $default is not allowed."); SEMA_NOTE(cases[default_case], "The previous $default was here."); - return false; + goto FAILED; } default_case = (int)i; continue; @@ -2211,20 +2228,27 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem if (!body) { statement->ast_kind = AST_NOP_STMT; - return true; + goto SUCCESS; } - return sema_analyse_then_overwrite(context, statement, body->compound_stmt.first_stmt); + if (!sema_analyse_then_overwrite(context, statement, body->compound_stmt.first_stmt)) goto FAILED; +SUCCESS: + sema_context_pop_ct_stack(context, ct_context); + return true; +FAILED: + sema_context_pop_ct_stack(context, ct_context); + return false; } static inline bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *statement) { + unsigned ct_context = sema_context_push_ct_stack(context); Expr *collection = exprptr(statement->ct_foreach_stmt.expr); if (!sema_analyse_ct_expr(context, collection)) return false; if (!expr_is_const_untyped_list(collection) && !expr_is_const_initializer(collection)) { SEMA_ERROR(collection, "Expected a list to iterate over"); - return false; + goto FAILED; } unsigned count; ConstInitializer *initializer = NULL; @@ -2242,11 +2266,15 @@ static inline bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *state else { // Empty list - if (init_type == CONST_INIT_ZERO) return true; + if (init_type == CONST_INIT_ZERO) + { + sema_context_pop_ct_stack(context, ct_context); + return true; + } if (init_type != CONST_INIT_ARRAY_FULL) { SEMA_ERROR(collection, "Only regular arrays are allowed here."); - return false; + goto FAILED; } count = vec_size(initializer->init_array_full); } @@ -2260,48 +2288,52 @@ static inline bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *state const char *index_name = statement->ct_foreach_stmt.index_name; AstId start = 0; - SCOPE_START; - if (index_name) + if (index_name) + { + index = decl_new_var(index_name, statement->ct_foreach_stmt.index_span, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); + index->type = type_int; + if (!sema_add_local(context, index)) goto FAILED; + } + Decl *value = decl_new_var(statement->ct_foreach_stmt.value_name, statement->ct_foreach_stmt.value_span, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); + if (!sema_add_local(context, value)) goto FAILED; + // Get the body + Ast *body = astptr(statement->ct_foreach_stmt.body); + AstId *current = &start; + unsigned loop_context = sema_context_push_ct_stack(context); + for (unsigned i = 0; i < count; i++) + { + sema_context_pop_ct_stack(context, loop_context); + Ast *compound_stmt = copy_ast_single(body); + if (expressions) { - index = decl_new_var(index_name, statement->ct_foreach_stmt.index_span, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); + value->var.init_expr = expressions[i]; + } + else + { + Expr *expr = expr_new(EXPR_CONST, collection->span); + if (!expr_rewrite_to_const_initializer_index(const_list_type, initializer, expr, i)) + { + SEMA_ERROR(collection, "Complex expressions are not allowed."); + goto FAILED; + } + value->var.init_expr = expr; + } + if (index) + { + index->var.init_expr = expr_new_const_int(index->span, type_int, i, true); index->type = type_int; - if (!sema_add_local(context, index)) return SCOPE_POP_ERROR(); } - Decl *value = decl_new_var(statement->ct_foreach_stmt.value_name, statement->ct_foreach_stmt.value_span, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); - if (!sema_add_local(context, value)) return SCOPE_POP_ERROR(); - // Get the body - Ast *body = astptr(statement->ct_foreach_stmt.body); - AstId *current = &start; - for (unsigned i = 0; i < count; i++) - { - Ast *compound_stmt = copy_ast_single(body); - if (expressions) - { - value->var.init_expr = expressions[i]; - } - else - { - Expr *expr = expr_new(EXPR_CONST, collection->span); - if (!expr_rewrite_to_const_initializer_index(const_list_type, initializer, expr, i)) - { - SEMA_ERROR(collection, "Complex expressions are not allowed."); - return false; - } - value->var.init_expr = expr; - } - if (index) - { - index->var.init_expr = expr_new_const_int(index->span, type_int, i, true); - index->type = type_int; - } - if (!sema_analyse_compound_stmt(context, compound_stmt)) return SCOPE_POP_ERROR(); - *current = astid(compound_stmt); - current = &compound_stmt->next; - } - SCOPE_END; + if (!sema_analyse_compound_stmt(context, compound_stmt)) goto FAILED; + *current = astid(compound_stmt); + current = &compound_stmt->next; + } + sema_context_pop_ct_stack(context, ct_context); statement->ast_kind = AST_COMPOUND_STMT; statement->compound_stmt.first_stmt = start; return true; +FAILED: + sema_context_pop_ct_stack(context, ct_context); + return false; } static inline bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement) @@ -2411,6 +2443,7 @@ bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement) */ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement) { + unsigned for_context = sema_context_push_ct_stack(context); bool success = false; ExprId init; if ((init = statement->for_stmt.init)) @@ -2427,13 +2460,13 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement if (decl->decl_kind != DECL_VAR || (decl->var.kind != VARDECL_LOCAL_CT && decl->var.kind != VARDECL_LOCAL_CT_TYPE)) { SEMA_ERROR(expr, "Only 'var $foo' and 'var $Type' declarations are allowed in '$for'"); - return false; + goto FAILED; } - if (!sema_analyse_var_decl_ct(context, decl)) return false; + if (!sema_analyse_var_decl_ct(context, decl)) goto FAILED; continue; } // If expression evaluate it and make sure it is constant. - if (!sema_analyse_ct_expr(context, expr)) return false; + if (!sema_analyse_ct_expr(context, expr)) goto FAILED; FOREACH_END(); } ExprId condition = statement->for_stmt.cond; @@ -2445,16 +2478,18 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement assert(condition); // We set a maximum of macro iterations. // we might consider reducing this. + unsigned current_ct_scope = sema_context_push_ct_stack(context); for (int i = 0; i < MAX_MACRO_ITERATIONS; i++) { + sema_context_pop_ct_stack(context, current_ct_scope); // First evaluate the cond, which we note that we *must* have. // we need to make a copy Expr *copy = copy_expr_single(exprptr(condition)); - if (!sema_analyse_cond_expr(context, copy)) return false; + if (!sema_analyse_cond_expr(context, copy)) goto FAILED; if (!expr_is_const(copy)) { SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time."); - return false; + goto FAILED; } // This is simple, since we know we have a boolean, just break if we reached "false" if (!copy->const_expr.b) break; @@ -2463,7 +2498,7 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement Ast *compound_stmt = copy_ast_single(body); // Analyse the body - if (!sema_analyse_compound_statement_no_scope(context, compound_stmt)) return false; + if (!sema_analyse_compound_statement_no_scope(context, compound_stmt)) goto FAILED; // Append it. *current = astid(compound_stmt); @@ -2471,13 +2506,16 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement // Copy and evaluate all the expressions in "incr" FOREACH_BEGIN(Expr *expr, incr_list) - if (!sema_analyse_ct_expr(context, copy_expr_single(expr))) return false; + if (!sema_analyse_ct_expr(context, copy_expr_single(expr))) goto FAILED; FOREACH_END(); } // Analysis is done turn the generated statements into a compound statement for lowering. statement->ast_kind = AST_COMPOUND_STMT; statement->compound_stmt = (AstCompoundStmt) { .first_stmt = start }; return true; +FAILED: + sema_context_pop_ct_stack(context, for_context); + return false; } @@ -2718,6 +2756,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) .label_start = 0, .current_local = 0 }; + vec_resize(context->ct_locals, 0); // Clear returns vec_resize(context->returns, 0); diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index be508881d..cd943fd15 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -321,13 +321,26 @@ void sema_analysis_run(void) void sema_context_init(SemaContext *context, CompilationUnit *unit) { - *context = (SemaContext) { .unit = unit, .compilation_unit = unit, .locals = global_context_acquire_locals_list() }; + *context = (SemaContext) { .unit = unit, .compilation_unit = unit, + .ct_locals = global_context_acquire_locals_list(), + .locals = global_context_acquire_locals_list() }; +} + +void sema_context_pop_ct_stack(SemaContext *context, unsigned old_state) +{ + vec_resize(context->ct_locals, old_state); +} + +unsigned sema_context_push_ct_stack(SemaContext *context) +{ + return vec_size(context->ct_locals); } void sema_context_destroy(SemaContext *context) { if (!context->unit) return; generic_context_release_locals_list(context->locals); + generic_context_release_locals_list(context->ct_locals); } Decl **global_context_acquire_locals_list(void) @@ -338,6 +351,7 @@ Decl **global_context_acquire_locals_list(void) } Decl **result = VECLAST(global_context.locals_list); vec_pop(global_context.locals_list); + vec_resize(result, 0); return result; } diff --git a/src/version.h b/src/version.h index 9316f0ed8..b45473eaf 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.109" \ No newline at end of file +#define COMPILER_VERSION "0.3.111" \ No newline at end of file diff --git a/test/test_suite/compile_time/ct_checks.c3t b/test/test_suite/compile_time/ct_checks.c3t index e571238e5..357665a2c 100644 --- a/test/test_suite/compile_time/ct_checks.c3t +++ b/test/test_suite/compile_time/ct_checks.c3t @@ -9,6 +9,7 @@ fn void main() var $y = 23; bool c = $checks(int z = 23, $y += 23, &c); bool d = $checks(&c, $y, int yy = 23); + int z = $y; io::printfln("%s %s %s", b, $y, c); } @@ -16,5 +17,6 @@ fn void main() store i32 0 store i8 0 - store i8 0 store i8 1 + store i8 1 + store i32 46 diff --git a/test/test_suite/macros/hash_ident.c3 b/test/test_suite/macros/hash_ident.c3 index a962d6f0b..4dfa36bd1 100644 --- a/test/test_suite/macros/hash_ident.c3 +++ b/test/test_suite/macros/hash_ident.c3 @@ -18,7 +18,8 @@ fn void main() int x = 0; @cofefe(x += 1); @cofefe(xx()); - @cofefe($x += 1); // #error: Cannot modify '$x' inside of a runtime scope. + @cofefe($x += 1); + $assert($x == 2); @cofefe(y += 1); // #error: 'y' could not be found } diff --git a/test/test_suite/stdlib/map.c3t b/test/test_suite/stdlib/map.c3t index 974fa9b39..9863ce5b7 100644 --- a/test/test_suite/stdlib/map.c3t +++ b/test/test_suite/stdlib/map.c3t @@ -508,7 +508,7 @@ after_check100: ; preds = %if.then panic_block: ; preds = %assign_optional %160 = load void (i8*, i64, i8*, i64, i8*, i64, i32)*, void (i8*, i64, i8*, i64, i8*, i64, i32)** @std_core_builtin_panic, align 8 - call void %160(i8* getelementptr inbounds ([28 x i8], [28 x i8]* @.panic_msg, i64 0, i64 0), i64 27, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.file, i64 0, i64 0), i64 6, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.func, i64 0, i64 0), i64 4, i32 245) + call void %160(i8* getelementptr inbounds ([28 x i8], [28 x i8]* @.panic_msg, i64 0, i64 0), i64 27, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.file, i64 0, i64 0), i64 6, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.func, i64 0, i64 0), i64 4, i32 246) unreachable noerr_block: ; preds = %after_check100 diff --git a/test/test_suite2/compile_time/ct_checks.c3t b/test/test_suite2/compile_time/ct_checks.c3t index e571238e5..357665a2c 100644 --- a/test/test_suite2/compile_time/ct_checks.c3t +++ b/test/test_suite2/compile_time/ct_checks.c3t @@ -9,6 +9,7 @@ fn void main() var $y = 23; bool c = $checks(int z = 23, $y += 23, &c); bool d = $checks(&c, $y, int yy = 23); + int z = $y; io::printfln("%s %s %s", b, $y, c); } @@ -16,5 +17,6 @@ fn void main() store i32 0 store i8 0 - store i8 0 store i8 1 + store i8 1 + store i32 46 diff --git a/test/test_suite2/macros/hash_ident.c3 b/test/test_suite2/macros/hash_ident.c3 index a962d6f0b..4dfa36bd1 100644 --- a/test/test_suite2/macros/hash_ident.c3 +++ b/test/test_suite2/macros/hash_ident.c3 @@ -18,7 +18,8 @@ fn void main() int x = 0; @cofefe(x += 1); @cofefe(xx()); - @cofefe($x += 1); // #error: Cannot modify '$x' inside of a runtime scope. + @cofefe($x += 1); + $assert($x == 2); @cofefe(y += 1); // #error: 'y' could not be found } diff --git a/test/test_suite2/stdlib/map.c3t b/test/test_suite2/stdlib/map.c3t index 457eb2b4a..77f9bc6cb 100644 --- a/test/test_suite2/stdlib/map.c3t +++ b/test/test_suite2/stdlib/map.c3t @@ -364,7 +364,7 @@ after_check67: ; preds = %if.then panic_block: ; preds = %assign_optional %81 = load ptr, ptr @std_core_builtin_panic, align 8 - call void %81(ptr @.panic_msg, i64 27, ptr @.file, i64 6, ptr @.func, i64 4, i32 245) + call void %81(ptr @.panic_msg, i64 27, ptr @.file, i64 6, ptr @.func, i64 4, i32 246) unreachable noerr_block: ; preds = %after_check67