mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
Select the discriminated member for asserted object literals
An object literal asserted to a discriminated union keeps its own record
layout, and that layout can equal another member's layout: in
`{ kind: "return", value, loc } as Stmt`, a non-null `value` gives the
fields of a `throw` member. The value was wrapped as that member, so a
switch on `kind` read it with the `return` member's layout. The compiled
program aborted with an invalid union tag or read past the record; the
native compiler crashed this way while lowering three.js.
The literal's discriminant now selects the member through type
assertions too, as it already did through parentheses and `satisfies`.
This commit is contained in:
@@ -8581,9 +8581,18 @@ export class Lowerer {
|
||||
// initial contextual layout was chosen. Its known discriminator still
|
||||
// selects the destination arm; validate that payload before wrapping.
|
||||
// Work with the already lowered value so property effects run once.
|
||||
// A type assertion keeps the literal's own record layout, which can
|
||||
// equal another arm's layout (`{ kind: "return", value, loc } as Stmt`
|
||||
// when a `throw` arm has the same fields), so assertions are looked
|
||||
// through as well.
|
||||
if (expr.type.kind === "record" && expected.kind === "union") {
|
||||
let literal = node;
|
||||
while (ts.isParenthesizedExpression(literal) || ts.isSatisfiesExpression(literal))
|
||||
while (
|
||||
ts.isParenthesizedExpression(literal) ||
|
||||
ts.isSatisfiesExpression(literal) ||
|
||||
ts.isAsExpression(literal) ||
|
||||
ts.isTypeAssertion(literal)
|
||||
)
|
||||
literal = literal.expression;
|
||||
const def = this.unions.get(expected.unionId);
|
||||
if (def?.discriminant && ts.isObjectLiteralExpression(literal)) {
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6021-asserted-literal-union-arm.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Object literals asserted to a discriminated union whose own fields match
|
||||
// another member's fields: the discriminant selects the member.
|
||||
interface Loc {
|
||||
start: number;
|
||||
}
|
||||
type Expr = { kind: "num"; value: number; loc: Loc } | { kind: "str"; text: string; loc: Loc };
|
||||
type Stmt =
|
||||
| { kind: "return"; value: Expr | null; awaited?: true; loc: Loc }
|
||||
| { kind: "throw"; value: Expr; loc: Loc }
|
||||
| { kind: "if"; cond: Expr; then: Stmt[]; loc: Loc };
|
||||
|
||||
function show(stmt: Stmt): string {
|
||||
switch (stmt.kind) {
|
||||
case "return":
|
||||
return `return${stmt.awaited ? " awaited" : ""} ${stmt.value === null ? "-" : stmt.value.kind}`;
|
||||
case "throw":
|
||||
return `throw ${stmt.value.kind}`;
|
||||
case "if":
|
||||
return `if ${stmt.cond.kind} { ${stmt.then.map(show).join("; ")} }`;
|
||||
}
|
||||
}
|
||||
|
||||
function guarded(value: Expr, loc: Loc): Stmt {
|
||||
return { kind: "if", cond: value, then: [{ kind: "return", value, loc } as Stmt], loc };
|
||||
}
|
||||
|
||||
function branches(reflect: boolean, value: Expr, loc: Loc): Stmt[] {
|
||||
return [
|
||||
...(reflect
|
||||
? [{ kind: "return", value, loc } as Stmt]
|
||||
: [{ kind: "throw", value, loc } as Stmt, { kind: "return", value: null, loc } as Stmt]),
|
||||
];
|
||||
}
|
||||
|
||||
const loc: Loc = { start: 0 };
|
||||
const num: Expr = { kind: "num", value: 1, loc };
|
||||
const str: Expr = { kind: "str", text: "x", loc };
|
||||
console.log(show(guarded(num, loc)));
|
||||
console.log(branches(true, str, loc).map(show).join(" | "));
|
||||
console.log(branches(false, num, loc).map(show).join(" | "));
|
||||
const listed: Stmt[] = [{ kind: "return", value: str, loc } as Stmt, { kind: "throw", value: num, loc }];
|
||||
console.log(listed.map(show).join(" | "), listed.map((stmt) => stmt.kind).join(","));
|
||||
@@ -1927,6 +1927,7 @@
|
||||
"601-closures-loops.ts": 1788,
|
||||
"602-closures-identity-recursion.ts": 2372,
|
||||
"6020-class-extends-function-constructor.js": 4300,
|
||||
"6021-asserted-literal-union-arm.ts": 3400,
|
||||
"603-closures-rc-stress.ts": 1832,
|
||||
"604-closures-for-of.ts": 2639,
|
||||
"605-closures-forward-capture-tdz.ts": 3277,
|
||||
@@ -4635,6 +4636,7 @@
|
||||
"601-closures-loops.ts": 2297,
|
||||
"602-closures-identity-recursion.ts": 2427,
|
||||
"6020-class-extends-function-constructor.js": 7500,
|
||||
"6021-asserted-literal-union-arm.ts": 7200,
|
||||
"603-closures-rc-stress.ts": 2589,
|
||||
"604-closures-for-of.ts": 3514,
|
||||
"605-closures-forward-capture-tdz.ts": 4638,
|
||||
|
||||
Reference in New Issue
Block a user