diff --git a/packages/compiler/src/backend/llvm/emitter.test.ts b/packages/compiler/src/backend/llvm/emitter.test.ts index 5c0dc6308..cd364f414 100644 --- a/packages/compiler/src/backend/llvm/emitter.test.ts +++ b/packages/compiler/src/backend/llvm/emitter.test.ts @@ -411,7 +411,8 @@ test("checked field receivers borrow only the successful projection", () => { }; const llvm = work(text(child(checked)), union); expect(llvm).toContain("@scr_throw_node_coded"); - expect(llvm).toMatch(PENDING_CHECK); + // The throw always leaves an exception pending: it unwinds directly. + expect(llvm).not.toMatch(PENDING_CHECK); expect(llvm).not.toContain("@scr_union_retain_v"); expect(llvm).not.toMatch(/call ptr @sc_rretain_/); const captured = work(text(child(checked)), union, true); diff --git a/packages/compiler/src/backend/llvm/emitter.ts b/packages/compiler/src/backend/llvm/emitter.ts index aa582e059..02c90cdb7 100644 --- a/packages/compiler/src/backend/llvm/emitter.ts +++ b/packages/compiler/src/backend/llvm/emitter.ts @@ -3626,6 +3626,18 @@ export class LlEmitter { B.startBlock(lk); } + /** After a runtime call that ALWAYS leaves an exception pending (the + * compiler-resolved Node throws): the pending test would always answer + * true, so unwind unconditionally. Emission continues in a block with no + * predecessors so the caller's remaining instructions stay well-formed; + * LLVM deletes it. */ + emitAlwaysPendingUnwind(): void { + const B = this.B; + if (B.isTerminated()) return; + this.emitUnwind(); + B.startBlock(B.newLabel("exc.dead")); + } + private workerLoopBudget(): string | null { if (!this.mod.workers) return null; const slot = this.B.slot(); diff --git a/packages/compiler/src/backend/llvm/lib-filesystem.ts b/packages/compiler/src/backend/llvm/lib-filesystem.ts index f2490ae03..5a70a55a8 100644 --- a/packages/compiler/src/backend/llvm/lib-filesystem.ts +++ b/packages/compiler/src/backend/llvm/lib-filesystem.ts @@ -114,7 +114,7 @@ export function emitDynamicLibCall(host: LlvmEmitterContext, e: LibCallExpr): Ll const ty = host.llType(e.type); const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null"; const out = host.own({ name: dummy, type: e.type }); - host.emitPendingCheck(); + host.emitAlwaysPendingUnwind(); return out; } return host.emitGenericLibCall(e); diff --git a/packages/compiler/src/backend/llvm/lib-process.ts b/packages/compiler/src/backend/llvm/lib-process.ts index ef0bacdcb..70b2cda83 100644 --- a/packages/compiler/src/backend/llvm/lib-process.ts +++ b/packages/compiler/src/backend/llvm/lib-process.ts @@ -636,12 +636,12 @@ export function emitErrorsEventsLibCall(host: LlvmEmitterContext, e: LibCallExpr B.line(`call void @scr_throw_arg_type(ptr ${an.name}, ptr ${ex.name}, ptr ${got.name})`); const ty = host.llType(e.type); if (ty === "void") { - host.emitPendingCheck(); + host.emitAlwaysPendingUnwind(); return { name: "", type: e.type }; } const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null"; const out = host.own({ name: dummy, type: e.type }); - host.emitPendingCheck(); + host.emitAlwaysPendingUnwind(); return out; } if (e.fn === "error.propTypeThrow") { @@ -653,12 +653,12 @@ export function emitErrorsEventsLibCall(host: LlvmEmitterContext, e: LibCallExpr B.line(`call void @scr_throw_prop_type(ptr ${an.name}, ptr ${ex.name}, ptr ${got.name})`); const ty = host.llType(e.type); if (ty === "void") { - host.emitPendingCheck(); + host.emitAlwaysPendingUnwind(); return { name: "", type: e.type }; } const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null"; const out = host.own({ name: dummy, type: e.type }); - host.emitPendingCheck(); + host.emitAlwaysPendingUnwind(); return out; } if (e.fn === "error.nodeThrow") { @@ -673,12 +673,12 @@ export function emitErrorsEventsLibCall(host: LlvmEmitterContext, e: LibCallExpr ); const ty = host.llType(e.type); if (ty === "void") { - host.emitPendingCheck(); + host.emitAlwaysPendingUnwind(); return { name: "", type: e.type }; } const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null"; const out = host.own({ name: dummy, type: e.type }); - host.emitPendingCheck(); + host.emitAlwaysPendingUnwind(); return out; } if (e.fn === "emitter.on") {