Repository navigation
Answer promises from thunked and dispatched untyped async methods - #1534
Merged
Merged
Conversation
An untyped JavaScript async method whose result the compiler keeps as
`dyn` (its storage passes through untyped helpers) has a `dyn` return
on its signature: the promise's payload, not the promise. Two call paths
took that as the call's own type and failed the IR check ("call … type
dyn != return promise"):
- thunkAsyncSlot's `$own` adapter, made when a TypeScript override of
such a method keeps the inherited slot (an async-sema `acquire`
override). The slot's ABI is now the body's promise.
- `%dyn.class.call` dispatch from untyped callers (zod's `parseAsync`
and `safeParseAsync` through checked-dynamic receivers), which now
answers the promise.
Prototype data slots also skip method values that have no checked-
dynamic form (an async method's promise-returning signature) instead of
building an invalid slot.
From the int/bc round-5 build of the build-container entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
A method whose result is either a status record or a promise of one
(a parse step that hands off to a static async merge helper in async
mode) has a union result such as `Promise<A | B> | A | B`. The dynamic
conversion accepted promise arms in a union only when every other arm
was JSON-safe, so records holding dynamic, Map or Set members were
rejected even though the composite converter boxes them. Such methods
had no checked-dynamic form: no prototype data slot, and reads through
untyped code answered undefined ("this._parse is not a function" in a
validation library's object and record schemas).
The union conversion now also accepts composite-boxable arms; the
union converter already boxes each arm through its own converter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `%dyn.class.call` dispatch answers a promise for an untyped async method whose signature keeps the promise's payload. An async generator method also has the async flag, but its signature already returns the generator object, so the call now keeps that type. Before, a call from untyped code (`schema.issues(values)` over an `async *issues` method) failed IR validation with "type promise != return generator". - thunkAsyncSlot keeps an async generator method's slot type the same way. - Corpus 6419 adds an async generator method that the package's own untyped helper iterates with `next()`.
…criptc into opt/untyped-async-promises
Contributor
Author
|
Added 60f2823: methods whose result is a union of a promise and composite records (records holding dyn, Map or Set members, such as a parse step that answers a status record or hands off to a static async merge helper) now box into the dynamic tier. Before this commit they had no checked-dynamic form, so they got no prototype data slot and reads through untyped code answered undefined. The new corpus program is 6592-promise-armed-method-values. Zero-cost gate against eef68a7: 3432 programs identical; the only difference is the new program. I merged c9c19d8 instead of rebasing. 🤖 Generated with Claude Code |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #1497, which merged before this landed. It takes the two compiler fixes the int/bc round-5 build needed to produce the build-container binary.
Summary
dynon the signature: the result is the promise's payload, not the promise. This happens when the result's storage passes through untyped helpers. Two call paths treated thatdynas the call's own type and failed the IR validator with "call … type dyn != return promise":thunkAsyncSlot's$ownadapter, created when a TypeScript override of such a method keeps the inherited slot (an async-semaacquireoverride). The slot's ABI is now the body's promise.%dyn.class.calldispatch from untyped callers, such as zod'sparseAsyncandsafeParseAsynccalled through checked-dynamic receivers. The call now answers the promise.On the build-container entry these were 75 + 2 internal compiler errors in
%dyn.class.calland$ownadapters, plus the 5 prototype-data errors. With async-sema 3.1.1 and zod 3.25.67 installed, the same 75 internal compiler errors reproduce, and this change clears them.Evidence
6419-untyped-async-package, Node vs native, plain and sanitized lanes. It is a package with an untyped token pool and schema: an asyncacquireoverridden in TypeScript,safeParseAsync/parseAsyncreached from the package's own untyped callers, a limiter closure, and prototype method reads. Before this change it failed to compile with both internal compiler errors.pnpm test:ts7 --baselines-only, frontend/IR unit tests and the shard test pass. The 64xx/639x/638x corpus passes in the plain lane, and 640x–641x also in the sanitized lane.🤖 Generated with Claude Code