diff --git a/src/Module.zig b/src/Module.zig index 4fd3ee5c01..76997fe95b 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -510,6 +510,7 @@ pub const Decl = struct { gpa.destroy(func); } if (decl.getVariable()) |variable| { + variable.deinit(gpa); gpa.destroy(variable); } if (decl.value_arena) |arena_state| { @@ -694,6 +695,8 @@ pub const Decl = struct { return func; } + /// If the Decl has a value and it is an extern function, returns it, + /// otherwise null. pub fn getExternFn(decl: *const Decl) ?*ExternFn { if (!decl.owns_tv) return null; const extern_fn = (decl.val.castTag(.extern_fn) orelse return null).data; @@ -701,6 +704,8 @@ pub const Decl = struct { return extern_fn; } + /// If the Decl has a value and it is a variable, returns it, + /// otherwise null. pub fn getVariable(decl: *Decl) ?*Var { if (!decl.owns_tv) return null; const variable = (decl.val.castTag(.variable) orelse return null).data; @@ -1469,9 +1474,20 @@ pub const Var = struct { init: Value, owner_decl: *Decl, + /// Library name if specified. + /// For example `extern "c" var stderrp = ...` would have 'c' as library name. + /// Allocated with Module's allocator; outlives the ZIR code. + lib_name: ?[*:0]const u8, + is_extern: bool, is_mutable: bool, is_threadlocal: bool, + + pub fn deinit(variable: *Var, gpa: Allocator) void { + if (variable.lib_name) |lib_name| { + gpa.free(mem.sliceTo(lib_name, 0)); + } + } }; /// The container that structs, enums, unions, and opaques have. diff --git a/src/Sema.zig b/src/Sema.zig index 48d7545a75..0e43a9f522 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5430,6 +5430,69 @@ fn zirFunc( ); } +/// Given a library name, examines if the library name should end up in +/// `link.File.Options.system_libs` table (for example, libc is always +/// specified via dedicated flag `link.File.Options.link_libc` instead), +/// and puts it there if it doesn't exist. +/// It also dupes the library name which can then be saved as part of the +/// respective `Decl` (either `ExternFn` or `Var`). +/// The liveness of the duped library name is tied to liveness of `Module`. +/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). +fn handleExternLibName( + sema: *Sema, + block: *Block, + src_loc: LazySrcLoc, + lib_name: []const u8, +) CompileError![:0]u8 { + blk: { + const mod = sema.mod; + const target = mod.getTarget(); + log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); + if (target_util.is_libc_lib_name(target, lib_name)) { + if (!mod.comp.bin_file.options.link_libc) { + return sema.fail( + block, + src_loc, + "dependency on libc must be explicitly specified in the build command", + .{}, + ); + } + mod.comp.bin_file.options.link_libc = true; + break :blk; + } + if (target_util.is_libcpp_lib_name(target, lib_name)) { + if (!mod.comp.bin_file.options.link_libcpp) { + return sema.fail( + block, + src_loc, + "dependency on libc++ must be explicitly specified in the build command", + .{}, + ); + } + mod.comp.bin_file.options.link_libcpp = true; + break :blk; + } + if (mem.eql(u8, lib_name, "unwind")) { + mod.comp.bin_file.options.link_libunwind = true; + break :blk; + } + if (!target.isWasm() and !mod.comp.bin_file.options.pic) { + return sema.fail( + block, + src_loc, + "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", + .{ lib_name, lib_name }, + ); + } + mod.comp.stage1AddLinkLib(lib_name) catch |err| { + return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ + lib_name, @errorName(err), + }); + }; + } + return sema.gpa.dupeZ(u8, lib_name); +} + fn funcCommon( sema: *Sema, block: *Block, @@ -5567,65 +5630,21 @@ fn funcCommon( }); }; - if (opt_lib_name) |lib_name| blk: { - const lib_name_src: LazySrcLoc = .{ .node_offset_lib_name = src_node_offset }; - log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); - if (target_util.is_libc_lib_name(target, lib_name)) { - if (!mod.comp.bin_file.options.link_libc) { - return sema.fail( - block, - lib_name_src, - "dependency on libc must be explicitly specified in the build command", - .{}, - ); - } - mod.comp.bin_file.options.link_libc = true; - break :blk; - } - if (target_util.is_libcpp_lib_name(target, lib_name)) { - if (!mod.comp.bin_file.options.link_libcpp) { - return sema.fail( - block, - lib_name_src, - "dependency on libc++ must be explicitly specified in the build command", - .{}, - ); - } - mod.comp.bin_file.options.link_libcpp = true; - break :blk; - } - if (mem.eql(u8, lib_name, "unwind")) { - mod.comp.bin_file.options.link_libunwind = true; - break :blk; - } - if (!target.isWasm() and !mod.comp.bin_file.options.pic) { - return sema.fail( - block, - lib_name_src, - "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", - .{ lib_name, lib_name }, - ); - } - mod.comp.stage1AddLinkLib(lib_name) catch |err| { - return sema.fail(block, lib_name_src, "unable to add link lib '{s}': {s}", .{ - lib_name, @errorName(err), - }); - }; - } - if (is_extern) { const new_extern_fn = try sema.gpa.create(Module.ExternFn); errdefer sema.gpa.destroy(new_extern_fn); - const lib_name: ?[*:0]const u8 = if (opt_lib_name) |lib_name| blk: { - break :blk try sema.gpa.dupeZ(u8, lib_name); - } else null; - new_extern_fn.* = Module.ExternFn{ .owner_decl = sema.owner_decl, - .lib_name = lib_name, + .lib_name = null, }; + if (opt_lib_name) |lib_name| { + new_extern_fn.lib_name = try sema.handleExternLibName(block, .{ + .node_offset_lib_name = src_node_offset, + }, lib_name); + } + const extern_fn_payload = try sema.arena.create(Value.Payload.ExternFn); extern_fn_payload.* = .{ .base = .{ .tag = .extern_fn }, @@ -12456,13 +12475,8 @@ fn zirVarExtended( try sema.validateVarType(block, mut_src, var_ty, small.is_extern); - if (lib_name != null) { - // Look at the sema code for functions which has this logic, it just needs to - // be extracted and shared by both var and func - return sema.fail(block, src, "TODO: handle var with lib_name in Sema", .{}); - } - const new_var = try sema.gpa.create(Module.Var); + errdefer sema.gpa.destroy(new_var); log.debug("created variable {*} owner_decl: {*} ({s})", .{ new_var, sema.owner_decl, sema.owner_decl.name, @@ -12474,7 +12488,13 @@ fn zirVarExtended( .is_extern = small.is_extern, .is_mutable = true, // TODO get rid of this unused field .is_threadlocal = small.is_threadlocal, + .lib_name = null, }; + + if (lib_name) |lname| { + new_var.lib_name = try sema.handleExternLibName(block, ty_src, lname); + } + const result = try sema.addConstant( var_ty, try Value.Tag.variable.create(sema.arena, new_var),