From a55f56a88f826909fa714babbaff382d4b78140c Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 18 Jun 2025 02:07:07 +0200 Subject: [PATCH] Linker errors when shadowing @local with public function #2198 --- lib/std/core/allocators/temp_allocator.c3 | 2 +- lib/std/math/math_nolibc/rempi.c3 | 2 +- releasenotes.md | 1 + src/compiler/ast.c | 8 ++- src/compiler/context.c | 29 +++++++---- src/compiler/module.c | 3 +- src/compiler/semantic_analyser.c | 1 - test/test_suite/globals/linking_locals.c3t | 50 +++++++++++++++++++ .../test_suite/statements/dead_statements.c3t | 2 +- 9 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 test/test_suite/globals/linking_locals.c3t diff --git a/lib/std/core/allocators/temp_allocator.c3 b/lib/std/core/allocators/temp_allocator.c3 index 92500a541..9eb4647a8 100644 --- a/lib/std/core/allocators/temp_allocator.c3 +++ b/lib/std/core/allocators/temp_allocator.c3 @@ -47,7 +47,7 @@ struct TempAllocatorChunk @local char[*] data; } -const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u; +const usz PAGE_IS_ALIGNED @local = (usz)isz.max + 1u; struct TempAllocatorPage { diff --git a/lib/std/math/math_nolibc/rempi.c3 b/lib/std/math/math_nolibc/rempi.c3 index 035eb3ced..c7dcd2683 100644 --- a/lib/std/math/math_nolibc/rempi.c3 +++ b/lib/std/math/math_nolibc/rempi.c3 @@ -109,7 +109,7 @@ const int[*] IPIO2 = { 0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, }; -const double[*] PIO2 = { +const double[*] PIO2 @local = { 1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */ 7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */ 5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */ diff --git a/releasenotes.md b/releasenotes.md index eb0ba0f43..e0639529f 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -47,6 +47,7 @@ - Bug in AST copying would make operator overloading like `+=` compile incorrectly #2217. - `$defined(#expr)` broken with binary. #2219 - Method ambiguity when importing parent module publicly in private submodule. #2208 +- Linker errors when shadowing @local with public function #2198 ### Stdlib changes - Deprecate `String.is_zstr` and `String.quick_zstr` #2188. diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 8ec7d81bf..cf4664c9c 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -492,7 +492,13 @@ void scratch_buffer_set_extern_decl_name(Decl *decl, bool clear) scratch_buffer_append(decl->name); return; } - if (decl->unit && decl->unit->module) scratch_buffer_append_module(decl->unit->module, decl->is_export); + Module *module = decl->unit ? decl->unit->module : NULL; + if (module) scratch_buffer_append_module(module, decl->is_export); scratch_buffer_append(decl->is_export ? "__" : "."); scratch_buffer_append(decl->name ? decl->name : "$anon"); + if (decl->visibility == VISIBLE_LOCAL) + { + assert(module); + scratch_buffer_printf(".%u", (unsigned)declid(decl)); + } } diff --git a/src/compiler/context.c b/src/compiler/context.c index 47c8d541d..a18dccbc0 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -284,17 +284,28 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) DEBUG_LOG("Registering symbol '%s' in %s.", decl->name, unit->module->name->module); Decl *old; - if ((old = htable_set(&unit->local_symbols, (void*)decl->name, decl))) goto ERR; - if (decl->visibility < VISIBLE_LOCAL) + if ((old = htable_set(&unit->local_symbols, (void*)decl->name, decl))) { - if ((old = htable_set(&unit->module->symbols, (void*)decl->name, decl))) goto ERR; + sema_shadow_error(NULL, decl, old); + decl_poison(decl); + decl_poison(old); + return; + } + + if ((old = htable_set(&unit->module->symbols, (void*)decl->name, decl))) + { + if (old->visibility == VISIBLE_LOCAL && decl->visibility == VISIBLE_LOCAL) return; + if (old->visibility == VISIBLE_LOCAL) + { + sema_shadow_error(NULL, old, decl); + } + else + { + sema_shadow_error(NULL, decl, old); + } + decl_poison(decl); + decl_poison(old); } - return; -ERR: - ASSERT(decl != old); - sema_shadow_error(NULL, decl, old); - decl_poison(decl); - decl_poison(old); } diff --git a/src/compiler/module.c b/src/compiler/module.c index aa24e37bc..329f773c3 100644 --- a/src/compiler/module.c +++ b/src/compiler/module.c @@ -6,7 +6,8 @@ Decl *module_find_symbol(Module *module, const char *symbol) { - return htable_get(&module->symbols, (void*)symbol); + Decl *decl = htable_get(&module->symbols, (void*)symbol); + return decl && decl->visibility != VISIBLE_LOCAL ? decl : NULL; } void scratch_buffer_append_module(Module *module, bool is_export) diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index b0af473bf..957d6708f 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -219,7 +219,6 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) { FOREACH(Decl *, decl, decls) { - if (decl->visibility == VISIBLE_LOCAL) continue; decl->unit = unit; switch (decl->decl_kind) { diff --git a/test/test_suite/globals/linking_locals.c3t b/test/test_suite/globals/linking_locals.c3t new file mode 100644 index 000000000..0bd794ccf --- /dev/null +++ b/test/test_suite/globals/linking_locals.c3t @@ -0,0 +1,50 @@ +// #target: macos-x64 +module mod; + +fn void call_foo() => foo(); + +fn void foo() @local {} + +module mod; + +fn void foo() @local {} +macro test() => foo(); + +module test; +import mod; + +fn void main() { + mod::call_foo(); + mod::test(); +} + +/* #expect: test.ll + +define void @test.main() #0 { +entry: + call void @mod.call_foo() + call void @mod.foo. + ret void +} + +declare void @mod.call_foo() #0 + +declare void @mod.foo + + +/* #expect: mod.ll + +define void @mod.call_foo() +entry: + call void @mod.foo. + ret void + +define internal void @mod.foo. +entry: + ret void +} + +define void @mod.foo. +entry: + ret void +} diff --git a/test/test_suite/statements/dead_statements.c3t b/test/test_suite/statements/dead_statements.c3t index bfdf2bd45..51735442e 100644 --- a/test/test_suite/statements/dead_statements.c3t +++ b/test/test_suite/statements/dead_statements.c3t @@ -25,7 +25,7 @@ fn int main(String[] args) } /* #expect: test.ll -define internal i64 @test.load_corpus2(ptr %0, i64 %1, ptr %2, i64 %3) #0 { +define internal i64 @test.load_corpus2 entry: %code = alloca %"char[]", align 8 %path = alloca %"char[]", align 8