From e4e499edd24862cc6e46bc09e0063625d3bb4438 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 23 Aug 2025 12:00:17 +0200 Subject: [PATCH] Allow `$defined` take declarations: `$defined(int x = y)` Taking the address of a label would cause a crash. #2430 --- lib/std/collections/hashmap.c3 | 5 +-- lib/std/collections/linked_hashmap.c3 | 5 +-- lib/std/collections/tuple.c3 | 10 +++--- lib/std/core/mem.c3 | 16 ++++----- lib/std/core/mem_allocator.c3 | 6 ++-- lib/std/math/math.c3 | 2 +- lib/std/threads/thread.c3 | 4 +-- releasenotes.md | 2 ++ src/compiler/compiler_internal.h | 4 +-- src/compiler/parse_expr.c | 27 +++++++------- src/compiler/parse_global.c | 10 ++++++ src/compiler/sema_decls.c | 35 ++++++++++++++----- src/compiler/sema_expr.c | 12 +++++-- src/compiler/sema_stmts.c | 12 +++---- .../defined_decl.c3 | 5 +++ test/test_suite/statements/label_addr.c3 | 10 ++++++ 16 files changed, 109 insertions(+), 56 deletions(-) create mode 100644 test/test_suite/compile_time_introspection/defined_decl.c3 create mode 100644 test/test_suite/statements/label_addr.c3 diff --git a/lib/std/collections/hashmap.c3 b/lib/std/collections/hashmap.c3 index 81f01341a..cdcf9c895 100644 --- a/lib/std/collections/hashmap.c3 +++ b/lib/std/collections/hashmap.c3 @@ -212,8 +212,9 @@ fn Entry*? HashMap.get_entry(&map, Key key) } <* - Get the value or update and - @require @assignable_to(#expr, Value) + Get the value or set it to the value + + @require $defined(Value val = #expr) *> macro Value HashMap.@get_or_set(&map, Key key, Value #expr) { diff --git a/lib/std/collections/linked_hashmap.c3 b/lib/std/collections/linked_hashmap.c3 index f44f6c209..91e3aabdf 100644 --- a/lib/std/collections/linked_hashmap.c3 +++ b/lib/std/collections/linked_hashmap.c3 @@ -188,8 +188,9 @@ fn LinkedEntry*? LinkedHashMap.get_entry(&map, Key key) } <* - Get the value or update and - @require @assignable_to(#expr, Value) + Get the value or set it to the value + + @require $defined(Value val = #expr) *> macro Value LinkedHashMap.@get_or_set(&map, Key key, Value #expr) { diff --git a/lib/std/collections/tuple.c3 b/lib/std/collections/tuple.c3 index d077df9dc..d719bcbab 100644 --- a/lib/std/collections/tuple.c3 +++ b/lib/std/collections/tuple.c3 @@ -15,8 +15,8 @@ fn usz? Pair.to_format(&self, Formatter* f) @dynamic <* @param [&out] a @param [&out] b - @require @assignable_to(self.first, $typeof(*a)) : "You cannot assign the first value to a" - @require @assignable_to(self.second, $typeof(*b)) : "You cannot assign the second value to b" + @require $defined(*a = self.first) : "You cannot assign the first value to a" + @require $defined(*b = self.second) : "You cannot assign the second value to b" *> macro void Pair.unpack(&self, a, b) { @@ -49,9 +49,9 @@ fn usz? Triple.to_format(&self, Formatter* f) @dynamic @param [&out] a @param [&out] b @param [&out] c - @require @assignable_to(self.first, $typeof(*a)) : "You cannot assign the first value to a" - @require @assignable_to(self.second, $typeof(*b)) : "You cannot assign the second value to b" - @require @assignable_to(self.third, $typeof(*c)) : "You cannot assign the second value to c" + @require $defined(*a = self.first) : "You cannot assign the first value to a" + @require $defined(*b = self.second) : "You cannot assign the second value to b" + @require $defined(*c = self.third) : "You cannot assign the second value to c" *> macro void Triple.unpack(&self, a, b, c) { diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 78a4afa3b..de1911b06 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -44,7 +44,7 @@ fn usz os_pagesize() @param ptr : "The pointer address to load from." @param mask : "The mask for the load" @param passthru : "The value to use for non masked values" - @require @assignable_to(&&passthru, $typeof(ptr)) : "Pointer and passthru must match" + @require $defined(*ptr = passthru) : "Pointer and passthru must match" @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" @require passthru.len == mask.len : "Mask and passthru must have the same length" @@ -63,7 +63,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru) @param passthru : "The value to use for non masked values" @param $alignment : "The alignment to assume for the pointer" - @require @assignable_to(&&passthru, $typeof(ptr)) : "Pointer and passthru must match" + @require $defined(*ptr = passthru) : "Pointer and passthru must match" @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" @require passthru.len == mask.len : "Mask and passthru must have the same length" @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" @@ -84,7 +84,7 @@ macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment) @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" - @require @assignable_to(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match" + @require $defined(*ptrvec[0] = passthru[0]) : "Pointer and passthru must match" @require passthru.len == mask.len : "Mask and passthru must have the same length" @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" @@ -106,7 +106,7 @@ macro gather(ptrvec, bool[<*>] mask, passthru) @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" - @require @assignable_to(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match" + @require $defined(*ptrvec[0] = passthru[0]) : "Pointer and passthru must match" @require passthru.len == mask.len : "Mask and passthru must have the same length" @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" @@ -126,7 +126,7 @@ macro @gather_aligned(ptrvec, bool[<*>] mask, passthru, usz $alignment) @param value : "The value to store masked" @param mask : "The mask for the store" - @require @assignable_to(&&value, $typeof(ptr)) : "Pointer and value must match" + @require $defined(*ptr = value) : "Pointer and value must match" @require @typekind(value) == VECTOR : "Expected value to be a vector" @require value.len == mask.len : "Mask and value must have the same length" *> @@ -141,7 +141,7 @@ macro masked_store(ptr, value, bool[<*>] mask) @param mask : "The mask for the store" @param $alignment : "The alignment of the pointer" - @require @assignable_to(&&value, $typeof(ptr)) : "Pointer and value must match" + @require $defined(*ptr = value) : "Pointer and value must match" @require @typekind(value) == VECTOR : "Expected value to be a vector" @require value.len == mask.len : "Mask and value must have the same length" @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" @@ -158,7 +158,7 @@ macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment) @param mask : "The mask for the store" @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" @require @typekind(value) == VECTOR : "Expected value to be a vector" - @require @assignable_to(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match" + @require $defined(*ptrvec[0] = value[0]) : "Pointer and value must match" @require value.len == mask.len : "Mask and value must have the same length" @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" @@ -176,7 +176,7 @@ macro scatter(ptrvec, value, bool[<*>] mask) @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" @require @typekind(value) == VECTOR : "Expected value to be a vector" - @require @assignable_to(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match" + @require $defined(*ptrvec[0] = value[0]) : "Pointer and value must match" @require value.len == mask.len : "Mask and value must have the same length" @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" diff --git a/lib/std/core/mem_allocator.c3 b/lib/std/core/mem_allocator.c3 index ca7dbb9f3..bf0921cac 100644 --- a/lib/std/core/mem_allocator.c3 +++ b/lib/std/core/mem_allocator.c3 @@ -167,7 +167,7 @@ macro void free_aligned(Allocator allocator, void* ptr) <* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead" @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" + @require $vacount == 0 ||| $defined($Type t = $vaexpr[0]) : "The second argument must be an initializer for the type" *> macro new(Allocator allocator, $Type, ...) @nodiscard { @@ -183,7 +183,7 @@ macro new(Allocator allocator, $Type, ...) @nodiscard <* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead" @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" + @require $vacount == 0 ||| $defined($Type t = $vaexpr[0]) : "The second argument must be an initializer for the type" *> macro new_try(Allocator allocator, $Type, ...) @nodiscard { @@ -200,7 +200,7 @@ macro new_try(Allocator allocator, $Type, ...) @nodiscard Allocate using an aligned allocation. This is necessary for types with a default memory alignment exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" + @require $vacount == 0 ||| $defined($Type t = $vaexpr[0]) : "The second argument must be an initializer for the type" *> macro new_aligned(Allocator allocator, $Type, ...) @nodiscard { diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index 12009c2df..db139fd5d 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -130,7 +130,7 @@ macro atan2(x, y) @require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value" @require @typekind(sinp) == POINTER : "Expected sinp to be a pointer" @require @typematch(sinp, cosp) : "Expected sinp and cosp to have the same type" - @require @assignable_to(x, $typeof(*sinp)) : "Expected x and sinp/cosp to have the same type" + @require $defined(*sinp = x) : "Expected x and sinp/cosp to have the same type" *> macro sincos_ref(x, sinp, cosp) { diff --git a/lib/std/threads/thread.c3 b/lib/std/threads/thread.c3 index 693dd8ef6..fa375f776 100644 --- a/lib/std/threads/thread.c3 +++ b/lib/std/threads/thread.c3 @@ -70,12 +70,12 @@ macro void? ConditionVariable.wait(&cond, Mutex* mutex) return NativeConditionVariable.wait((NativeConditionVariable*)cond, (NativeMutex*)mutex); } <* - @require @assignable_to(#ms_or_duration, Duration) || @assignable_to(#ms_or_duration, ulong) + @require $defined(Duration d = #ms_or_duration) ||| $defined(ulong l = #ms_or_duration) @return? thread::WAIT_TIMEOUT, thread::WAIT_FAILED *> macro void? ConditionVariable.wait_timeout(&cond, Mutex* mutex, #ms_or_duration) @safemacro { - $if @assignable_to(#ms_or_duration, Duration): + $if $defined(*&&(Duration){} = #ms_or_duration): return NativeConditionVariable.wait_timeout_duration((NativeConditionVariable*)cond, (NativeMutex*)mutex, #ms_or_duration); $else return NativeConditionVariable.wait_timeout((NativeConditionVariable*)cond, (NativeMutex*)mutex, #ms_or_duration); diff --git a/releasenotes.md b/releasenotes.md index d874690c5..56b2e75ce 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -20,6 +20,7 @@ - Error if a stack allocated variable is too big (configurable with `--max-stack-object-size`). - Add `@safeinfer` to allow `var` to be used locally. - Types converts to typeid implicitly. +- Allow `$defined` take declarations: `$defined(int x = y)` ### Fixes - List.remove_at would incorrectly trigger ASAN. @@ -58,6 +59,7 @@ - Incorrect type checking when &[] and [] return optional values. - Failed to find subscript overloading on optional values. - `Socket.get_option` didn't properly call `getsockopt`, and `getsockopt` had an invalid signature. +- Taking the address of a label would cause a crash. #2430 ### Stdlib changes - Add `==` to `Pair`, `Triple` and TzDateTime. Add print to `Pair` and `Triple`. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index d316af04c..08c57e4e5 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2419,8 +2419,8 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl); bool sema_analyse_method_register(SemaContext *context, Decl *method); bool sema_resolve_type_structure(SemaContext *context, Type *type); -bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl); -bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local); +bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl, bool *check_defined); +bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local, bool *check_defined); bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement); bool sema_analyse_ct_echo_stmt(SemaContext *context, Ast *statement); bool sema_analyse_statement(SemaContext *context, Ast *statement); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 443d2e641..a65f832aa 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -95,20 +95,6 @@ bool parse_generic_expr_list(ParseContext *c, Expr ***exprs_ref) return true; } -static bool parse_expr_list(ParseContext *c, Expr ***exprs_ref, TokenType end_token) -{ - while (!try_consume(c, end_token)) - { - ASSIGN_EXPR_OR_RET(Expr *expr, parse_expr(c), false); - vec_add(*exprs_ref, expr); - if (!try_consume(c, TOKEN_COMMA)) - { - CONSUME_OR_RET(end_token, false); - return true; - } - } - return true; -} /** * rethrow_expr ::= call_expr '!' @@ -1170,7 +1156,18 @@ static Expr *parse_ct_defined(ParseContext *c, Expr *left, SourceSpan lhs_start Expr *defined = expr_new(EXPR_CT_DEFINED, c->span); advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); - if (!parse_expr_list(c, &defined->expression_list, TOKEN_RPAREN)) return poisoned_expr; + while (!try_consume(c, TOKEN_RPAREN)) + { + + ASSIGN_EXPR_OR_RET(Expr *expr, parse_decl_or_expr(c), poisoned_expr); + vec_add(defined->expression_list, expr); + if (!try_consume(c, TOKEN_COMMA)) + { + CONSUME_OR_RET(TOKEN_RPAREN, false); + break; + } + } + RANGE_EXTEND_PREV(defined); return defined; } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 9230ef234..e419c4ffc 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -888,6 +888,16 @@ Expr *parse_decl_or_expr(ParseContext *c) // If it's not a type info, we assume an expr. if (expr->expr_kind != EXPR_TYPEINFO) return expr; + switch (c->tok) + { + case TOKEN_RPAREN: + case TOKEN_RBRACKET: + case TOKEN_RBRACE: + case TOKEN_RVEC: + return expr; + default: + break; + } // Otherwise we expect a declaration. ASSIGN_DECL_OR_RET(decl, parse_local_decl_after_type(c, expr->type_expr), poisoned_expr); DECL: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index be4eacacf..fefdeba0b 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -4470,7 +4470,7 @@ static bool sema_analyse_variable_type(SemaContext *context, Type *type, SourceS /** * Analyse $foo and $Foo variables. */ -bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) +bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl, bool *check_failed) { Expr *init; ASSERT(decl->decl_kind == DECL_VAR && "Should only be called on variables."); @@ -4500,6 +4500,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) // If this isn't a type, it's an error. if (!expr_is_const_typeid(init)) { + if (check_failed) goto FAIL_CHECK; SEMA_ERROR(decl->var.init_expr, "Expected a type assigned to %s.", decl->name); goto FAIL; } @@ -4519,6 +4520,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) { if (type_is_inferred(decl->type)) { + if (check_failed) goto FAIL_CHECK; SEMA_ERROR(type_info, "No size could be inferred."); goto FAIL; } @@ -4536,6 +4538,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) // Check that it is constant. if (!expr_is_runtime_const(init)) { + if (check_failed) goto FAIL_CHECK; SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); goto FAIL; } @@ -4546,6 +4549,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) { if (init->expr_kind == EXPR_TYPEINFO) { + if (check_failed) goto FAIL_CHECK; SEMA_ERROR(init, "You can't assign a type to a regular compile time variable like '%s', but it would be allowed if the variable was a compile time type variable. Such a variable needs to have a type-like name, e.g. '$MyType'.", decl->name); goto FAIL; } @@ -4553,6 +4557,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) // Check it is constant. if (!expr_is_runtime_const(init)) { + if (check_failed) goto FAIL_CHECK; SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); goto FAIL; } @@ -4567,6 +4572,12 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) UNREACHABLE } return sema_add_local(context, decl); +FAIL_CHECK: + if (check_failed) + { + *check_failed = true; + return false; + } FAIL: sema_add_local(context, decl); return decl_poison(decl); @@ -4574,7 +4585,7 @@ FAIL: /** * Analyse a regular global or local declaration, e.g. int x = 123 */ -bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) +bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local, bool *check_defined) { ASSERT(decl->decl_kind == DECL_VAR && "Unexpected declaration type"); @@ -4586,7 +4597,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) { case VARDECL_LOCAL_CT: case VARDECL_LOCAL_CT_TYPE: - return sema_analyse_var_decl_ct(context, decl); + return sema_analyse_var_decl_ct(context, decl, check_defined); case VARDECL_GLOBAL: is_global = true; break; @@ -4626,7 +4637,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) { } - else + else if (!check_defined) { if (context->call_env.kind == CALL_ENV_GLOBAL_INIT && !context->call_env.in_no_eval) { @@ -4655,15 +4666,16 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) return decl_poison(decl); } ASSERT(!decl->var.no_init); - if (kind == VARDECL_LOCAL && !context_is_macro(context) && init_expr->expr_kind != EXPR_LAMBDA && !decl->var.safe_infer) + if (!check_defined && kind == VARDECL_LOCAL && !context_is_macro(context) && init_expr->expr_kind != EXPR_LAMBDA && !decl->var.safe_infer) { SEMA_ERROR(decl, "Defining a variable using 'var %s = ...' is only allowed inside a macro, or when defining a lambda. You can override this by adding the attribute '@safeinfer' to the declaration.", decl->name); return decl_poison(decl); } if (!sema_analyse_expr(context, init_expr)) return decl_poison(decl); - if (global_level_var || !type_is_abi_aggregate(init_expr->type)) sema_cast_const(init_expr); + if (check_defined || global_level_var || !type_is_abi_aggregate(init_expr->type)) sema_cast_const(init_expr); if (global_level_var && !expr_is_runtime_const(init_expr)) { + if (check_defined) return *check_defined = true, false; SEMA_ERROR(init_expr, "This expression cannot be evaluated at compile time."); return decl_poison(decl); } @@ -4675,14 +4687,18 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) case STORAGE_NORMAL: break; case STORAGE_WILDCARD: + if (check_defined) return *check_defined = true, false; SEMA_ERROR(init_expr, "No type can be inferred from the optional result."); return decl_poison(decl); case STORAGE_VOID: + if (check_defined) return *check_defined = true, false; SEMA_ERROR(init_expr, "You cannot initialize a value to 'void'."); return decl_poison(decl); case STORAGE_COMPILE_TIME: + if (check_defined) return *check_defined = true, false; if (init_expr->type == type_untypedlist) { + if (check_defined) return *check_defined = true, false; SEMA_ERROR(init_expr, "The type of an untyped list cannot be inferred, you can try adding an explicit type to solve this."); return decl_poison(decl); @@ -4697,6 +4713,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) SEMA_ERROR(init_expr, "You can't store a compile time type in a variable."); return decl_poison(decl); case STORAGE_UNKNOWN: + if (check_defined) return *check_defined = true, false; SEMA_ERROR(init_expr, "You cannot initialize a value to %s as it has unknown size.", type_quoted_error_string(init_expr->type)); return decl_poison(decl); @@ -4753,7 +4770,8 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) CallEnvKind env_kind = context->call_env.kind; if (is_static) context->call_env.kind = CALL_ENV_FUNCTION_STATIC; decl->in_init = true; - success = sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, false, true, NULL); + success = sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, false, true, check_defined); + if (!success && check_defined) return false; decl->in_init = false; context->call_env.kind = env_kind; if (infer_len) @@ -4768,6 +4786,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) if (!success) return decl_poison(decl); if (!expr_is_runtime_const(init)) { + if (check_defined) return *check_defined = true, false; SEMA_ERROR(init, "The expression must be a constant value."); return decl_poison(decl); } @@ -5359,7 +5378,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) if (!sema_analyse_macro(context, decl, &erase_decl)) goto FAILED; break; case DECL_VAR: - if (!sema_analyse_var_decl(context, decl, false)) goto FAILED; + if (!sema_analyse_var_decl(context, decl, false, NULL)) goto FAILED; break; case DECL_ATTRIBUTE: if (!sema_analyse_attribute_decl(context, context, decl, &erase_decl)) goto FAILED; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 48bd3cddc..f679413d7 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -8349,6 +8349,8 @@ static inline const char *sema_addr_may_take_of_ident(Expr *inner) return sema_addr_may_take_of_var(inner, decl); case DECL_MACRO: return "It is not possible to take the address of a macro."; + case DECL_LABEL: + return "It is not possible to take the address of a label."; default: UNREACHABLE } @@ -10411,7 +10413,6 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_OPERATOR_CHARS: case EXPR_MACRO_BODY_EXPANSION: case EXPR_BUILTIN_ACCESS: - case EXPR_DECL: case EXPR_LAST_FAULT: case EXPR_DEFAULT_ARG: case EXPR_IDENTIFIER: @@ -10420,6 +10421,13 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_CT_SUBSCRIPT: case EXPR_IOTA_DECL: UNREACHABLE + case EXPR_DECL: + if (!sema_analyse_var_decl(context, main_expr->decl_expr, true, &failed)) + { + if (!failed) goto FAIL; + success = false; + } + break; case EXPR_BINARY: main_expr->resolve_status = RESOLVE_RUNNING; if (!sema_expr_analyse_binary(active_context, NULL, main_expr, &failed)) @@ -10936,7 +10944,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr, { Decl *decl = expr->decl_expr; bool erase = decl->var.kind == VARDECL_LOCAL_CT_TYPE || decl->var.kind == VARDECL_LOCAL_CT; - if (!sema_analyse_var_decl(context, decl, true)) return false; + if (!sema_analyse_var_decl(context, decl, true, NULL)) return false; if (decl->decl_kind == DECL_ERASED) { expr->expr_kind = EXPR_NOP; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index ffbdd4c7b..dbf2120b6 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -944,7 +944,7 @@ static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr) Decl *decl = decl_new_var(ident->unresolved_ident_expr.ident, ident->span, var_type, VARDECL_LOCAL); // 4e. Analyse it - if (!sema_analyse_var_decl(context, decl, true)) return false; + if (!sema_analyse_var_decl(context, decl, true, NULL)) return false; expr->expr_kind = EXPR_TRY; expr->try_expr = (ExprTry) { .decl = decl, .optional = optional }; @@ -1003,7 +1003,7 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) decl->var.no_init = true; // 4e. Analyse it - if (!sema_analyse_var_decl(context, decl, true)) return false; + if (!sema_analyse_var_decl(context, decl, true, NULL)) return false; RESOLVE_EXPRS:; Expr **exprs = expr->unresolved_catch_expr.exprs; @@ -1221,12 +1221,12 @@ static inline bool sema_analyse_decls_stmt(SemaContext *context, Ast *statement) VarDeclKind kind = decl->var.kind; if (kind == VARDECL_LOCAL_CT_TYPE || kind == VARDECL_LOCAL_CT) { - if (!sema_analyse_var_decl_ct(context, decl)) return false; + if (!sema_analyse_var_decl_ct(context, decl, NULL)) return false; statement->decls_stmt[i] = NULL; } else { - if (!sema_analyse_var_decl(context, decl, true)) return false; + if (!sema_analyse_var_decl(context, decl, true, NULL)) return false; should_nop = false; } } @@ -1239,7 +1239,7 @@ static inline bool sema_analyse_declare_stmt(SemaContext *context, Ast *statemen Decl *decl = statement->declare_stmt; VarDeclKind kind = decl->var.kind; bool erase = kind == VARDECL_LOCAL_CT_TYPE || kind == VARDECL_LOCAL_CT; - if (!sema_analyse_var_decl(context, decl, true)) + if (!sema_analyse_var_decl(context, decl, true, NULL)) { if (!decl_ok(decl)) context->active_scope.is_poisoned = true; return false; @@ -3061,7 +3061,7 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement SEMA_ERROR(expr, "Only 'var $foo' and 'var $Type' declarations are allowed in '$for'"); goto FAILED; } - if (!sema_analyse_var_decl_ct(context, decl)) goto FAILED; + if (!sema_analyse_var_decl_ct(context, decl, NULL)) goto FAILED; continue; } // If expression evaluate it and make sure it is constant. diff --git a/test/test_suite/compile_time_introspection/defined_decl.c3 b/test/test_suite/compile_time_introspection/defined_decl.c3 new file mode 100644 index 000000000..d39622229 --- /dev/null +++ b/test/test_suite/compile_time_introspection/defined_decl.c3 @@ -0,0 +1,5 @@ +fn int main(String[] args) +{ + $defined(int a = 3.0); + $defined(int[*] b); // #error: Inferred array types can only be used in declarations +} \ No newline at end of file diff --git a/test/test_suite/statements/label_addr.c3 b/test/test_suite/statements/label_addr.c3 new file mode 100644 index 000000000..4f3c189ae --- /dev/null +++ b/test/test_suite/statements/label_addr.c3 @@ -0,0 +1,10 @@ +import std; +fn int main(String[] args) +{ + switch FOO: (1) + { + case 1: + io::printfn("%p", &FOO); // #error: It is not possible to take the address of a label + } + return 0; +} \ No newline at end of file