diff --git a/src/AstGen.zig b/src/AstGen.zig index 036d628591..77846c4732 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1081,6 +1081,7 @@ fn fnProtoExpr( .is_var_args = is_var_args, .is_inferred_error = false, .is_test = false, + .is_extern = false, }); return rvalue(gz, scope, rl, result, fn_proto.ast.proto_node); } @@ -2807,6 +2808,7 @@ fn fnDecl( .is_var_args = is_var_args, .is_inferred_error = false, .is_test = false, + .is_extern = true, }); } else func: { if (is_var_args) { @@ -2883,6 +2885,7 @@ fn fnDecl( .is_var_args = is_var_args, .is_inferred_error = is_inferred_error, .is_test = false, + .is_extern = false, }); }; @@ -3223,6 +3226,7 @@ fn testDecl( .is_var_args = false, .is_inferred_error = true, .is_test = true, + .is_extern = false, }); _ = try decl_block.addBreak(.break_inline, block_inst, func_inst); @@ -7973,6 +7977,7 @@ const GenZir = struct { is_var_args: bool, is_inferred_error: bool, is_test: bool, + is_extern: bool, }) !Zir.Inst.Ref { assert(args.src_node != 0); assert(args.ret_ty != .none); @@ -8010,7 +8015,8 @@ const GenZir = struct { } if (args.cc != .none or args.lib_name != 0 or - args.is_var_args or args.is_test or args.align_inst != .none) + args.is_var_args or args.is_test or args.align_inst != .none or + args.is_extern) { try astgen.extra.ensureUnusedCapacity( gpa, @@ -8051,6 +8057,7 @@ const GenZir = struct { .has_cc = args.cc != .none, .has_align = args.align_inst != .none, .is_test = args.is_test, + .is_extern = args.is_extern, }), .operand = payload_index, } }, diff --git a/src/Module.zig b/src/Module.zig index d84b4e2f35..1b935f65a9 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -282,13 +282,10 @@ pub const Decl = struct { pub fn destroy(decl: *Decl, module: *Module) void { const gpa = module.gpa; - log.debug("destroy Decl {*} ({s})", .{ decl, decl.name }); + log.debug("destroy {*} ({s})", .{ decl, decl.name }); decl.clearName(gpa); if (decl.has_tv) { - if (decl.getFunction()) |func| { - func.deinit(gpa); - gpa.destroy(func); - } else if (decl.val.getTypeNamespace()) |namespace| { + if (decl.val.getTypeNamespace()) |namespace| { if (namespace.getDecl() == decl) { namespace.clearDecls(module); } @@ -470,7 +467,6 @@ pub const Decl = struct { /// otherwise null. pub fn getFunction(decl: *Decl) ?*Fn { if (!decl.has_tv) return null; - if (decl.ty.zigTypeTag() != .Fn) return null; const func = (decl.val.castTag(.function) orelse return null).data; if (func.owner_decl != decl) return null; return func; @@ -2960,17 +2956,26 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { decl.clearValues(gpa); } - const new_variable = try decl_arena.allocator.create(Var); - new_variable.* = .{ - .owner_decl = decl, - .init = try decl_tv.val.copy(&decl_arena.allocator), - .is_extern = is_extern, - .is_mutable = is_mutable, - .is_threadlocal = is_threadlocal, + const copied_val = try decl_tv.val.copy(&decl_arena.allocator); + const is_extern_fn = copied_val.tag() == .extern_fn; + + // TODO: also avoid allocating this Var structure if `!is_mutable`. + // I think this will require adjusting Sema to copy the value or something + // like that; otherwise it causes use of undefined value when freeing resources. + const decl_val: Value = if (is_extern_fn) copied_val else blk: { + const new_variable = try decl_arena.allocator.create(Var); + new_variable.* = .{ + .owner_decl = decl, + .init = copied_val, + .is_extern = is_extern, + .is_mutable = is_mutable, + .is_threadlocal = is_threadlocal, + }; + break :blk try Value.Tag.variable.create(&decl_arena.allocator, new_variable); }; decl.ty = try decl_tv.ty.copy(&decl_arena.allocator); - decl.val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable); + decl.val = decl_val; decl.align_val = try align_val.copy(&decl_arena.allocator); decl.linksection_val = try linksection_val.copy(&decl_arena.allocator); decl.has_tv = true; @@ -2984,6 +2989,16 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { // The scope needs to have the decl in it. try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl); } + + if (decl.val.tag() == .extern_fn) { + try mod.comp.bin_file.allocateDeclIndexes(decl); + try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); + + if (type_changed and mod.emit_h != null) { + try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); + } + } + return type_changed; } } diff --git a/src/Sema.zig b/src/Sema.zig index bb0a61854a..3e6ab6b588 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2819,6 +2819,7 @@ fn zirFunc( Value.initTag(.null_value), false, inferred_error_set, + false, src_locs, null, ); @@ -2835,6 +2836,7 @@ fn funcCommon( align_val: Value, var_args: bool, inferred_error_set: bool, + is_extern: bool, src_locs: Zir.Inst.Func.SrcLocs, opt_lib_name: ?[]const u8, ) InnerError!*Inst { @@ -2885,10 +2887,6 @@ fn funcCommon( }); }; - if (body_inst == 0) { - 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}); @@ -2930,6 +2928,17 @@ fn funcCommon( } } + if (is_extern) { + return sema.mod.constInst(sema.arena, src, .{ + .ty = fn_ty, + .val = try Value.Tag.extern_fn.create(sema.arena, sema.owner_decl), + }); + } + + if (body_inst == 0) { + return mod.constType(sema.arena, src, fn_ty); + } + const is_inline = fn_ty.fnCallingConvention() == .Inline; const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued; @@ -5795,6 +5804,7 @@ fn zirFuncExtended( align_val, small.is_var_args, small.is_inferred_error, + small.is_extern, src_locs, lib_name, ); diff --git a/src/Zir.zig b/src/Zir.zig index 5c3c41871d..b44386afea 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2217,7 +2217,8 @@ pub const Inst = struct { has_cc: bool, has_align: bool, is_test: bool, - _: u10 = undefined, + is_extern: bool, + _: u9 = undefined, }; }; @@ -4103,6 +4104,7 @@ const Writer = struct { extra.data.return_type, inferred_error_set, false, + false, .none, .none, body, @@ -4150,6 +4152,7 @@ const Writer = struct { extra.data.return_type, small.is_inferred_error, small.is_var_args, + small.is_extern, cc, align_inst, body, @@ -4232,6 +4235,7 @@ const Writer = struct { ret_ty: Inst.Ref, inferred_error_set: bool, var_args: bool, + is_extern: bool, cc: Inst.Ref, align_inst: Inst.Ref, body: []const Inst.Index, @@ -4248,6 +4252,7 @@ const Writer = struct { try self.writeOptionalInstRef(stream, ", cc=", cc); try self.writeOptionalInstRef(stream, ", align=", align_inst); try self.writeFlag(stream, ", vargs", var_args); + try self.writeFlag(stream, ", extern", is_extern); try self.writeFlag(stream, ", inferror", inferred_error_set); if (body.len == 0) {