From 0ed917cdc21082aa6d80a0d38d6e930245c4fd17 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 23 Feb 2026 19:15:23 +0100 Subject: [PATCH] Improve error when trying to use an extern const as a compile time constant. #2969 --- releasenotes.md | 1 + src/compiler/sema_decls.c | 18 ++++++++++++++++-- src/compiler/sema_expr.c | 10 +++++++++- .../compile_time/extern_is_not_const.c3 | 16 ++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 test/test_suite/compile_time/extern_is_not_const.c3 diff --git a/releasenotes.md b/releasenotes.md index cfb679486..b60a0532f 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -20,6 +20,7 @@ - Constdef declarations introduced. - Properly support `@deprecated` as contract. - Support deprecating enum values. +- Improve error when trying to use an extern const as a compile time constant. #2969 ### Stdlib changes - Summarize sort macros as generic function wrappers to reduce the amount of generated code. #2831 diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 7769a20dd..2783745b6 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -4707,6 +4707,20 @@ static bool sema_analyse_variable_type(SemaContext *context, Type *type, SourceS UNREACHABLE } +INLINE void sema_error_not_constant(SemaContext *context, Decl *target, Expr *init) +{ + if (init->expr_kind == EXPR_IDENTIFIER) + { + Decl *decl = init->ident_expr; + if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_CONST && decl->is_extern) + { + SEMA_ERROR(init, "%s must be assigned a compile-time constant. 'extern' constants are resolved at link time and therefore are not compile-time values.", target->name); + return; + } + } + SEMA_ERROR(init, "Expected a compile time constant value assigned to %s.", target->name); + +} /** * Analyse $foo and $Foo variables. */ @@ -4792,7 +4806,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl, bool *check_defi if (!expr_is_runtime_const(init)) { if (check_defined) goto FAIL_CHECK; - SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); + sema_error_not_constant(context, decl, init); goto FAIL; } break; @@ -4811,7 +4825,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl, bool *check_defi if (!expr_is_runtime_const(init)) { if (check_defined) goto FAIL_CHECK; - SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); + sema_error_not_constant(context, decl, init); goto FAIL; } // Update the type. diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 644f7e5d5..b93a768de 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1679,7 +1679,15 @@ static bool sema_analyse_parameter(SemaContext *context, Expr *arg, Decl *param, { RETURN_SEMA_FUNC_ERROR(definition, arg, "This method is only valid on a compile time constant value."); } - RETURN_SEMA_FUNC_ERROR(definition, arg, "A compile time parameter must always be a constant, did you mistake it for a normal parameter?"); + if (arg->expr_kind == EXPR_IDENTIFIER) + { + Decl *decl = arg->ident_expr; + if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_CONST && decl->is_extern) + { + RETURN_SEMA_FUNC_ERROR(definition, arg, "A compile-time parameter requires a value known at compile time. Did you mistake if for a regular parameter? 'extern' constants are not compile-time constants."); + } + } + RETURN_SEMA_FUNC_ERROR(definition, arg, "A compile time parameter must always be a compile-time constant value, did you mistake it for a regular parameter?"); } break; case VARDECL_PARAM_CT_TYPE: diff --git a/test/test_suite/compile_time/extern_is_not_const.c3 b/test/test_suite/compile_time/extern_is_not_const.c3 new file mode 100644 index 000000000..0f6682b74 --- /dev/null +++ b/test/test_suite/compile_time/extern_is_not_const.c3 @@ -0,0 +1,16 @@ +module test; +import std; + +extern const void* FOO @cname("malloc"); + +macro foo($x) {} + +fn void test() +{ + var $x = FOO; // #error: $x must be assigned a compile-time constant. 'extern' constants are resolved +} + +fn void test2() +{ + foo(FOO); // #error: $x must be assigned a compile-time constant. 'extern' constants are resolved +}