From 7a27f0d80c5ce9d596b2b43699e131ab387e1317 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 4 May 2021 14:40:59 -0700 Subject: [PATCH] Sema: restore the extern lib name functionality --- BRANCH_TODO | 128 ------------------------------------------------- src/Module.zig | 36 ++++++++++++++ src/Sema.zig | 59 +++++++++++++++++++++-- 3 files changed, 90 insertions(+), 133 deletions(-) diff --git a/BRANCH_TODO b/BRANCH_TODO index 7b9b1c783d..4e52612ed9 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -55,132 +55,4 @@ natural alignment for fields and do not have any comptime fields. this will save 16 bytes per struct field in the compilation. -pub fn analyzeNamespace( - mod: *Module, - namespace: *Scope.Namespace, - decls: []const ast.Node.Index, -) InnerError!void { - for (decls) |decl_node| switch (node_tags[decl_node]) { - .@"comptime" => { - const name_index = mod.getNextAnonNameIndex(); - const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index}); - defer mod.gpa.free(name); - - const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); - - const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash); - namespace.decls.putAssumeCapacity(new_decl, {}); - mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); - }, - - // Container fields are handled in AstGen. - .container_field_init, - .container_field_align, - .container_field, - => continue, - - .test_decl => { - if (mod.comp.bin_file.options.is_test) { - log.err("TODO: analyze test decl", .{}); - } - }, - .@"usingnamespace" => { - const name_index = mod.getNextAnonNameIndex(); - const name = try std.fmt.allocPrint(mod.gpa, "__usingnamespace_{d}", .{name_index}); - defer mod.gpa.free(name); - - const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); - - const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash); - namespace.decls.putAssumeCapacity(new_decl, {}); - - mod.ensureDeclAnalyzed(new_decl) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => continue, - }; - }, - else => unreachable, - }; -} - -/// Trailing: -/// 0. `EmitH` if `module.emit_h != null`. -/// 1. A per-Decl link object. Represents the position of the code in the output file. -/// This is populated regardless of semantic analysis and code generation. -/// Depending on the target, will be one of: -/// * Elf.TextBlock -/// * Coff.TextBlock -/// * MachO.TextBlock -/// * C.DeclBlock -/// * Wasm.DeclBlock -/// * void -/// 2. If it is a function, a per-Decl link function object. Represents the -/// function in the linked output file, if the `Decl` is a function. -/// This is stored here and not in `Fn` because `Decl` survives across updates but -/// `Fn` does not. Depending on the target, will be one of: -/// * Elf.SrcFn -/// * Coff.SrcFn -/// * MachO.SrcFn -/// * C.FnBlock -/// * Wasm.FnData -/// * SpirV.FnData - - - extra_index += @boolToInt(has_align); - extra_index += @boolToInt(has_section); - - /// Contains un-analyzed ZIR instructions generated from Zig source AST. - /// Even after we finish analysis, the ZIR is kept in memory, so that - /// comptime and inline function calls can happen. - /// Parameter names are stored here so that they may be referenced for debug info, - /// without having source code bytes loaded into memory. - /// The number of parameters is determined by referring to the type. - /// The first N elements of `extra` are indexes into `string_bytes` to - /// a null-terminated string. - /// This memory is managed with gpa, must be freed when the function is freed. - zir: Zir, - - - if (fn_proto.lib_name) |lib_name_token| blk: { - log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str}); - mod.comp.stage1AddLinkLib(lib_name_str) catch |err| { - return mod.failTok( - &fn_type_scope.base, - lib_name_token, - "unable to add link lib '{s}': {s}", - .{ lib_name_str, @errorName(err) }, - ); - }; - const target = mod.comp.getTarget(); - if (target_util.is_libc_lib_name(target, lib_name_str)) { - if (!mod.comp.bin_file.options.link_libc) { - return mod.failTok( - &fn_type_scope.base, - lib_name_token, - "dependency on libc must be explicitly specified in the build command", - .{}, - ); - } - break :blk; - } - if (target_util.is_libcpp_lib_name(target, lib_name_str)) { - if (!mod.comp.bin_file.options.link_libcpp) { - return mod.failTok( - &fn_type_scope.base, - lib_name_token, - "dependency on libc++ must be explicitly specified in the build command", - .{}, - ); - } - break :blk; - } - if (!target.isWasm() and !mod.comp.bin_file.options.pic) { - return mod.failTok( - &fn_type_scope.base, - lib_name_token, - "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", - .{ lib_name_str, lib_name_str }, - ); - } - } diff --git a/src/Module.zig b/src/Module.zig index 3f88efd717..f32909cabe 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1603,6 +1603,34 @@ pub const SrcLoc = struct { const token_starts = tree.tokens.items(.start); return token_starts[tok_index]; }, + + .node_offset_lib_name => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_datas = tree.nodes.items(.data); + const node_tags = tree.nodes.items(.tag); + const parent_node = src_loc.declRelativeToNodeIndex(node_off); + var params: [1]ast.Node.Index = undefined; + const full = switch (node_tags[parent_node]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, parent_node), + .fn_proto_multi => tree.fnProtoMulti(parent_node), + .fn_proto_one => tree.fnProtoOne(¶ms, parent_node), + .fn_proto => tree.fnProto(parent_node), + .fn_decl => blk: { + const fn_proto = node_datas[parent_node].lhs; + break :blk switch (node_tags[fn_proto]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, fn_proto), + .fn_proto_multi => tree.fnProtoMulti(fn_proto), + .fn_proto_one => tree.fnProtoOne(¶ms, fn_proto), + .fn_proto => tree.fnProto(fn_proto), + else => unreachable, + }; + }, + else => unreachable, + }; + const tok_index = full.lib_name.?; + const token_starts = tree.tokens.items(.start); + return token_starts[tok_index]; + }, } } }; @@ -1768,6 +1796,12 @@ pub const LazySrcLoc = union(enum) { /// to the type expression. /// The Decl is determined contextually. node_offset_anyframe_type: i32, + /// The source location points to the string literal of `extern "foo"`, found + /// by taking this AST node index offset from the containing + /// Decl AST node, which points to a function prototype or variable declaration + /// expression AST node. Next, navigate to the string literal of the `extern "foo"`. + /// The Decl is determined contextually. + node_offset_lib_name: i32, /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope. pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc { @@ -1808,6 +1842,7 @@ pub const LazySrcLoc = union(enum) { .node_offset_fn_type_cc, .node_offset_fn_type_ret_ty, .node_offset_anyframe_type, + .node_offset_lib_name, => .{ .file_scope = scope.getFileScope(), .parent_decl_node = scope.srcDecl().?.src_node, @@ -1855,6 +1890,7 @@ pub const LazySrcLoc = union(enum) { .node_offset_fn_type_cc, .node_offset_fn_type_ret_ty, .node_offset_anyframe_type, + .node_offset_lib_name, => .{ .file_scope = decl.getFileScope(), .parent_decl_node = decl.src_node, diff --git a/src/Sema.zig b/src/Sema.zig index 46b230f174..5b2dc339f6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -63,6 +63,7 @@ const InnerError = Module.InnerError; const Decl = Module.Decl; const LazySrcLoc = Module.LazySrcLoc; const RangeSet = @import("RangeSet.zig"); +const target_util = @import("target.zig"); pub fn analyzeFnBody( sema: *Sema, @@ -2739,6 +2740,7 @@ fn zirFunc( false, inferred_error_set, src_locs, + null, ); } @@ -2754,11 +2756,14 @@ fn funcCommon( var_args: bool, inferred_error_set: bool, src_locs: Zir.Inst.Func.SrcLocs, + opt_lib_name: ?[]const u8, ) InnerError!*Inst { const src: LazySrcLoc = .{ .node_offset = src_node_offset }; const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; const return_type = try sema.resolveType(block, ret_ty_src, zir_return_type); + const mod = sema.mod; + const fn_ty: Type = fn_ty: { // Hot path for some common function types. if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value) { @@ -2789,7 +2794,7 @@ fn funcCommon( } if (align_val.tag() != .null_value) { - return sema.mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{}); + return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{}); } break :fn_ty try Type.Tag.function.create(sema.arena, .{ @@ -2801,7 +2806,48 @@ fn funcCommon( }; if (body_inst == 0) { - return sema.mod.constType(sema.arena, src, fn_ty); + return mod.constType(sema.arena, src, fn_ty); + } + + 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}); + mod.comp.stage1AddLinkLib(lib_name) catch |err| { + return mod.fail(&block.base, lib_name_src, "unable to add link lib '{s}': {s}", .{ + lib_name, @errorName(err), + }); + }; + const target = mod.getTarget(); + if (target_util.is_libc_lib_name(target, lib_name)) { + if (!mod.comp.bin_file.options.link_libc) { + return mod.fail( + &block.base, + lib_name_src, + "dependency on libc must be explicitly specified in the build command", + .{}, + ); + } + break :blk; + } + if (target_util.is_libcpp_lib_name(target, lib_name)) { + if (!mod.comp.bin_file.options.link_libcpp) { + return mod.fail( + &block.base, + lib_name_src, + "dependency on libc++ must be explicitly specified in the build command", + .{}, + ); + } + break :blk; + } + if (!target.isWasm() and !mod.comp.bin_file.options.pic) { + return mod.fail( + &block.base, + lib_name_src, + "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", + .{ lib_name, lib_name }, + ); + } } const is_inline = fn_ty.fnCallingConvention() == .Inline; @@ -5599,11 +5645,13 @@ fn zirFuncExtended( const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small); var extra_index: usize = extra.end; - if (small.has_lib_name) { + + const lib_name: ?[]const u8 = if (small.has_lib_name) blk: { const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]); extra_index += 1; - return sema.mod.fail(&block.base, src, "TODO: implement Sema func lib name", .{}); - } + break :blk lib_name; + } else null; + const cc: std.builtin.CallingConvention = if (small.has_cc) blk: { const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; @@ -5640,6 +5688,7 @@ fn zirFuncExtended( small.is_var_args, small.is_inferred_error, src_locs, + lib_name, ); }