From 27537e0718f9105781cfe37dbd2da52151b7cf4d Mon Sep 17 00:00:00 2001 From: Eli Sterling Date: Mon, 10 Aug 2026 05:10:25 +0000 Subject: [PATCH] Fix dynamic promise runtime gating --- packages/compiler/src/ir/nodes.ts | 8 ++++++++ tests/harness/island.test.ts | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/compiler/src/ir/nodes.ts b/packages/compiler/src/ir/nodes.ts index 54ea11bc1..6736c4677 100644 --- a/packages/compiler/src/ir/nodes.ts +++ b/packages/compiler/src/ir/nodes.ts @@ -6074,6 +6074,14 @@ export function moduleUsesDynAsync(mod: IrModule): boolean { found = true; return; } + // Promise values crossing the checked-dynamic boundary are boxed by + // emit-walkers through scr_dyn_new_promise_adapting(). That constructor + // lives in scr_async_dyn.c, so promise-typed IR must pull that TU in even + // when the program never awaits a dyn value. + if (node.type !== undefined && node.type.kind === "promise") { + found = true; + return; + } for (const key of Object.keys(v)) visit((v as Record)[key]); }; visit(mod); diff --git a/tests/harness/island.test.ts b/tests/harness/island.test.ts index f7be561e8..0ef62ee65 100644 --- a/tests/harness/island.test.ts +++ b/tests/harness/island.test.ts @@ -338,6 +338,16 @@ console.log(__island_eval("Promise.reject(new TypeError('island second')); 'arme expect(r.stderr).toBe("Unhandled promise rejection: RangeError: static first\n"); }); + test("links the dynamic promise adapter for typed promises crossing a dynamic callback", async () => { + await build( + "typed-promise-dynamic-boundary", + `async function typed(): Promise { return 1; } +function invoke(fn: () => unknown): unknown { return fn(); } +console.log(invoke(typed)); +`, + ); + }); + test("--dynamic does not change emitted C for island-free programs", async () => { const source = `function greet(who: string): string { return "hello " + who;