Hold a refined type's refinements in one unordered set instead of nested layers - #77
Open
dpmills wants to merge 1 commit into
Open
Hold a refined type's refinements in one unordered set instead of nested layers#77dpmills wants to merge 1 commit into
dpmills wants to merge 1 commit into
Conversation
Contributor
Author
|
This change is part of the following stack:
Change managed by git-spice. |
This was referenced Aug 11, 2026
dpmills
force-pushed
the
dmills/refinement-set
branch
2 times, most recently
from
August 12, 2026 00:09
471a5f7 to
f0c8c69
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 12, 2026 00:38
f0c8c69 to
26a9d57
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 12, 2026 20:43
26a9d57 to
6597b8f
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 12, 2026 22:30
6597b8f to
dc09c89
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 12, 2026 22:45
dc09c89 to
2952fc6
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 13, 2026 05:39
2952fc6 to
f85a5ce
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 19, 2026 23:34
8c3604d to
d840cdb
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 20, 2026 00:12
d840cdb to
edb1f5f
Compare
This was referenced Aug 20, 2026
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 20, 2026 20:51
edb1f5f to
945e56c
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
3 times, most recently
from
August 21, 2026 04:39
e00d838 to
487c221
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 21, 2026 04:55
487c221 to
a41b3e3
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 21, 2026 05:47
a41b3e3 to
8a5d902
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
3 times, most recently
from
August 21, 2026 20:45
5e588d6 to
63d35b7
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
from
August 21, 2026 22:23
63d35b7 to
c345287
Compare
dpmills
force-pushed
the
dmills/refinement-set
branch
5 times, most recently
from
August 22, 2026 01:06
707bc7e to
f9b95a9
Compare
A coalesced type's refinement layers stacked in constraint *arrival* order: two refined upper bounds meeting at one variable produced `{{T | q} | p}` or `{{T | p} | q}` by which arrived first. Subtyping never cared — the deficit machinery compares layers as a set — but `Type`'s equality did, and equality is load-bearing wherever a type is an *identity*: the trivial-equality short-circuit, cache keys, the recorded-vs-recomputed walls. One `Vec` was serving three readings at once: a set to subtyping, a stack to planning, an identity to `SpecKey`.
`Type::Refinement` now carries a `RefinementSet` — unordered, deduplicated, set-semantic `Eq`/`Hash` — and `Type::refined` flattens, so `{{T | p} | q}` is unrepresentable and "which layer is outermost" cannot be asked. A canonical sort was tried twice and rejected: it pins the ambiguity instead of deleting it, and it denies planning the freedom to apply filters in whatever order it likes.
Three things the change makes explicit. **Materializing refinements is a pipeline**, so planning *chooses* an application order — which order is free, choosing differently in two places is not, and `application_order` is the one place it is chosen, keyed on rendered content rather than physical order. **The recognizers were reading a chain positionally**: `groupby` now finds the refinement shaped like its key equation and `join` tries each refinement as the join condition, both leaving the rest on the domain they pass downstream, where dropping them silently discarded a filter. **An in-place predicate rewrite can make two refinements equal**, and `PartialEq` reads cardinality, so `RefinementSet::rewrite_each` re-establishes the dedup instead of each of the eight rewrite sites arguing it.
`CAMBRA_REFINEMENT_ORDER=reverse` flips every set's physical order and CI runs the suite both ways. Set semantics makes that order meaningless by contract, and a consumer that lets it become observable compiles clean either way.
Review guide: `ty.rs` carries `RefinementSet`, the flattening constructors, `application_order`, and `rewrite_each`, and the rest follows from them mechanically. The behavioural sites are `compact.rs`'s `merge_refinements`, the two planning walks over the application order, and the two recognizers. One test pins each of the three sections above: `refinements_meeting_at_one_variable_do_not_depend_on_arrival_order`, `application_elem_types_follow_the_refinement_not_the_position`, `a_rewrite_that_collapses_two_refinements_leaves_a_set`. The docs' "refinement layer" and "innermost first" are converted throughout.
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.
A coalesced type's refinement layers stacked in constraint arrival order: two refined upper bounds meeting at one variable produced
{{T | q} | p}or{{T | p} | q}by which arrived first. Subtyping never cared — the deficit machinery compares layers as a set — butType's equality did, and equality is load-bearing wherever a type is an identity: the trivial-equality short-circuit, cache keys, the recorded-vs-recomputed walls. OneVecwas serving three readings at once: a set to subtyping, a stack to planning, an identity toSpecKey.Type::Refinementnow carries aRefinementSet— unordered, deduplicated, set-semanticEq/Hash— andType::refinedflattens, so{{T | p} | q}is unrepresentable and "which layer is outermost" cannot be asked. A canonical sort was tried twice and rejected: it pins the ambiguity instead of deleting it, and it denies planning the freedom to apply filters in whatever order it likes.Three things the change makes explicit. Materializing refinements is a pipeline, so planning chooses an application order — which order is free, choosing differently in two places is not, and
application_orderis the one place it is chosen, keyed on rendered content rather than physical order. The recognizers were reading a chain positionally:groupbynow finds the refinement shaped like its key equation andjointries each refinement as the join condition, both leaving the rest on the domain they pass downstream, where dropping them silently discarded a filter. An in-place predicate rewrite can make two refinements equal, andPartialEqreads cardinality, soRefinementSet::rewrite_eachre-establishes the dedup instead of each of the eight rewrite sites arguing it.CAMBRA_REFINEMENT_ORDER=reverseflips every set's physical order and CI runs the suite both ways. Set semantics makes that order meaningless by contract, and a consumer that lets it become observable compiles clean either way.Review guide:
ty.rscarriesRefinementSet, the flattening constructors,application_order, andrewrite_each, and the rest follows from them mechanically. The behavioural sites arecompact.rs'smerge_refinements, the two planning walks over the application order, and the two recognizers. One test pins each of the three sections above:refinements_meeting_at_one_variable_do_not_depend_on_arrival_order,application_elem_types_follow_the_refinement_not_the_position,a_rewrite_that_collapses_two_refinements_leaves_a_set. The docs' "refinement layer" and "innermost first" are converted throughout.