Skip to content

Run JavaScript promise executors passed by name, and read catch (e: any) bindings dynamically - #1508

Merged
ctate merged 1 commit into
opt/bc-any-crossingsfrom
opt/bc-any-runtime
Oct 20, 2026
Merged

ctate merged 1 commit into
opt/bc-any-crossingsfrom
opt/bc-any-runtime

Conversation

@cramforce

Copy link
Copy Markdown
Contributor

Stacked on #1503. Two gaps that any-heavy service code hits after #1503.

Named promise executors in JavaScript packages

Retry helpers commonly build their promise from a function declaration:

function retry(fn, opts) {
  function run(resolve, reject) { /* ... */ }
  return new Promise(run);
}

The executor's parameters are untyped, so its reject parameter was not the native (reason: Error) => void closure. Compilation succeeded, but the package threw at run time ("Promise executors whose reject parameter is not '(reason: Error) => void'"). Such an executor now gets an adapter: it receives the native resolve/reject pair and calls the original with both boxed as checked-dynamic function values, so the JavaScript body calls them like any function. Executors whose parameters are already typed keep their existing path.

catch (error: any)

A catch binding annotated any now reads the caught value through its checked-dynamic face, as an unknown binding already did. Member reads, expando writes, delete and in act on the thrown object itself, and a rethrow keeps its identity. Before, every use was refused (SC1063). Deleting a field that a program class declares still throws, because class layouts are fixed.

Evidence

  • New corpus programs:
  • The 86 corpus programs whose names mention promise, executor or catch, JavaScript ones included, pass the differential test.
  • Zero-cost gate against Compile any crossings: destructuring, namespace patterns, any parameters and async callbacks #1503 (37551e4): PASS. 2530 programs are identical. 4642-acyclic-contract.ts fails on both sides, as it already does on main. Bench geomean +0.13% and self-hosted compile time -0.21%, both within tolerance.
  • pnpm lint and pnpm format:check are clean. pnpm test:ts7 --baselines-only passes, with the new baselines recorded. diagnostics.test.ts fails only contract-acyclic.ts, which fails the same way on main.
  • The self-hosted compiler's IR build reports no diagnostics, and tests/harness/self-hosting-native-driver.test.ts passes.

🤖 Generated with Claude Code

…any)` bindings dynamically

- `new Promise(run)` where `run` is a JavaScript function value whose
  parameters are untyped (the retry-helper shape `function run(resolve,
  reject) {...}; return new Promise(run)`) gets an adapter executor: it
  receives the native resolve/reject pair and calls the original with
  both boxed as checked-dynamic function values. This was a deferred
  runtime refusal inside such packages.
- A catch binding annotated `any` reads the caught value through its
  checked-dynamic face, as an unannotated `unknown` one does: member
  reads, expando writes, `delete` and `in` act on the thrown object, and
  a rethrow keeps its identity. It was refused (SC1063).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
scriptc Ready Ready Preview, v0 Oct 20, 2026 8:55am UTC

@ctate
ctate merged commit 7d05328 into opt/bc-any-crossings Oct 20, 2026
61 of 66 checks passed
@ctate
ctate deleted the opt/bc-any-runtime branch October 20, 2026 13:56

This branch was successfully deployed

1 active deployment
Preview — a64d7f8c Deployed Oct 20, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants