authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-06 09:14:15+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-06 09:24:13+01:00
logdb9500a31401c65327a4fd556f50d74ce75fb858
treef490658644c9b6741f6375e93189568104e25745
parent556f0ce5bfb962764bb68d9d9c16765cf9e1b9ba

stage2: handle extern lib name annotation for vars

For example, a situation like this is allowed ```zig extern "c" var stderrp: c_int; ``` In this case, `Module.Var` wrapping `stderrp` will have `lib_name` populated with the library name where this import is expected.

2 files changed, 93 insertions(+), 57 deletions(-)

src/Module.zig+16
......@@ -510,6 +510,7 @@ pub const Decl = struct {
510510 gpa.destroy(func);
511511 }
512512 if (decl.getVariable()) |variable| {
513 variable.deinit(gpa);
513514 gpa.destroy(variable);
514515 }
515516 if (decl.value_arena) |arena_state| {
......@@ -694,6 +695,8 @@ pub const Decl = struct {
694695 return func;
695696 }
696697
698 /// If the Decl has a value and it is an extern function, returns it,
699 /// otherwise null.
697700 pub fn getExternFn(decl: *const Decl) ?*ExternFn {
698701 if (!decl.owns_tv) return null;
699702 const extern_fn = (decl.val.castTag(.extern_fn) orelse return null).data;
......@@ -701,6 +704,8 @@ pub const Decl = struct {
701704 return extern_fn;
702705 }
703706
707 /// If the Decl has a value and it is a variable, returns it,
708 /// otherwise null.
704709 pub fn getVariable(decl: *Decl) ?*Var {
705710 if (!decl.owns_tv) return null;
706711 const variable = (decl.val.castTag(.variable) orelse return null).data;
......@@ -1469,9 +1474,20 @@ pub const Var = struct {
14691474 init: Value,
14701475 owner_decl: *Decl,
14711476
1477 /// Library name if specified.
1478 /// For example `extern "c" var stderrp = ...` would have 'c' as library name.
1479 /// Allocated with Module's allocator; outlives the ZIR code.
1480 lib_name: ?[*:0]const u8,
1481
14721482 is_extern: bool,
14731483 is_mutable: bool,
14741484 is_threadlocal: bool,
1485
1486 pub fn deinit(variable: *Var, gpa: Allocator) void {
1487 if (variable.lib_name) |lib_name| {
1488 gpa.free(mem.sliceTo(lib_name, 0));
1489 }
1490 }
14751491};
14761492
14771493/// The container that structs, enums, unions, and opaques have.
src/Sema.zig+77-57
......@@ -5430,6 +5430,69 @@ fn zirFunc(
54305430 );
54315431}
54325432
5433/// Given a library name, examines if the library name should end up in
5434/// `link.File.Options.system_libs` table (for example, libc is always
5435/// specified via dedicated flag `link.File.Options.link_libc` instead),
5436/// and puts it there if it doesn't exist.
5437/// It also dupes the library name which can then be saved as part of the
5438/// respective `Decl` (either `ExternFn` or `Var`).
5439/// The liveness of the duped library name is tied to liveness of `Module`.
5440/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`).
5441fn handleExternLibName(
5442 sema: *Sema,
5443 block: *Block,
5444 src_loc: LazySrcLoc,
5445 lib_name: []const u8,
5446) CompileError![:0]u8 {
5447 blk: {
5448 const mod = sema.mod;
5449 const target = mod.getTarget();
5450 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
5451 if (target_util.is_libc_lib_name(target, lib_name)) {
5452 if (!mod.comp.bin_file.options.link_libc) {
5453 return sema.fail(
5454 block,
5455 src_loc,
5456 "dependency on libc must be explicitly specified in the build command",
5457 .{},
5458 );
5459 }
5460 mod.comp.bin_file.options.link_libc = true;
5461 break :blk;
5462 }
5463 if (target_util.is_libcpp_lib_name(target, lib_name)) {
5464 if (!mod.comp.bin_file.options.link_libcpp) {
5465 return sema.fail(
5466 block,
5467 src_loc,
5468 "dependency on libc++ must be explicitly specified in the build command",
5469 .{},
5470 );
5471 }
5472 mod.comp.bin_file.options.link_libcpp = true;
5473 break :blk;
5474 }
5475 if (mem.eql(u8, lib_name, "unwind")) {
5476 mod.comp.bin_file.options.link_libunwind = true;
5477 break :blk;
5478 }
5479 if (!target.isWasm() and !mod.comp.bin_file.options.pic) {
5480 return sema.fail(
5481 block,
5482 src_loc,
5483 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
5484 .{ lib_name, lib_name },
5485 );
5486 }
5487 mod.comp.stage1AddLinkLib(lib_name) catch |err| {
5488 return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{
5489 lib_name, @errorName(err),
5490 });
5491 };
5492 }
5493 return sema.gpa.dupeZ(u8, lib_name);
5494}
5495
54335496fn funcCommon(
54345497 sema: *Sema,
54355498 block: *Block,
......@@ -5567,65 +5630,21 @@ fn funcCommon(
55675630 });
55685631 };
55695632
5570 if (opt_lib_name) |lib_name| blk: {
5571 const lib_name_src: LazySrcLoc = .{ .node_offset_lib_name = src_node_offset };
5572 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
5573 if (target_util.is_libc_lib_name(target, lib_name)) {
5574 if (!mod.comp.bin_file.options.link_libc) {
5575 return sema.fail(
5576 block,
5577 lib_name_src,
5578 "dependency on libc must be explicitly specified in the build command",
5579 .{},
5580 );
5581 }
5582 mod.comp.bin_file.options.link_libc = true;
5583 break :blk;
5584 }
5585 if (target_util.is_libcpp_lib_name(target, lib_name)) {
5586 if (!mod.comp.bin_file.options.link_libcpp) {
5587 return sema.fail(
5588 block,
5589 lib_name_src,
5590 "dependency on libc++ must be explicitly specified in the build command",
5591 .{},
5592 );
5593 }
5594 mod.comp.bin_file.options.link_libcpp = true;
5595 break :blk;
5596 }
5597 if (mem.eql(u8, lib_name, "unwind")) {
5598 mod.comp.bin_file.options.link_libunwind = true;
5599 break :blk;
5600 }
5601 if (!target.isWasm() and !mod.comp.bin_file.options.pic) {
5602 return sema.fail(
5603 block,
5604 lib_name_src,
5605 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
5606 .{ lib_name, lib_name },
5607 );
5608 }
5609 mod.comp.stage1AddLinkLib(lib_name) catch |err| {
5610 return sema.fail(block, lib_name_src, "unable to add link lib '{s}': {s}", .{
5611 lib_name, @errorName(err),
5612 });
5613 };
5614 }
5615
56165633 if (is_extern) {
56175634 const new_extern_fn = try sema.gpa.create(Module.ExternFn);
56185635 errdefer sema.gpa.destroy(new_extern_fn);
56195636
5620 const lib_name: ?[*:0]const u8 = if (opt_lib_name) |lib_name| blk: {
5621 break :blk try sema.gpa.dupeZ(u8, lib_name);
5622 } else null;
5623
56245637 new_extern_fn.* = Module.ExternFn{
56255638 .owner_decl = sema.owner_decl,
5626 .lib_name = lib_name,
5639 .lib_name = null,
56275640 };
56285641
5642 if (opt_lib_name) |lib_name| {
5643 new_extern_fn.lib_name = try sema.handleExternLibName(block, .{
5644 .node_offset_lib_name = src_node_offset,
5645 }, lib_name);
5646 }
5647
56295648 const extern_fn_payload = try sema.arena.create(Value.Payload.ExternFn);
56305649 extern_fn_payload.* = .{
56315650 .base = .{ .tag = .extern_fn },
......@@ -12456,13 +12475,8 @@ fn zirVarExtended(
1245612475
1245712476 try sema.validateVarType(block, mut_src, var_ty, small.is_extern);
1245812477
12459 if (lib_name != null) {
12460 // Look at the sema code for functions which has this logic, it just needs to
12461 // be extracted and shared by both var and func
12462 return sema.fail(block, src, "TODO: handle var with lib_name in Sema", .{});
12463 }
12464
1246512478 const new_var = try sema.gpa.create(Module.Var);
12479 errdefer sema.gpa.destroy(new_var);
1246612480
1246712481 log.debug("created variable {*} owner_decl: {*} ({s})", .{
1246812482 new_var, sema.owner_decl, sema.owner_decl.name,
......@@ -12474,7 +12488,13 @@ fn zirVarExtended(
1247412488 .is_extern = small.is_extern,
1247512489 .is_mutable = true, // TODO get rid of this unused field
1247612490 .is_threadlocal = small.is_threadlocal,
12491 .lib_name = null,
1247712492 };
12493
12494 if (lib_name) |lname| {
12495 new_var.lib_name = try sema.handleExternLibName(block, ty_src, lname);
12496 }
12497
1247812498 const result = try sema.addConstant(
1247912499 var_ty,
1248012500 try Value.Tag.variable.create(sema.arena, new_var),