Commit e2cbbd0c26

mlugg <mlugg@mlugg.co.uk>
2024-03-08 22:42:45
Sema: perform codegen for anon decl created by `@extern`
This fixes a bug where, at least with the LLVM backend, `@extern` calls which had the same name as a normal `extern` in the same Zcu would result in the `@extern` incorrectly suffixing the identifier `.2`. Usually, the LLVM backend has a system to change the generated globals to "collapse" them all together, but it only works if `updateDecl` is called!
1 parent 9cf28d1
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -25989,41 +25989,35 @@ fn zirBuiltinExtern(
     }
     const ptr_info = ty.ptrInfo(mod);
 
-    // TODO check duplicate extern
-
     const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node);
     errdefer mod.destroyDecl(new_decl_index);
     const new_decl = mod.declPtr(new_decl_index);
-    new_decl.name = options.name;
-
-    new_decl.src_line = sema.owner_decl.src_line;
-    new_decl.ty = Type.fromInterned(ptr_info.child);
-    new_decl.val = Value.fromInterned(
-        if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Fn)
-            try ip.getExternFunc(sema.gpa, .{
-                .ty = ptr_info.child,
-                .decl = new_decl_index,
-                .lib_name = options.library_name,
-            })
-        else
-            try mod.intern(.{ .variable = .{
-                .ty = ptr_info.child,
-                .init = .none,
-                .decl = new_decl_index,
-                .lib_name = options.library_name,
-                .is_extern = true,
-                .is_const = ptr_info.flags.is_const,
-                .is_threadlocal = options.is_thread_local,
-                .is_weak_linkage = options.linkage == .Weak,
-            } }),
-    );
-    new_decl.alignment = .none;
-    new_decl.@"linksection" = .none;
-    new_decl.has_tv = true;
+    try mod.initNewAnonDecl(new_decl_index, sema.owner_decl.src_line, .{
+        .ty = Type.fromInterned(ptr_info.child),
+        .val = Value.fromInterned(
+            if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Fn)
+                try ip.getExternFunc(sema.gpa, .{
+                    .ty = ptr_info.child,
+                    .decl = new_decl_index,
+                    .lib_name = options.library_name,
+                })
+            else
+                try mod.intern(.{ .variable = .{
+                    .ty = ptr_info.child,
+                    .init = .none,
+                    .decl = new_decl_index,
+                    .lib_name = options.library_name,
+                    .is_extern = true,
+                    .is_const = ptr_info.flags.is_const,
+                    .is_threadlocal = options.is_thread_local,
+                    .is_weak_linkage = options.linkage == .Weak,
+                } }),
+        ),
+    }, options.name);
     new_decl.owns_tv = true;
-    new_decl.analysis = .complete;
-
-    try sema.ensureDeclAnalyzed(new_decl_index);
+    // Note that this will queue the anon decl for codegen, so that the backend can
+    // correctly handle the extern, including duplicate detection.
+    try mod.finalizeAnonDecl(new_decl_index);
 
     return Air.internedToRef((try mod.getCoerced(Value.fromInterned((try mod.intern(.{ .ptr = .{
         .ty = switch (ip.indexToKey(ty.toIntern())) {
test/behavior/extern.zig
@@ -27,3 +27,21 @@ test "function extern symbol" {
 export fn a_mystery_function() i32 {
     return 4567;
 }
+
+test "function extern symbol matches extern decl" {
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+
+    const S = struct {
+        extern fn another_mystery_function() u32;
+        const same_thing = @extern(*const fn () callconv(.C) u32, .{ .name = "another_mystery_function" });
+    };
+    try expect(S.another_mystery_function() == 12345);
+    try expect(S.same_thing() == 12345);
+}
+
+export fn another_mystery_function() u32 {
+    return 12345;
+}