diff --git a/src/all_types.hpp b/src/all_types.hpp index 6876aaa3ba0a06053ab571836df4e61c05c79eff..902c6e13c234fd15c43cd7434f7aee829afe8eef 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1679,7 +1679,7 @@ struct CodeGen { HashMap generic_table; HashMap memoized_fn_eval_table; HashMap llvm_fn_table; - HashMap exported_symbol_names; + HashMap exported_symbol_names; HashMap external_prototypes; HashMap string_literals_table; HashMap type_info_cache; diff --git a/src/analyze.cpp b/src/analyze.cpp index a6c158a7806d972794536bad0658460d432b2845..3ceda4193422c12826e766885730230c94a5e12e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3439,9 +3439,9 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { if (is_export) { g->resolve_queue.append(tld); - auto entry = g->exported_symbol_names.put_unique(tld->name, tld->source_node); + auto entry = g->exported_symbol_names.put_unique(tld->name, tld); if (entry) { - AstNode *other_source_node = entry->value; + AstNode *other_source_node = entry->value->source_node; ErrorMsg *msg = add_node_error(g, tld->source_node, buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name))); add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here")); diff --git a/src/codegen.cpp b/src/codegen.cpp index 78bd037839f12d923f2ff60c77446d83c0cdafa0..520c1eed0005d79d8313334687ea93338f199056 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -478,10 +478,23 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { fn_table_entry->llvm_value = LLVMConstBitCast(existing_llvm_fn, LLVMPointerType(fn_llvm_type, 0)); return fn_table_entry->llvm_value; } else { + auto entry = g->exported_symbol_names.maybe_get(symbol_name); + if (entry == nullptr) { + fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); + } else { + assert(entry->value->id == TldIdFn); + TldFn *tld_fn = reinterpret_cast(entry->value); + tld_fn->fn_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), + tld_fn->fn_entry->type_entry->data.fn.raw_type_ref); + fn_table_entry->llvm_value = LLVMConstBitCast(tld_fn->fn_entry->llvm_value, + LLVMPointerType(fn_llvm_type, 0)); + return fn_table_entry->llvm_value; + } + } + } else { + if (fn_table_entry->llvm_value == nullptr) { fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); } - } else { - fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); for (size_t i = 1; i < fn_table_entry->export_list.length; i += 1) { FnExport *fn_export = &fn_table_entry->export_list.items[i]; diff --git a/src/ir.cpp b/src/ir.cpp index d59acb70155b45715f4b6231f1d503d3db8dbfd2..87e03ea51910c31aceb946897b9f1365587dfa20 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13279,12 +13279,21 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio } } - auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, instruction->base.source_node); + // TODO: This function needs to be audited. + // It's not clear how all the different types are supposed to be handled. + // Need comprehensive tests for exporting one thing in one file and declaring an extern var + // in another file. + TldFn *tld_fn = allocate(1); + tld_fn->base.id = TldIdFn; + tld_fn->base.source_node = instruction->base.source_node; + + auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, &tld_fn->base); if (entry) { - AstNode *other_export_node = entry->value; + AstNode *other_export_node = entry->value->source_node; ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("exported symbol collision: '%s'", buf_ptr(symbol_name))); add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here")); + return ira->codegen->invalid_instruction; } bool want_var_export = false; @@ -13295,6 +13304,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdFn: { assert(target->value.data.x_ptr.special == ConstPtrSpecialFunction); ZigFn *fn_entry = target->value.data.x_ptr.data.fn.fn_entry; + tld_fn->fn_entry = fn_entry; CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc; switch (cc) { case CallingConventionUnspecified: { diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 07158ae784a073a16a4174775ed743e1cd697f90..d1286cbf8a961af01717a59dd8cb35072629c4b2 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -22,6 +22,7 @@ comptime { _ = @import("behavior/bugs/2006.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); + _ = @import("behavior/bugs/529.zig"); _ = @import("behavior/bugs/655.zig"); _ = @import("behavior/bugs/656.zig"); _ = @import("behavior/bugs/704.zig"); diff --git a/test/stage1/behavior/bugs/529.zig b/test/stage1/behavior/bugs/529.zig new file mode 100644 index 0000000000000000000000000000000000000000..08d6768f7cf3da29241c0b24fa28e675641252ec --- /dev/null +++ b/test/stage1/behavior/bugs/529.zig @@ -0,0 +1,15 @@ +const A = extern struct { + field: c_int, +}; + +extern fn issue529(?*A) void; + +comptime { + _ = @import("529_other_file_2.zig"); +} + +test "issue 529 fixed" { + @import("529_other_file.zig").issue529(null); + issue529(null); +} + diff --git a/test/stage1/behavior/bugs/529_other_file.zig b/test/stage1/behavior/bugs/529_other_file.zig new file mode 100644 index 0000000000000000000000000000000000000000..4ee4e31a1095ca2b1da0b011acdd430c840fbfef --- /dev/null +++ b/test/stage1/behavior/bugs/529_other_file.zig @@ -0,0 +1,5 @@ +pub const A = extern struct { + field: c_int, +}; + +pub extern fn issue529(?*A) void; diff --git a/test/stage1/behavior/bugs/529_other_file_2.zig b/test/stage1/behavior/bugs/529_other_file_2.zig new file mode 100644 index 0000000000000000000000000000000000000000..b493959e506b3b29d1715d35d3c4644a4e9150c4 --- /dev/null +++ b/test/stage1/behavior/bugs/529_other_file_2.zig @@ -0,0 +1,4 @@ +pub const A = extern struct { + field: c_int, +}; +export fn issue529(a: ?*A) void {}