From 8adabaa4ed2321882e2ef04ebb2d9b621805aac2 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 15 Aug 2025 11:18:45 +0100 Subject: [PATCH] Zcu: don't tell linkers about exports if there are compile errors In the best case, this is redundant work, because we aren't actually going to emit a working binary this update. In the worst case, it causes bugs because the linker may not have *seen* the thing being exported due to the compile errors. Resolves: #24417 --- src/Compilation.zig | 10 +++------- src/Zcu/PerThread.zig | 17 ++++++++++++----- .../exported_function_uses_invalid_type.zig | 13 +++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 test/cases/compile_errors/exported_function_uses_invalid_type.zig diff --git a/src/Compilation.zig b/src/Compilation.zig index 435919fce9..e340cb57f8 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4254,14 +4254,10 @@ fn appendCompileLogLines(log_text: *std.ArrayListUnmanaged(u8), zcu: *Zcu, loggi } } -fn anyErrors(comp: *Compilation) bool { - return (totalErrorCount(comp) catch return true) != 0; -} - -fn totalErrorCount(comp: *Compilation) !u32 { - var errors = try comp.getAllErrorsAlloc(); +pub fn anyErrors(comp: *Compilation) bool { + var errors = comp.getAllErrorsAlloc() catch return true; defer errors.deinit(comp.gpa); - return errors.errorMessageCount(); + return errors.errorMessageCount() > 0; } pub const ErrorNoteHashContext = struct { diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 5a76760540..6d2da7afa4 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -3149,18 +3149,23 @@ pub fn processExports(pt: Zcu.PerThread) !void { } } + // If there are compile errors, we won't call `updateExports`. Not only would it be redundant + // work, but the linker may not have seen an exported `Nav` due to a compile error, so linker + // implementations would have to handle that case. This early return avoids that. + const skip_linker_work = zcu.comp.anyErrors(); + // Map symbol names to `Export` for name collision detection. var symbol_exports: SymbolExports = .{}; defer symbol_exports.deinit(gpa); for (nav_exports.keys(), nav_exports.values()) |exported_nav, exports_list| { const exported: Zcu.Exported = .{ .nav = exported_nav }; - try pt.processExportsInner(&symbol_exports, exported, exports_list.items); + try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work); } for (uav_exports.keys(), uav_exports.values()) |exported_uav, exports_list| { const exported: Zcu.Exported = .{ .uav = exported_uav }; - try pt.processExportsInner(&symbol_exports, exported, exports_list.items); + try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work); } } @@ -3171,6 +3176,7 @@ fn processExportsInner( symbol_exports: *SymbolExports, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, + skip_linker_work: bool, ) error{OutOfMemory}!void { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -3216,13 +3222,14 @@ fn processExportsInner( } break :failed false; }) { - // This `Decl` is failed, so was never sent to codegen. - // TODO: we should probably tell the backend to delete any old exports of this `Decl`? - return; + // This `Nav` is failed, so was never sent to codegen. There should be a compile error. + assert(skip_linker_work); }, .uav => {}, } + if (skip_linker_work) return; + if (zcu.llvm_object) |llvm_object| { try zcu.handleUpdateExports(export_indices, llvm_object.updateExports(pt, exported, export_indices)); } else if (zcu.comp.bin_file) |lf| { diff --git a/test/cases/compile_errors/exported_function_uses_invalid_type.zig b/test/cases/compile_errors/exported_function_uses_invalid_type.zig new file mode 100644 index 0000000000..62274236ff --- /dev/null +++ b/test/cases/compile_errors/exported_function_uses_invalid_type.zig @@ -0,0 +1,13 @@ +export fn foo() void { + const S = struct { x: u32 = "bad default" }; + const s: S = undefined; + _ = s; +} + +// This test case explicitly runs on the LLVM backend as well as self-hosted, as +// the original bug leading to this test occurred only with the LLVM backend. + +// error +// backend=stage2,llvm +// +// :2:33: error: expected type 'u32', found '*const [11:0]u8'