diff --git a/src/all_types.hpp b/src/all_types.hpp index 6876aaa3ba..902c6e13c2 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 a6c158a780..3ceda41934 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 78bd037839..520c1eed00 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 { - fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); + 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 { - fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); + if (fn_table_entry->llvm_value == nullptr) { + 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 d59acb7015..87e03ea519 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 07158ae784..d1286cbf8a 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 0000000000..08d6768f7c --- /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 0000000000..4ee4e31a10 --- /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 0000000000..b493959e50 --- /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 {}