Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/compiler/src/frontend/lowering/lower-calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6305,10 +6305,12 @@ function loweredTemplateStrings(
// the validator's bare-unitLit rule (typeCheckReturnExpression).
// A `void e` body rides the statement lowering (the value is
// discarded here, so the operand evaluates for effect alone —
// `(name) => void doThing(name)`, the fire-and-forget arrow).
// `(name) => void doThing(name)`, the fire-and-forget arrow). A
// conditional body does the same so its void arms become lazy
// statement branches instead of a forbidden void-valued ternary.
let stripped: ts.Expression = bodyExpr;
while (ts.isParenthesizedExpression(stripped)) stripped = stripped.expression;
if (ts.isVoidExpression(stripped)) {
if (ts.isVoidExpression(stripped) || ts.isConditionalExpression(stripped)) {
body = [lowerer.lowerExprStatement(stripped)];
} else {
const value = lowerer.lowerExpr(bodyExpr);
Expand Down
20 changes: 20 additions & 0 deletions packages/compiler/src/frontend/lowering/lower-stmts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4725,6 +4725,26 @@ function isEsModuleStamp(expr: ts.Expression): boolean {
export function lowerExprStatement(lowerer: Lowerer, expr: ts.Expression): IrStmt {
const stmtNode = expr.parent;
while (ts.isParenthesizedExpression(expr)) expr = expr.expression;
// A discarded conditional has no value to merge: represent its lazy
// branch selection directly as statement control flow. In particular,
// both arms may be void calls (`flag ? emitA() : emitB()`), for which a
// value-shaped void ternary has no IR representation. Preserve the same
// narrowing environments as a source if statement, and lower only a
// statically selected arm when the condition folded without effects.
if (ts.isConditionalExpression(expr)) {
const cond = lowerer.lowerCondition(expr.condition);
if (cond.kind === "boolLit") {
return lowerExprStatement(lowerer, cond.value ? expr.whenTrue : expr.whenFalse);
}
const then = lowerer.narrowingAliases(aliasTypeofNarrows(lowerer, expr.condition, true), () =>
withRuntimeOptionalNarrowed(lowerer, runtimeOptionalTrueIds(lowerer, expr.condition), () =>
[lowerExprStatement(lowerer, expr.whenTrue)]),
);
const else_ = lowerer.narrowingAliases(aliasTypeofNarrows(lowerer, expr.condition, false), () =>
[lowerExprStatement(lowerer, expr.whenFalse)],
);
return { kind: "if", cond, then, else_, loc: locOf(expr) };
}
// Assignment statements over the no-storage binding families:
// - `f1 = f2` where the RHS roots at an ambient-undefined name:
// Node evaluates the RHS first and dies on the root's
Expand Down
6 changes: 6 additions & 0 deletions packages/compiler/test/ts7/baselines/order-parity.json
Original file line number Diff line number Diff line change
Expand Up @@ -6259,6 +6259,12 @@
],
"diags": []
},
"<repo>/tests/corpus/2900-void-conditional-arrow.ts": {
"order": [
"<repo>/tests/corpus/2900-void-conditional-arrow.ts"
],
"diags": []
},
"<repo>/tests/corpus/300-if-else.ts": {
"order": [
"<repo>/tests/corpus/300-if-else.ts"
Expand Down
22 changes: 22 additions & 0 deletions tests/corpus/2900-void-conditional-arrow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
let calls = 0;

function mark(label: string): void {
calls++;
console.log(label);
}

const choose = (first: boolean, second: boolean): void =>
first ? mark("first") : second ? mark("second") : mark("third");

choose(true, false);
choose(false, true);
choose(false, false);

function chooseStatement(first: boolean, second: boolean): void {
first ? mark("statement-first") : second ? mark("statement-second") : mark("statement-third");
}

chooseStatement(true, false);
chooseStatement(false, true);
chooseStatement(false, false);
console.log(calls);
Loading