mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-08 08:44:37 +08:00
Resolve a promise-or-unit operand in Promise.resolve
`Promise.resolve(p)` over `Promise<void> | void` — an async callback's declared result — was refused: the maybe-promise detection required every non-promise arm to carry a value, while a nullish arm is exactly what `Promise.resolve` fulfills as undefined. `promiseOrUnitUnionOf` names that shape, and the resolution reuses the existing adopt-or- fulfill path, so the promise arm passes through unchanged (`Promise.resolve(p) === p`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
bf28595035
commit
a4a84f59da
@@ -5,7 +5,11 @@ import * as ts from "../../ts7/adapter.js";
|
||||
import type { Lowerer } from "../lowerer.js";
|
||||
import { countedFor, varRef } from "../../../ir/build.js";
|
||||
import { arrayValueStore, optionalElementValue, primitiveElement } from "../array-values.js";
|
||||
import { maybePromiseResolve, maybePromiseUnionOf } from "../maybe-promise.js";
|
||||
import {
|
||||
maybePromiseResolve,
|
||||
maybePromiseUnionOf,
|
||||
promiseOrUnitUnionOf,
|
||||
} from "../maybe-promise.js";
|
||||
import { isJsSourceFile, locOf } from "../../program.js";
|
||||
import {
|
||||
BOOL,
|
||||
@@ -670,11 +674,17 @@ export function lowerPromiseStaticCall(
|
||||
if (v.type.kind !== "promise") lowerer.badType(argNode, argT);
|
||||
return v;
|
||||
}
|
||||
const maybePromise = argIr ? maybePromiseUnionOf(lowerer, argIr) : null;
|
||||
// A promise-or-unit union (`Promise<void> | void`,
|
||||
// `Promise<T> | undefined`) resolves exactly like a maybe-promise
|
||||
// one: Node adopts a promise operand and fulfills every other value
|
||||
// as itself (a nullish arm fulfills undefined).
|
||||
const maybePromise = argIr
|
||||
? (maybePromiseUnionOf(lowerer, argIr) ?? promiseOrUnitUnionOf(lowerer, argIr))
|
||||
: null;
|
||||
if (maybePromise) {
|
||||
const resultT = lowerer.mapTypeOf(lowerer.typeOf(call));
|
||||
const v = lowerer.lowerExpr(argNode);
|
||||
const mp = maybePromiseUnionOf(lowerer, v.type);
|
||||
const mp = maybePromiseUnionOf(lowerer, v.type) ?? promiseOrUnitUnionOf(lowerer, v.type);
|
||||
const resolved =
|
||||
resultT?.kind === "promise" && mp
|
||||
? maybePromiseResolve(lowerer, v, mp, resultT, loc)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// `Promise.resolve(u)` over a promise-or-unit union (`Promise<void> |
|
||||
// void`): Node adopts a promise operand — the SAME promise comes
|
||||
// back — and fulfills a nullish one as undefined. The settled turn of the
|
||||
// adopted promise is unchanged, so a handler chain observes the same
|
||||
// microtask order either way.
|
||||
|
||||
let runs = 0;
|
||||
|
||||
async function step(label: string): Promise<void> {
|
||||
runs++;
|
||||
await Promise.resolve();
|
||||
console.log("step", label, runs);
|
||||
}
|
||||
|
||||
// A callback whose declared result is `Promise<void> | void`: the
|
||||
// async path answers a promise, the sync one answers undefined.
|
||||
function handler(flag: boolean): Promise<void> | void {
|
||||
if (flag) return step("async");
|
||||
runs++;
|
||||
console.log("sync handler", runs);
|
||||
}
|
||||
|
||||
function call(flag: boolean): Promise<void> {
|
||||
return Promise.resolve(handler(flag)).then(() => console.log("call done", flag));
|
||||
}
|
||||
|
||||
const pending: Promise<void> | void = Promise.resolve();
|
||||
console.log("identity", Promise.resolve(pending) === pending);
|
||||
|
||||
console.log("start");
|
||||
call(true)
|
||||
.then(() => call(false))
|
||||
.then(() => call(true))
|
||||
.then(() => console.log("runs", runs));
|
||||
Reference in New Issue
Block a user