You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Lua optimizer rule reduceTableDefinitionAccessor (added in #140) folds a field access into a table literal, rewriting { foo = 1, bar = 2 }.foo to 1. It fires only when the constructor is unambiguous: every row must be name-value (all isNameValue) and no field name may repeat. Both guards are there because the Lua table constructor is ambiguous at that level. A key-value row can carry an arbitrary expression key (["foo"] = …, [k] = …) that a name-keyed lookup cannot see, string and number keys coerce, and a repeated key resolves to the last assignment while a first-match fold would return the earliest.
None of that ambiguity exists one layer up, in the IR. LiteralObject carries [(PropName, Exp)], where PropName is a static label with no computed-key form, and PureScript's type system already forbids duplicate labels in a record literal. The optimizer assumes well-typed IR anyway (see Note [IR is assumed well-typed]). So the equivalent IR rewrite needs no guards at all:
Doing the fold in the IR is also better placed than the Lua-AST version:
It runs inside the optimize+dce fixpoint, so the folded value feeds straight back into constant folding, inlining, and dead-code elimination.
It has a live trigger. LiteralObject is a non-recursive literal, so inlineLocalBindings inlines let r = { foo: 1, bar: 2 } in r.foo into the projection site, producing ObjectProp (LiteralObject …) foo mid-fixpoint. That is created by the inliner on ordinary code, not a rare source shape.
It shrinks the IR before magicDo, flatten-deep-binds, and lowering, so the emitted Lua is already clean.
Proposal:
Add the rewrite (e.g. reduceObjectProp) to the rule chain in optimizedExpressionM.
Optionally handle ObjectProp (ObjectUpdate obj patches) prop: if prop is among the patches use the patched value, otherwise recurse into obj.
Evaluation order: the fold drops the evaluation of the discarded fields (bar above). That is the same move the current Lua-AST rule already makes, and it is consistent with the project's policy that DCE drops unused bindings unconditionally. The eval goldens remain the semantic oracle.
Found while reviewing the #140 guard: the guards are a symptom of doing a high-level, semantic optimization at the wrong altitude.
The Lua optimizer rule
reduceTableDefinitionAccessor(added in #140) folds a field access into a table literal, rewriting{ foo = 1, bar = 2 }.footo1. It fires only when the constructor is unambiguous: every row must be name-value (all isNameValue) and no field name may repeat. Both guards are there because the Lua table constructor is ambiguous at that level. A key-value row can carry an arbitrary expression key (["foo"] = …,[k] = …) that a name-keyed lookup cannot see, string and number keys coerce, and a repeated key resolves to the last assignment while a first-match fold would return the earliest.None of that ambiguity exists one layer up, in the IR.
LiteralObjectcarries[(PropName, Exp)], wherePropNameis a static label with no computed-key form, and PureScript's type system already forbids duplicate labels in a record literal. The optimizer assumes well-typed IR anyway (see Note [IR is assumed well-typed]). So the equivalent IR rewrite needs no guards at all:Doing the fold in the IR is also better placed than the Lua-AST version:
optimize+dcefixpoint, so the folded value feeds straight back into constant folding, inlining, and dead-code elimination.LiteralObjectis a non-recursive literal, soinlineLocalBindingsinlineslet r = { foo: 1, bar: 2 } in r.foointo the projection site, producingObjectProp (LiteralObject …) foomid-fixpoint. That is created by the inliner on ordinary code, not a rare source shape.Proposal:
reduceObjectProp) to the rule chain inoptimizedExpressionM.reduceTableDefinitionAccessorand its spec as superseded. This supersedes the guard added in reduceTableDefinitionAccessor ignores TableRowKV rows and duplicate fields (folds to Nil) #140; that hardening was correct while the rule lived on the Lua AST.ObjectProp (ObjectUpdate obj patches) prop: ifpropis among the patches use the patched value, otherwise recurse intoobj.Evaluation order: the fold drops the evaluation of the discarded fields (
barabove). That is the same move the current Lua-AST rule already makes, and it is consistent with the project's policy that DCE drops unused bindings unconditionally. The eval goldens remain the semantic oracle.Found while reviewing the #140 guard: the guards are a symptom of doing a high-level, semantic optimization at the wrong altitude.