Commit 7a27f0d80c

Andrew Kelley <andrew@ziglang.org>
2021-05-04 23:40:59
Sema: restore the extern lib name functionality
1 parent 1f0fd64
Changed files (3)
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(&params, parent_node),
+                    .fn_proto_multi => tree.fnProtoMulti(parent_node),
+                    .fn_proto_one => tree.fnProtoOne(&params, 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(&params, fn_proto),
+                            .fn_proto_multi => tree.fnProtoMulti(fn_proto),
+                            .fn_proto_one => tree.fnProtoOne(&params, 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,
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,
     );
 }
 
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 },
-            );
-        }
-    }