mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
frontend: fix reference tracking through coerced function bodies
This bug was manifesting for user as a nasty link error because they were calling their application's main entry point as a coerced function, which essentially broke reference tracking for the entire ZCU, causing exported symbols to silently not get exported. I've been a little unsure about how coerced functions should interact with the unit graph before, but the solution is actually really obvious now: they shouldn't! `Sema` is now responsible for unwrapping possibly-coerced functions *before* queuing analysis or marking unit references. This makes the reference graph optimal (there are no redundant edges representing coerced versions of the same function) and simplifies logic elsewhere at the expense of just a few lines in Sema.
This commit is contained in:
parent
377a8b2a3b
commit
8744865425
56
src/Sema.zig
56
src/Sema.zig
@ -4376,8 +4376,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
|
|||||||
if (zcu.intern_pool.isFuncBody(val)) {
|
if (zcu.intern_pool.isFuncBody(val)) {
|
||||||
const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));
|
const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));
|
||||||
if (try ty.fnHasRuntimeBitsSema(pt)) {
|
if (try ty.fnHasRuntimeBitsSema(pt)) {
|
||||||
try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = val }));
|
const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(val);
|
||||||
try zcu.ensureFuncBodyAnalysisQueued(val);
|
try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index }));
|
||||||
|
try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5589,16 +5590,21 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
|
|||||||
}
|
}
|
||||||
|
|
||||||
try sema.ensureMemoizedStateResolved(src, .panic);
|
try sema.ensureMemoizedStateResolved(src, .panic);
|
||||||
try zcu.ensureFuncBodyAnalysisQueued(zcu.builtin_decl_values.get(.@"panic.call"));
|
const panic_fn_index = zcu.builtin_decl_values.get(.@"panic.call");
|
||||||
|
|
||||||
const panic_fn = Air.internedToRef(zcu.builtin_decl_values.get(.@"panic.call"));
|
|
||||||
|
|
||||||
const opt_usize_ty = try pt.optionalType(.usize_type);
|
const opt_usize_ty = try pt.optionalType(.usize_type);
|
||||||
const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{
|
const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{
|
||||||
.ty = opt_usize_ty.toIntern(),
|
.ty = opt_usize_ty.toIntern(),
|
||||||
.val = .none,
|
.val = .none,
|
||||||
} })));
|
} })));
|
||||||
try sema.callBuiltin(block, src, panic_fn, .auto, &.{ coerced_msg, null_ret_addr }, .@"@panic");
|
// `callBuiltin` also calls `addReferenceEntry` to the function body for us.
|
||||||
|
try sema.callBuiltin(
|
||||||
|
block,
|
||||||
|
src,
|
||||||
|
.fromIntern(panic_fn_index),
|
||||||
|
.auto,
|
||||||
|
&.{ coerced_msg, null_ret_addr },
|
||||||
|
.@"@panic",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||||
@ -7567,8 +7573,9 @@ fn analyzeCall(
|
|||||||
ref_func: {
|
ref_func: {
|
||||||
const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func;
|
const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func;
|
||||||
if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func;
|
if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func;
|
||||||
try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = runtime_func_val.toIntern() }));
|
const orig_fn_index = ip.unwrapCoercedFunc(runtime_func_val.toIntern());
|
||||||
try zcu.ensureFuncBodyAnalysisQueued(runtime_func_val.toIntern());
|
try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = orig_fn_index }));
|
||||||
|
try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const call_tag: Air.Inst.Tag = switch (modifier) {
|
const call_tag: Air.Inst.Tag = switch (modifier) {
|
||||||
@ -26383,23 +26390,27 @@ fn explainWhyTypeIsNotPacked(
|
|||||||
/// instructions. This function ensures the panic function will be available to
|
/// instructions. This function ensures the panic function will be available to
|
||||||
/// be called during that time.
|
/// be called during that time.
|
||||||
fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !void {
|
fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !void {
|
||||||
|
const zcu = sema.pt.zcu;
|
||||||
|
|
||||||
// If the backend doesn't support `.panic_fn`, it doesn't want us to lower the panic handlers.
|
// If the backend doesn't support `.panic_fn`, it doesn't want us to lower the panic handlers.
|
||||||
// The backend will transform panics into traps instead.
|
// The backend will transform panics into traps instead.
|
||||||
if (sema.pt.zcu.backendSupportsFeature(.panic_fn)) {
|
if (!zcu.backendSupportsFeature(.panic_fn)) return;
|
||||||
_ = try sema.getPanicIdFunc(src, panic_id);
|
|
||||||
}
|
const fn_index = try sema.getPanicIdFunc(src, panic_id);
|
||||||
|
const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(fn_index);
|
||||||
|
try sema.addReferenceEntry(null, src, .wrap(.{ .func = orig_fn_index }));
|
||||||
|
try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index {
|
fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index {
|
||||||
const zcu = sema.pt.zcu;
|
const zcu = sema.pt.zcu;
|
||||||
try sema.ensureMemoizedStateResolved(src, .panic);
|
try sema.ensureMemoizedStateResolved(src, .panic);
|
||||||
const panic_func = zcu.builtin_decl_values.get(panic_id.toBuiltin());
|
const panic_fn_index = zcu.builtin_decl_values.get(panic_id.toBuiltin());
|
||||||
try zcu.ensureFuncBodyAnalysisQueued(panic_func);
|
|
||||||
switch (sema.owner.unwrap()) {
|
switch (sema.owner.unwrap()) {
|
||||||
.@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
|
.@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
|
||||||
.func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(owner_func, true),
|
.func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(owner_func, true),
|
||||||
}
|
}
|
||||||
return panic_func;
|
return panic_fn_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addSafetyCheck(
|
fn addSafetyCheck(
|
||||||
@ -31143,6 +31154,11 @@ fn addReferenceEntry(
|
|||||||
referenced_unit: AnalUnit,
|
referenced_unit: AnalUnit,
|
||||||
) !void {
|
) !void {
|
||||||
const zcu = sema.pt.zcu;
|
const zcu = sema.pt.zcu;
|
||||||
|
const ip = &zcu.intern_pool;
|
||||||
|
switch (referenced_unit.unwrap()) {
|
||||||
|
.func => |f| assert(ip.unwrapCoercedFunc(f) == f), // for `.{ .func = f }`, `f` must be uncoerced
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
|
if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
|
||||||
const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
|
const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
|
||||||
if (gop.found_existing) return;
|
if (gop.found_existing) return;
|
||||||
@ -31329,8 +31345,9 @@ fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_i
|
|||||||
const nav_val = zcu.navValue(nav_index);
|
const nav_val = zcu.navValue(nav_index);
|
||||||
if (!ip.isFuncBody(nav_val.toIntern())) return;
|
if (!ip.isFuncBody(nav_val.toIntern())) return;
|
||||||
|
|
||||||
try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = nav_val.toIntern() }));
|
const orig_fn_index = ip.unwrapCoercedFunc(nav_val.toIntern());
|
||||||
try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern());
|
try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index }));
|
||||||
|
try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyzeRef(
|
fn analyzeRef(
|
||||||
@ -34963,8 +34980,9 @@ fn resolveInferredErrorSet(
|
|||||||
}
|
}
|
||||||
// In this case we are dealing with the actual InferredErrorSet object that
|
// In this case we are dealing with the actual InferredErrorSet object that
|
||||||
// corresponds to the function, not one created to track an inline/comptime call.
|
// corresponds to the function, not one created to track an inline/comptime call.
|
||||||
try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = func_index }));
|
const orig_func_index = ip.unwrapCoercedFunc(func_index);
|
||||||
try pt.ensureFuncBodyUpToDate(func_index);
|
try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_func_index }));
|
||||||
|
try pt.ensureFuncBodyUpToDate(orig_func_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody`
|
// This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody`
|
||||||
|
|||||||
@ -3451,8 +3451,11 @@ pub fn mapOldZirToNew(
|
|||||||
/// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`.
|
/// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`.
|
||||||
pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !void {
|
pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !void {
|
||||||
const ip = &zcu.intern_pool;
|
const ip = &zcu.intern_pool;
|
||||||
|
|
||||||
const func = zcu.funcInfo(func_index);
|
const func = zcu.funcInfo(func_index);
|
||||||
|
|
||||||
|
assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one
|
||||||
|
|
||||||
if (zcu.func_body_analysis_queued.contains(func_index)) return;
|
if (zcu.func_body_analysis_queued.contains(func_index)) return;
|
||||||
|
|
||||||
if (func.analysisUnordered(ip).is_analyzed) {
|
if (func.analysisUnordered(ip).is_analyzed) {
|
||||||
|
|||||||
@ -1571,7 +1571,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
|
|||||||
return .{ .type_changed = true };
|
return .{ .type_changed = true };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {
|
pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!void {
|
||||||
dev.check(.sema);
|
dev.check(.sema);
|
||||||
|
|
||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
@ -1581,15 +1581,15 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
|
|||||||
const gpa = zcu.gpa;
|
const gpa = zcu.gpa;
|
||||||
const ip = &zcu.intern_pool;
|
const ip = &zcu.intern_pool;
|
||||||
|
|
||||||
_ = zcu.func_body_analysis_queued.swapRemove(maybe_coerced_func_index);
|
_ = zcu.func_body_analysis_queued.swapRemove(func_index);
|
||||||
|
|
||||||
// We only care about the uncoerced function.
|
|
||||||
const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
|
|
||||||
const anal_unit: AnalUnit = .wrap(.{ .func = func_index });
|
const anal_unit: AnalUnit = .wrap(.{ .func = func_index });
|
||||||
|
|
||||||
log.debug("ensureFuncBodyUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
|
log.debug("ensureFuncBodyUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
|
||||||
|
|
||||||
const func = zcu.funcInfo(maybe_coerced_func_index);
|
const func = zcu.funcInfo(func_index);
|
||||||
|
|
||||||
|
assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one
|
||||||
|
|
||||||
const was_outdated = zcu.outdated.swapRemove(anal_unit) or
|
const was_outdated = zcu.outdated.swapRemove(anal_unit) or
|
||||||
zcu.potentially_outdated.swapRemove(anal_unit);
|
zcu.potentially_outdated.swapRemove(anal_unit);
|
||||||
|
|||||||
19
test/cases/export_from_body_of_coerced_fn.zig
Normal file
19
test/cases/export_from_body_of_coerced_fn.zig
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
fn original() usize {
|
||||||
|
_ = struct {
|
||||||
|
export const val: u32 = 123;
|
||||||
|
};
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() void {
|
||||||
|
const coerced: fn () u64 = original;
|
||||||
|
_ = coerced();
|
||||||
|
|
||||||
|
const S = struct {
|
||||||
|
extern const val: u32;
|
||||||
|
};
|
||||||
|
if (S.val != 123) @panic("wrong value");
|
||||||
|
}
|
||||||
|
|
||||||
|
// run
|
||||||
|
// target=x86_64-linux
|
||||||
Loading…
x
Reference in New Issue
Block a user