Sema: restore the extern lib name functionality

This commit is contained in:
Andrew Kelley 2021-05-04 14:40:59 -07:00
parent 1f0fd64302
commit 7a27f0d80c
3 changed files with 90 additions and 133 deletions

View File

@ -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 },
);
}
}

View File

@ -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,

View File

@ -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,
);
}