authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-25 15:19:12+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-26 19:12:48+02:00
log476efc5b701c2a33cfd09b09989ccf400641a2ac
tree4daca802ea94d920835577bf86c8638f4cb9df6e
parent77568d3009254ad747a8f4058c94b9e83b069ad2
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

remove `internal` and `link_once` global linkages

These have no clear use case, no coherent definition, and incomplete/broken compiler and linker support. If actually needed, they can be reintroduced if someone can present a reasonable use case along with a plan for how they should be implemented consistently in all backends and linkers.

18 files changed, 36 insertions(+), 147 deletions(-)

lib/compiler/aro/aro/TypeStore.zig+1-2
......@@ -1201,9 +1201,8 @@ pub const QualType = packed struct(u32) {
12011201 }
12021202
12031203 pub fn linkage(qt: QualType, comp: *const Compilation) std.builtin.GlobalLinkage {
1204 if (qt.hasAttribute(comp, .internal_linkage)) return .internal;
12051204 if (qt.hasAttribute(comp, .weak)) return .weak;
1206 if (qt.hasAttribute(comp, .selectany)) return .link_once;
1205 if (qt.hasAttribute(comp, .selectany)) return .weak;
12071206 return .strong;
12081207 }
12091208
lib/compiler_rt.zig+7-19
......@@ -23,27 +23,15 @@ else
2323 unreachable;
2424
2525pub inline fn symbol(comptime func: *const anyopaque, comptime name: []const u8) void {
26 @export(func, .{ .name = name, .linkage = linkage, .visibility = visibility });
26 // The function shouldn't be exported at all when testing compiler_rt.
27 // Instead, we just need to reference the function so that it's compiled.
28 if (!builtin.is_test) @export(func, .{
29 .name = name,
30 .linkage = if (ofmt_c) .strong else .weak,
31 .visibility = .hidden,
32 });
2733}
2834
29/// For now, we prefer weak linkage because some of the routines we implement here may also be
30/// provided by system/dynamic libc. Eventually we should be more disciplined about this on a
31/// per-symbol, per-target basis: https://github.com/ziglang/zig/issues/11883
32pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test)
33 .internal
34else if (ofmt_c)
35 .strong
36else
37 .weak;
38
39/// Determines the symbol's visibility to other objects.
40/// For WebAssembly this allows the symbol to be resolved to other modules, but will not
41/// export it to the host runtime.
42pub const visibility: std.builtin.SymbolVisibility = if (linkage == .internal or builtin.link_mode == .dynamic)
43 .default
44else
45 .hidden;
46
4735pub const test_safety = switch (builtin.zig_backend) {
4836 .stage2_aarch64 => false,
4937 else => builtin.is_test,
lib/std/lang.zig+1-3
......@@ -15,11 +15,9 @@ pub const StackTrace = struct {
1515
1616/// This data structure is used by the Zig language code generation and
1717/// therefore must be kept in sync with the compiler implementation.
18pub const GlobalLinkage = enum(u2) {
19 internal,
18pub const GlobalLinkage = enum(u1) {
2019 strong,
2120 weak,
22 link_once,
2321};
2422
2523/// This data structure is used by the Zig language code generation and
src/InternPool.zig+1-1
......@@ -5433,7 +5433,7 @@ pub const Tag = enum(u8) {
54335433 relocation: std.lang.ExternOptions.Relocation,
54345434 source: Source,
54355435 decoration_type: DecorationType,
5436 _: u23 = 0,
5436 _: u24 = 0,
54375437
54385438 pub const Source = enum(u1) { builtin, syntax };
54395439 pub const DecorationType = enum(u2) { none, location, descriptor, flat };
src/Sema.zig-13
......@@ -23107,12 +23107,6 @@ fn resolveExportOptions(
2310723107 return sema.fail(block, name_src, "exported symbol name cannot be empty", .{});
2310823108 }
2310923109
23110 if (visibility != .default and linkage == .internal) {
23111 return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{
23112 name, @tagName(visibility),
23113 });
23114 }
23115
2311623110 return .{
2311723111 .name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls),
2311823112 .linkage = linkage,
......@@ -25290,13 +25284,6 @@ fn zirBuiltinExtern(
2529025284 }
2529125285
2529225286 const options = try sema.resolveExternOptions(block, options_src, extra.rhs);
25293 switch (options.linkage) {
25294 .internal => if (options.visibility != .default) {
25295 return sema.fail(block, options_src, "internal symbol cannot have non-default visibility", .{});
25296 },
25297 .strong, .weak => {},
25298 .link_once => return sema.fail(block, options_src, "external symbol cannot have link once linkage", .{}),
25299 }
2530025287 switch (options.relocation) {
2530125288 .any => {},
2530225289 .pcrel => if (options.visibility == .default) return sema.fail(block, options_src, "cannot require a pc-relative relocation to a symbol with default visibility", .{}),
src/Zcu/PerThread.zig+1-1
......@@ -3657,7 +3657,7 @@ pub fn processExports(pt: Zcu.PerThread) (Allocator.Error || Io.Cancelable)!void
36573657 const exp = export_index.ptr(zcu);
36583658 switch (exp.opts.linkage) {
36593659 .strong => {},
3660 .weak, .internal, .link_once => continue,
3660 .weak => continue,
36613661 }
36623662 const gop = exports_by_name.getOrPutAssumeCapacity(exp.opts.name);
36633663 if (gop.found_existing) {
src/codegen.zig+24-40
......@@ -830,53 +830,37 @@ pub fn genNavRef(
830830 log.debug("genNavRef({f})", .{nav.fqn.fmt(ip)});
831831
832832 const is_threadlocal = nav.resolved.?.@"threadlocal" and zcu.comp.config.any_non_single_threaded;
833 const lib_name, const linkage = if (nav.getExtern(ip)) |e|
834 .{ e.lib_name, e.linkage }
835 else
836 .{ .none, .internal };
837833 if (lf.cast(.elf)) |elf_file| {
838834 const zo = elf_file.zigObjectPtr().?;
839 switch (linkage) {
840 .internal => {
841 const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
842 if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
843 return @fromBackingInt(@intCast(sym_index));
844 },
845 .strong, .weak => {
846 const sym_index = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip));
847 switch (linkage) {
848 .internal => unreachable,
849 .strong => {},
850 .weak => zo.symbol(sym_index).flags.weak = true,
851 .link_once => unreachable,
852 }
853 if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
854 return @fromBackingInt(@intCast(sym_index));
855 },
856 .link_once => unreachable,
835 if (nav.getExtern(ip)) |e| {
836 const sym_index = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip));
837 switch (e.linkage) {
838 .strong => {},
839 .weak => zo.symbol(sym_index).flags.weak = true,
840 }
841 if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
842 return @fromBackingInt(@intCast(sym_index));
843 } else {
844 const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
845 if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
846 return @fromBackingInt(@intCast(sym_index));
857847 }
858848 } else if (lf.cast(.elf2)) |elf| {
859849 return elf.navSymbol(nav_index);
860850 } else if (lf.cast(.macho)) |macho_file| {
861851 const zo = macho_file.getZigObject().?;
862 switch (linkage) {
863 .internal => {
864 const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
865 if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
866 return @fromBackingInt(@intCast(sym_index));
867 },
868 .strong, .weak => {
869 const sym_index = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip));
870 switch (linkage) {
871 .internal => unreachable,
872 .strong => {},
873 .weak => zo.symbols.items[sym_index].flags.weak = true,
874 .link_once => unreachable,
875 }
876 if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
877 return @fromBackingInt(@intCast(sym_index));
878 },
879 .link_once => unreachable,
852 if (nav.getExtern(ip)) |e| {
853 const sym_index = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip));
854 switch (e.linkage) {
855 .strong => {},
856 .weak => zo.symbols.items[sym_index].flags.weak = true,
857 }
858 if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
859 return @fromBackingInt(@intCast(sym_index));
860 } else {
861 const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
862 if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
863 return @fromBackingInt(@intCast(sym_index));
880864 }
881865 } else if (lf.cast(.coff2)) |coff| {
882866 return @fromBackingInt(@intCast(@backingInt(try coff.navSymbol(zcu, nav_index))));
src/codegen/c.zig-2
......@@ -2515,10 +2515,8 @@ pub fn genDeclFwd(dg: *DeclGen, w: *Writer) Error!void {
25152515 },
25162516 else => {
25172517 switch (@"extern".linkage) {
2518 .internal => try w.writeAll("static "),
25192518 .strong => try w.print("zig_extern zig_visibility({t}) ", .{@"extern".visibility}),
25202519 .weak => try w.print("zig_extern zig_weak_linkage zig_visibility({t}) ", .{@"extern".visibility}),
2521 .link_once => return dg.fail("TODO: CBE: implement linkonce linkage?", .{}),
25222520 }
25232521 if (nav.resolved.?.@"threadlocal" and !dg.mod.single_threaded) {
25242522 try w.writeAll("zig_threadlocal ");
src/codegen/llvm.zig-4
......@@ -1077,10 +1077,8 @@ pub const Object = struct {
10771077 false => .default,
10781078 };
10791079 llvm_global.ptr(&o.builder).linkage = switch (@"extern".linkage) {
1080 .internal => if (o.builder.strip and !workaroundPrivateSymbolBugs(zcu.getTarget(), &resolved)) .private else .internal,
10811080 .strong => .external,
10821081 .weak => .extern_weak,
1083 .link_once => unreachable,
10841082 };
10851083 llvm_global.ptr(&o.builder).visibility = .fromSymbolVisibility(@"extern".visibility);
10861084 } else {
......@@ -1304,10 +1302,8 @@ pub const Object = struct {
13041302 if (comp.config.dll_export_fns and exp.opts.visibility != .hidden)
13051303 alias_global.setDllStorageClass(.dllexport, &o.builder);
13061304 alias_global.setLinkage(switch (exp.opts.linkage) {
1307 .internal => if (o.builder.strip) .private else .internal, // we still did useful work in replacing an existing symbol if there was one
13081305 .strong => .external,
13091306 .weak => .weak_odr,
1310 .link_once => .linkonce_odr,
13111307 }, &o.builder);
13121308 alias_global.setVisibility(switch (exp.opts.visibility) {
13131309 .default => .default,
src/link/Elf/ZigObject.zig-11
......@@ -1910,19 +1910,8 @@ pub fn updateExports(
19101910 }
19111911 }
19121912 const stb_bits: u8 = switch (exp.opts.linkage) {
1913 .internal => elf.STB_LOCAL,
19141913 .strong => elf.STB_GLOBAL,
19151914 .weak => elf.STB_WEAK,
1916 .link_once => {
1917 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
1918 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
1919 gpa,
1920 exp.src,
1921 "Unimplemented: GlobalLinkage.LinkOnce",
1922 .{},
1923 ));
1924 continue;
1925 },
19261915 };
19271916 const stt_bits: u8 = @as(u4, @truncate(esym.st_info));
19281917 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
src/link/Elf2.zig-4
......@@ -4245,8 +4245,6 @@ fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
42454245 .bind = switch (opts.linkage) {
42464246 .strong => .strong,
42474247 .weak => .weak,
4248 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
4249 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
42504248 },
42514249 .visibility = switch (opts.visibility) {
42524250 .default => .DEFAULT,
......@@ -12205,8 +12203,6 @@ fn updateExportInner(
1220512203 .bind = switch (@"export".opts.linkage) {
1220612204 .strong => .strong,
1220712205 .weak => .weak,
12208 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
12209 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
1221012206 },
1221112207 .visibility = switch (@"export".opts.visibility) {
1221212208 .default => .DEFAULT,
src/link/MachO/ZigObject.zig-15
......@@ -1301,15 +1301,6 @@ pub fn updateExports(
13011301 continue;
13021302 }
13031303 }
1304 if (exp.opts.linkage == .link_once) {
1305 try zcu.failed_exports.putNoClobber(zcu.gpa, export_index, try Zcu.ErrorMsg.create(
1306 gpa,
1307 exp.src,
1308 "Unimplemented: GlobalLinkage.link_once",
1309 .{},
1310 ));
1311 continue;
1312 }
13131304
13141305 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
13151306 const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null);
......@@ -1326,11 +1317,6 @@ pub fn updateExports(
13261317 global_sym.atom_ref = .{ .index = atom_index, .file = self.index };
13271318
13281319 switch (exp.opts.linkage) {
1329 .internal => {
1330 // Symbol should be hidden, or in MachO lingo, private extern.
1331 global_nlist.n_type.bits.pext = true;
1332 global_sym.visibility = .hidden;
1333 },
13341320 .strong => {
13351321 global_sym.visibility = .global;
13361322 },
......@@ -1341,7 +1327,6 @@ pub fn updateExports(
13411327 global_sym.visibility = .global;
13421328 global_sym.flags.weak = true;
13431329 },
1344 else => unreachable,
13451330 }
13461331 }
13471332}
src/link/Wasm.zig-6
......@@ -972,10 +972,8 @@ pub const ZcuFunc = union {
972972 const name_string = wasm.getExistingString(name_slice).?;
973973 return .{
974974 .binding = switch (ext.linkage) {
975 .internal => .local,
976975 .strong => .strong,
977976 .weak => .weak,
978 .link_once => @panic("TODO: COMDAT"),
979977 },
980978 .visibility_hidden = switch (ext.visibility) {
981979 .default => false,
......@@ -1952,10 +1950,8 @@ pub const ObjectDataImport = extern struct {
19521950 const name_string = wasm.getExistingString(name_slice).?;
19531951 return .{
19541952 .binding = switch (ext.linkage) {
1955 .internal => .local,
19561953 .strong => .strong,
19571954 .weak => .weak,
1958 .link_once => @panic("TODO: COMDAT"),
19591955 },
19601956 .visibility_hidden = switch (ext.visibility) {
19611957 .default => false,
......@@ -2591,10 +2587,8 @@ pub const ZcuImportIndex = enum(u32) {
25912587 const name_string = wasm.getExistingString(name_slice).?;
25922588 return .{
25932589 .binding = switch (ext.linkage) {
2594 .internal => .local,
25952590 .strong => .strong,
25962591 .weak => .weak,
2597 .link_once => @panic("TODO: COMDAT"),
25982592 },
25992593 .visibility_hidden = switch (ext.visibility) {
26002594 .default => false,
src/link/Wasm/Flush.zig-6
......@@ -319,10 +319,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
319319 .function_index = function_index,
320320 .flags = .{
321321 .binding = switch (opts.linkage) {
322 .internal => .local,
323322 .strong => .strong,
324323 .weak => .weak,
325 .link_once => @panic("TODO: COMDAT"),
326324 },
327325 .visibility_hidden = is_hidden,
328326 .exported = !is_hidden,
......@@ -346,10 +344,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
346344 .resolution = .fromIpNav(wasm, nav_export.nav_index),
347345 .flags = if (is_obj) .{
348346 .binding = switch (opts.linkage) {
349 .internal => .local,
350347 .strong => .strong,
351348 .weak => .weak,
352 .link_once => @panic("TODO: COMDAT"),
353349 },
354350 .visibility_hidden = !explicit and switch (opts.visibility) {
355351 .default => false,
......@@ -383,10 +379,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
383379 .resolution = .fromIpIndex(wasm, uav_export.uav_index),
384380 .flags = if (is_obj) .{
385381 .binding = switch (opts.linkage) {
386 .internal => .local,
387382 .strong => .strong,
388383 .weak => .weak,
389 .link_once => @panic("TODO: COMDAT"),
390384 },
391385 .visibility_hidden = !explicit and switch (opts.visibility) {
392386 .default => false,
test/behavior/export_builtin.zig+1-13
......@@ -16,18 +16,6 @@ test "exporting enum value" {
1616 try expect(S.e == .two);
1717}
1818
19test "exporting with internal linkage" {
20 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
21
22 const S = struct {
23 fn foo() callconv(.c) void {}
24 comptime {
25 @export(&foo, .{ .name = "exporting_with_internal_linkage_foo", .linkage = .internal });
26 }
27 };
28 S.foo();
29}
30
3119test "exporting using namespace access" {
3220 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3321 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -37,7 +25,7 @@ test "exporting using namespace access" {
3725 const x: u32 = 5;
3826 };
3927 comptime {
40 @export(&Inner.x, .{ .name = "foo", .linkage = .internal });
28 @export(&Inner.x, .{ .name = "foo", .linkage = .strong });
4129 }
4230 };
4331
test/standalone/global_linkage/obj1.zig-2
......@@ -1,7 +1,5 @@
1var internal_integer: usize = 1;
21var obj1_integer: usize = 421;
32
43comptime {
5 @export(&internal_integer, .{ .name = "internal_integer", .linkage = .internal });
64 @export(&obj1_integer, .{ .name = "obj1_integer", .linkage = .strong });
75}
test/standalone/global_linkage/obj2.zig-2
......@@ -1,7 +1,5 @@
1var internal_integer: usize = 2;
21var obj2_integer: usize = 422;
32
43comptime {
5 @export(&internal_integer, .{ .name = "internal_integer", .linkage = .internal });
64 @export(&obj2_integer, .{ .name = "obj2_integer", .linkage = .strong });
75}
test/tests.zig-3
......@@ -1294,7 +1294,6 @@ const module_test_targets = blk: {
12941294 .os_tag = .linux,
12951295 },
12961296 .new_linker = true,
1297 .skip_modules = &.{ "compiler-rt", "behavior" }, // '@export' with '.internal' linkage
12981297 },
12991298 .{
13001299 .target = .{
......@@ -1304,7 +1303,6 @@ const module_test_targets = blk: {
13041303 },
13051304 .link_libc = true,
13061305 .new_linker = true,
1307 .skip_modules = &.{ "compiler-rt", "behavior" }, // '@export' with '.internal' linkage
13081306 },
13091307 .{
13101308 .target = .{
......@@ -1314,7 +1312,6 @@ const module_test_targets = blk: {
13141312 },
13151313 .link_libc = true,
13161314 .new_linker = true,
1317 .skip_modules = &.{ "compiler-rt", "behavior" }, // '@export' with '.internal' linkage
13181315 },
13191316
13201317 // Darwin Targets