Skip to content

Traverse types once when relating them invariantly - #254

Merged
coord-e merged 1 commit into
mainfrom
claude/reduce-duplicate-clauses-br7c8m
Sep 2, 2026
Merged

Traverse types once when relating them invariantly#254
coord-e merged 1 commit into
mainfrom
claude/reduce-duplicate-clauses-br7c8m

Conversation

@coord-e

@coord-e coord-e commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Relating two types invariantly was expressed as subtyping in both directions:

let mut clauses = self.relate_sub_refined_type(got, expected);
clauses.extend(self.relate_sub_refined_type(expected, got));

relate_sub_refined_type walks the whole type structure, so this traverses it twice, and every owning or mutable pointer found inside relates its referent invariantly again. The number of clauses emitted for a refinement therefore doubles per pointer layer above it.

The two directions do not derive different constraints. Writing S for relate_sub_refined_type, E for relate_equal_refined_type and R for relate_sub_type:

S(A,B) = R(A.ty, B.ty) ++ [ A.ref => B.ref ]
E(A,B) = S(A,B) ++ S(B,A)
R(own X, own Y) = E(X, Y)              -- and likewise for &mut

E is symmetric by its definition, so E(own X, own Y) contains E(X,Y) and E(Y,X) — the same multiset twice. The direction only distinguishes anything at a covariant position, and every position below an owning or mutable pointer is invariant again, so the second direction just re-derives the constraints of the first.

This walks the type once, carrying the relation to derive, and emits both implications at the positions where the refinements must agree.

Effect

Measured on a &mut self trait method over Option<i32> (own (&mut (own Option<{int | p3}>, )), three such pointer layers), and on the same program with one more struct nesting:

before after
three pointer layers 29 clauses / 59.9 KB 14 clauses / 28.0 KB
four pointer layers 49 clauses / 115.0 KB 14 clauses / 31.6 KB

Both had 12 distinct clauses throughout. The count no longer grows with nesting depth. No change on the existing UI tests' output, which do not nest pointers above a refinement.

Two duplicates remain in that example, of the form p3 v <= ... /\ p3 v: both sides of an invariant position happen to carry the same predicate variable, so the two implications coincide. That is inherent to relating a position invariantly and is bounded by 2 regardless of depth. Dropping those would mean treating a clause whose head occurs in its own body as a nop, which needs PartialEq on Atom/Term/Formula; left out of this change.

Other changes

relate_equal_refined_type had no callers outside this module once the recursion stopped going through it, so it is dropped from the Subtyping trait.

Testing

cargo test (334 UI tests + unit and doc tests), cargo clippy -- -D warnings, cargo fmt --check.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PEtrgExrszb8aBUEGASPeE


Generated by Claude Code

Relating two types invariantly was expressed as subtyping in both
directions, which walks the whole type structure twice. Each owning or
mutable pointer nested inside relates its referent invariantly again, so
the number of clauses emitted for a refinement doubled per pointer layer
above it: the eight identical clauses per basic block edge in a
`&mut self` method over `Option<i32>` come from three such layers.

The two directions do not derive different constraints. Relating types
invariantly is symmetric, and every position below an owning or mutable
pointer is related invariantly again, so the second direction re-derives
the constraints of the first.

Relate types in a single traversal carrying the relation to derive,
emitting both implications where the refinements must agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEtrgExrszb8aBUEGASPeE
@coord-e
coord-e force-pushed the claude/reduce-duplicate-clauses-br7c8m branch from 7d49aaf to 4415027 Compare September 2, 2026 12:39
@coord-e
coord-e requested a balanced review from Copilot September 2, 2026 12:40
@coord-e
coord-e marked this pull request as ready for review September 2, 2026 12:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The refactor preserves relation semantics while eliminating redundant recursive traversals.

Pull request overview

Optimizes invariant type relations by traversing type structures once while preserving bidirectional refinement constraints.

Changes:

  • Introduces explicit Sub and Equal relation modes.
  • Propagates invariance through owning and mutable pointers.
  • Removes the unused equality method from Subtyping.
File summaries
File Description
src/rty/subtyping.rs Consolidates subtype and equality traversal to prevent duplicated clauses.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@coord-e
coord-e merged commit d1a6a15 into main Sep 2, 2026
7 checks passed
@coord-e
coord-e deleted the claude/reduce-duplicate-clauses-br7c8m branch September 2, 2026 12:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants