Fold ObjectProp field reads over known constructors (#213)#215
Merged
Conversation
Extend case-of-known-constructor (reduceKnownConstructor) to the record-projection field read the pattern matcher actually emits: `ObjectProp (K a₁ … aₙ) "valueᵢ"` over a saturated constructor application folds to `aᵢ`. The label maps to its position through the constructor's declared field names, so a projection whose label is not one of them is declined. This is the ObjectProp twin of the DataArgumentByIndex fold from #177 and the algebraic-data companion of the record-literal fold from #153. Discarded arguments are dropped rather than evaluated or duplicated, and the folded value takes the read node's own annotation, not the field's, matching the existing discard/annotation discipline. Standalone impact is near zero; the shape appears once dictionary-method inlining (#180) folds a constructor into a projection, so this lands as a prerequisite that issue builds on.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR extends the IR optimizer’s “case-of-known-constructor” folding so it can eliminate constructor payload reads that appear as ObjectProp … (PropName "value<i>"), matching the projection shape produced by the pattern matcher and unblocking downstream dictionary-method inlining work.
Changes:
- Add a
reduceKnownConstructorrewrite forObjectProp (Ctor … a₁ … aₙ) "valueᵢ"to fold toaᵢwhen the constructor application is saturated and the label matches a declared field. - Add Hspec + Hedgehog coverage mirroring the existing
DataArgumentByIndexfolding tests, including annotation preservation and randomized discard-semantics stress. - Add a changelog fragment documenting the new optimizer behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
lib/Language/PureScript/Backend/IR/Optimizer.hs |
Adds the ObjectProp-over-saturated-Ctor folding case to reduceKnownConstructor, using field-name/prop-name matching and preserving the projection node’s annotation. |
test/Language/PureScript/Backend/IR/Optimizer/Spec.hs |
Adds targeted unit/property tests covering success/decline cases, sibling-rule cascade, discard semantics, and annotation leak guards for the new fold. |
changelog.d/20260709_110442_unisay_objectprop_ctor_fold.md |
Documents the new optimizer rule and its intended discipline/impact. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #213.
What
Constructor field access reaches the IR as an
ObjectProprecord projection,ObjectProp (Ref v) (PropName "value0"), not asDataArgumentByIndex. That is the shape the generatedbind/map/maybeworkers actually contain. Nothing folded it:reduceObjectProphandles record literals and updates only (#153), and the case-of-known-constructor folds of #177 cover theDataArgumentByIndexfield-read form. So(Just 7).value0was left standing even with the constructor syntactically in place.How
This adds one case to
reduceKnownConstructor:ObjectProp (K a₁ … aₙ) "valueᵢ"over a saturated constructor application folds toaᵢ. The projection label maps to its positional argument through the constructor's declared field names (value0,value1, …, the row keys the Lua backend emits for aCtor), so a label that is not one of them is declined rather than guessed. It reuses the discipline already established for theDataArgumentByIndexfold: discarded arguments are dropped rather than evaluated or duplicated, and the folded value carries the read node's own annotation, not the field's (the leak care behindreduceObjectProp).Why now
This is a prerequisite for #180 (budgeted dictionary-method inlining). Once a method is inlined and its constructor argument is in place, the payload reads it through exactly this
ObjectPropshape; without this fold the inliner would inline but the projection would not collapse. #214 (case-of-known-constructor through a let) stacks on this branch, and #180 stacks on #214.Verification
New optimizer specs mirror the #177 coverage for the projection form: saturated sum and product reads, the sibling-rule cascade, the partial-application and absent-label declines, the discarded-argument drop, and the two annotation-leak guards. A randomized property stresses the discard semantics across arbitrary arity, algebraic type, and argument content.
Full suite green (
cabal test all): 695 examples, 0 failures. No structural golden moved and no eval oracle moved, which matches the near-zero standalone impact the issue predicts, since the shape barely occurs before method inlining lands.fourmoluandhlintare clean and the build is warning-free.