Problem
The Lua-level optimizer has two sibling rewrites that fold an operation applied to an immediately-invoked scope call ((function() … end)(), the shape codegen emits for an if in expression position) into the callee's return sites. They cover their tail chains asymmetrically. foldCallThroughScopeCall — the rule folding (function() … return e end)()(args) into return e(args) — recurses through branching tails: its pushIntoStatement handles Return, IfThenElse (both blocks, gated on atomic arguments), and Do. Its projection sibling foldFieldProjectionThroughScopeCall — folding ((function() … return e end)()).f into return e.f — only matches a body whose last statement is a single Return (matchScopeCallProjection), so a field read over a branching scope call keeps its IIFE:
return ((function() if b then return l else return r end end)()).name
stays as is, while the call-shaped analogue of the same body folds. Since #243 the IR optimizer distributes accessors into IfThenElse arms before codegen, so IR-originated shapes no longer reach this backstop — but the Lua optimizer also runs over shapes the IR never sees (FFI-adjacent rewriting, fixtures), and the asymmetry makes the backstop's coverage surprising.
Approach
Extend foldFieldProjectionThroughScopeCall to traverse the same tail chain as foldCallThroughScopeCall: recurse the projection through IfThenElse/Do tails, declining on the same conditions (an early Return among the leading statements, a fall-off path, a multi-value tail Return). A field name has no evaluation cost, so no atomic-arguments analogue is needed. Alternatively, extract the shared tail-chain walker from foldCallThroughScopeCall and instantiate it for both consumers, so the two rules cannot drift again.
Prerequisites / Relations
None blocking. Relates to #243 (the IR-level distribution that now handles these shapes before codegen for IR-originated code) and to the scope-call fold family shipped for #172.
Verification / Measurement
A unit test over the Lua optimizer: a projection over a branching scope call folds into both return sites, and a body with an early return or fall-off path is declined. Existing goldens should not move (the IR rule already strips these shapes from IR-originated output); if any do, the diff is the review artifact.
Problem
The Lua-level optimizer has two sibling rewrites that fold an operation applied to an immediately-invoked scope call (
(function() … end)(), the shape codegen emits for anifin expression position) into the callee's return sites. They cover their tail chains asymmetrically.foldCallThroughScopeCall— the rule folding(function() … return e end)()(args)intoreturn e(args)— recurses through branching tails: itspushIntoStatementhandlesReturn,IfThenElse(both blocks, gated on atomic arguments), andDo. Its projection siblingfoldFieldProjectionThroughScopeCall— folding((function() … return e end)()).fintoreturn e.f— only matches a body whose last statement is a singleReturn(matchScopeCallProjection), so a field read over a branching scope call keeps its IIFE:stays as is, while the call-shaped analogue of the same body folds. Since #243 the IR optimizer distributes accessors into
IfThenElsearms before codegen, so IR-originated shapes no longer reach this backstop — but the Lua optimizer also runs over shapes the IR never sees (FFI-adjacent rewriting, fixtures), and the asymmetry makes the backstop's coverage surprising.Approach
Extend
foldFieldProjectionThroughScopeCallto traverse the same tail chain asfoldCallThroughScopeCall: recurse the projection throughIfThenElse/Dotails, declining on the same conditions (an earlyReturnamong the leading statements, a fall-off path, a multi-value tailReturn). A field name has no evaluation cost, so no atomic-arguments analogue is needed. Alternatively, extract the shared tail-chain walker fromfoldCallThroughScopeCalland instantiate it for both consumers, so the two rules cannot drift again.Prerequisites / Relations
None blocking. Relates to #243 (the IR-level distribution that now handles these shapes before codegen for IR-originated code) and to the scope-call fold family shipped for #172.
Verification / Measurement
A unit test over the Lua optimizer: a projection over a branching scope call folds into both return sites, and a body with an early return or fall-off path is declined. Existing goldens should not move (the IR rule already strips these shapes from IR-originated output); if any do, the diff is the review artifact.