From 6f11260a5c444cbb1a9dacbc95653bfb7fbaec02 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 10 Oct 2025 14:04:19 +0200 Subject: [PATCH] Disallow aliasing of `@local` symbols with a higher visibility in the alias. --- releasenotes.md | 5 +++-- src/compiler/context.c | 1 + src/compiler/sema_decls.c | 5 +++++ src/compiler/sema_expr.c | 2 +- src/compiler/sema_name_resolution.c | 2 +- test/test_suite/define/alias_visibility.c3t | 20 +++++++++++++++---- .../define/alias_visibility_forbidden.c3 | 2 ++ 7 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 test/test_suite/define/alias_visibility_forbidden.c3 diff --git a/releasenotes.md b/releasenotes.md index d2b8bee28..cb2f4bbe1 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -8,7 +8,8 @@ - Add new builtins `$$str_snakecase` `$$str_replace` and `$$str_pascalcase`. - `"build-dir"` option now available for `project.json`, added to project. #2323 - Allow `..` ranges to use "a..a-1" in order to express zero length. - +- Disallow aliasing of `@local` symbols with a higher visibility in the alias. + ### Fixes - Bug in `io::write_using_write_byte`. - Bitstruct value cannot be used to index a const array in compile time. #2512 @@ -16,8 +17,8 @@ - Bitstruct truncated constant error escapes `$defined` #2515. - Compiler segfault when accessing member of number cast to bitstruct #2516. - Compiler assert when getting a member of a `bitstruct : char @bigendian` #2517. -- Incorrect visibility on local globals with public aliases. #2519 - Add ??? and +++= to list-precedence. +- Fix issues with linking when using symbol aliases. #2519 ### Stdlib changes - Sorting functions correctly took slices by value, but also other types by value. Now, only slices are accepted by value, other containers are always by ref. diff --git a/src/compiler/context.c b/src/compiler/context.c index 2c34c2228..48865baf2 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -142,6 +142,7 @@ bool context_is_macro(SemaContext *context) void unit_register_external_symbol(SemaContext *context, Decl *decl) { + decl = decl_flatten(decl); if (decl->is_external_visible) return; Module *active_module = context->current_macro ? context->original_module : context->compilation_unit->module; if (decl->unit->module == active_module) return; diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 7875c0c95..38f89aebc 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -5302,6 +5302,11 @@ static inline bool sema_analyse_alias(SemaContext *context, Decl *decl, bool *er } decl->type = symbol->type; decl->define_decl.alias = symbol; + if (symbol->visibility == VISIBLE_LOCAL && decl->visibility < VISIBLE_LOCAL) + { + RETURN_SEMA_ERROR(decl, "A local symbol like '%s' may not be aliased to 'private' or 'public' visibility, please change it to at least be '@private'", symbol->name); + } + // If the symbol is more hidden than the alias, then we must increase the visibility. if (decl_is_externally_visible(decl) && !decl_is_externally_visible(symbol)) { symbol->is_external_visible = true; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 3dce12e03..8c97c9a5a 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -10614,7 +10614,7 @@ static inline bool sema_expr_analyse_lambda(SemaContext *context, Type *target_t { decl->var.is_read = true; } - decl->is_external_visible = true; + decl_flatten(decl)->is_external_visible = true; vec_add(unit->module->lambdas_to_evaluate, decl); } else diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index f3453d82c..1516b3c22 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -830,7 +830,7 @@ INLINE bool sema_resolve_symbol_common(SemaContext *context, NameResolve *name_r SEMA_NOTE(found, "'%s' is defined here.", found->name); return false; } - unit_register_external_symbol(context, found); + if (found->decl_kind != DECL_ALIAS) unit_register_external_symbol(context, found); if (found->unit->module->is_generic) { if (name_resolve->is_parameterized) return true; diff --git a/test/test_suite/define/alias_visibility.c3t b/test/test_suite/define/alias_visibility.c3t index 036aff445..ffaf7c53e 100644 --- a/test/test_suite/define/alias_visibility.c3t +++ b/test/test_suite/define/alias_visibility.c3t @@ -1,20 +1,32 @@ // #target: macos-x64 module test1; alias x = y; -int y @local = 1; +int y @private = 1; + +module test2; +int z @private = 1; module test; import test1; +import test2 @public; + +alias z = test2::z; fn int main(String[] args) { - return test1::x; + return test1::x + z; } + /* #expect: test1.ll -@test1.y.10 = local_unnamed_addr global i32 1, align 4 +@test1.y = local_unnamed_addr global i32 1, align 4 + +/* #expect: test2.ll + +@test2.z = local_unnamed_addr global i32 1, align 4 // #expect: test.ll -@test1.y.10 = external global i32, align 4 \ No newline at end of file +@test1.y = external global i32, align 4 +@test2.z = external global i32, align 4 diff --git a/test/test_suite/define/alias_visibility_forbidden.c3 b/test/test_suite/define/alias_visibility_forbidden.c3 new file mode 100644 index 000000000..eb4815bc9 --- /dev/null +++ b/test/test_suite/define/alias_visibility_forbidden.c3 @@ -0,0 +1,2 @@ +alias x @private = y; // #error: A local symbol like +int y @local = 1;