Skip to content

[ty] Refactor relation.rs to store state on a struct rather than passing around 7 arguments every time we recurse#23837

Merged
AlexWaygood merged 1 commit intomainfrom
alex/relation-visitation
Mar 13, 2026
Merged

[ty] Refactor relation.rs to store state on a struct rather than passing around 7 arguments every time we recurse#23837
AlexWaygood merged 1 commit intomainfrom
alex/relation-visitation

Conversation

@AlexWaygood
Copy link
Member

@AlexWaygood AlexWaygood commented Mar 9, 2026

Summary

What the title says. Currently all our recursive calls to has_relation_to_impl look like this, which is very verbose, very repetitive, and not very readable:

        inner_type.has_relation_to_impl(
            db,
            target,
            constraints,
            inferable,
            relation,
            relation_visitor,
            disjointness_visitor,
        )

This PR moves the central recursive match in Type::has_relation_to_impl to a method on a new struct, TypeRelationChecker struct. TypeRelationChecker stores constraints, inferable, relation, relation_visitor and disjointness_visitor as fields, so these no longer need to be passed as arguments. Instead, the typical recursive call now looks like this:

        self.check_type_pair(db, inner_type, target)

which is much more concise :-)

Instead of having many ::has_relation_to_impl methods scattered across the codebase on various types, we now instead have lots of methods on TypeRelationChecker (still scattered across various modules): FunctionType::has_relation_to_impl becomes TypeRelationChecker::check_function_pair; BoundMethodType::has_relation_to_impl becomes TypeRelationChecker::check_method_pair; etc.

I've also done the same for Type::is_disjoint_from_impl.

Test Plan

Existing tests. This should be a pure refactor with no semantic changes.

@AlexWaygood AlexWaygood added internal An internal refactor or improvement ty Multi-file analysis & type inference labels Mar 9, 2026
@astral-sh-bot
Copy link

astral-sh-bot bot commented Mar 9, 2026

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 85.29%. The percentage of expected errors that received a diagnostic held steady at 78.13%. The number of fully passing files held steady at 64/132.

@astral-sh-bot
Copy link

astral-sh-bot bot commented Mar 9, 2026

Memory usage report

Summary

Project Old New Diff Outcome
sphinx 265.23MB 265.25MB +0.01% (14.63kB)
flake8 47.89MB 47.89MB +0.00% (1020.00B)
trio 117.75MB 117.75MB +0.00% (228.00B)
prefect 705.15MB 705.14MB -0.00% (9.83kB) ⬇️

Significant changes

Click to expand detailed breakdown

sphinx

Name Old New Diff Outcome
infer_definition_types 24.03MB 24.03MB +0.02% (4.45kB)
infer_expression_types_impl 21.51MB 21.52MB +0.02% (3.96kB)
infer_expression_type_impl 3.21MB 3.21MB +0.06% (2.05kB)
StaticClassLiteral<'db>::try_mro_ 2.11MB 2.11MB +0.08% (1.75kB)
infer_scope_types_impl 15.58MB 15.58MB +0.00% (552.00B)
loop_header_reachability 380.67kB 381.17kB +0.13% (516.00B)
Specialization 1.02MB 1.02MB +0.04% (448.00B)
StaticClassLiteral<'db>::try_mro_::interned_arguments 487.62kB 487.97kB +0.07% (360.00B)
GenericAlias 452.67kB 452.95kB +0.06% (288.00B)
all_narrowing_constraints_for_expression 2.34MB 2.34MB +0.01% (180.00B)
Type<'db>::member_lookup_with_policy_ 6.10MB 6.10MB +0.00% (120.00B)
is_redundant_with_impl::interned_arguments 2.07MB 2.07MB -0.00% (88.00B)
infer_unpack_types 446.19kB 446.25kB +0.01% (60.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_ 2.37MB 2.37MB +0.00% (60.00B)
is_redundant_with_impl 1.81MB 1.81MB -0.00% (24.00B)

flake8

Name Old New Diff Outcome
infer_definition_types 1.86MB 1.86MB +0.02% (300.00B)
infer_expression_types_impl 1.07MB 1.07MB +0.03% (300.00B)
infer_expression_type_impl 157.13kB 157.30kB +0.11% (180.00B)
infer_unpack_types 37.78kB 37.84kB +0.16% (60.00B)
all_narrowing_constraints_for_expression 82.51kB 82.57kB +0.07% (60.00B)
infer_scope_types_impl 1002.15kB 1002.21kB +0.01% (60.00B)
loop_header_reachability 13.66kB 13.72kB +0.43% (60.00B)

trio

Name Old New Diff Outcome
infer_expression_types_impl 7.06MB 7.06MB +0.00% (192.00B)
infer_definition_types 7.57MB 7.57MB +0.00% (36.00B)

prefect

Name Old New Diff Outcome
StaticClassLiteral<'db>::try_mro_ 6.07MB 6.06MB -0.05% (3.17kB) ⬇️
infer_expression_types_impl 60.78MB 60.77MB -0.00% (1.44kB) ⬇️
infer_expression_type_impl 14.35MB 14.34MB -0.01% (1.17kB) ⬇️
StaticClassLiteral<'db>::implicit_attribute_inner_ 9.74MB 9.74MB -0.01% (1.17kB) ⬇️
infer_definition_types 88.88MB 88.88MB -0.00% (1.08kB) ⬇️
Type<'db>::member_lookup_with_policy_ 15.41MB 15.41MB -0.01% (964.00B) ⬇️
is_redundant_with_impl 5.57MB 5.57MB +0.01% (780.00B) ⬇️
Specialization 2.52MB 2.51MB -0.03% (752.00B) ⬇️
StaticClassLiteral<'db>::try_mro_::interned_arguments 1.40MB 1.40MB -0.04% (648.00B) ⬇️
GenericAlias 1.17MB 1.17MB -0.04% (504.00B) ⬇️
Type<'db>::class_member_with_policy_ 17.32MB 17.32MB -0.00% (432.00B) ⬇️
infer_scope_types_impl 52.93MB 52.93MB +0.00% (360.00B) ⬇️
is_redundant_with_impl::interned_arguments 5.36MB 5.36MB +0.00% (176.00B) ⬇️
UnionType 3.46MB 3.46MB +0.00% (128.00B) ⬇️
IntersectionType<'db>::from_two_elements_ 341.10kB 341.20kB +0.03% (104.00B) ⬇️
... 7 more

@astral-sh-bot
Copy link

astral-sh-bot bot commented Mar 9, 2026

mypy_primer results

No ecosystem changes detected ✅

@AlexWaygood AlexWaygood marked this pull request as ready for review March 9, 2026 15:19
@AlexWaygood
Copy link
Member Author

(I don't love the name HasRelationToDecider, but the term "visitor" is already taken/very overloaded in this codebase...)

@carljm
Copy link
Contributor

carljm commented Mar 9, 2026

I think this is an improvement. Really just naming thoughts:

HasRelationToDecider is a mouthful, and visit_type_pair doesn't feel quite right as the method name; it's not a "visit" in the typical visitor-pattern sense.

Personally I like the idea of naming the struct just TypeRelation (relegating the existing enum by that name to TypeRelationKind) and naming the method holds. As in relation = TypeRelation(...); relation.holds(...).

In some structural cases we descend into methods on other structs (like protocols or signatures), and it looks like currently we "unpack" the new struct into multiple arguments again when we do that. Seems like those methods could just take the decider/relation struct instead? Maybe better split into follow-up to keep the diff smaller, I guess.

@AlexWaygood
Copy link
Member Author

In some structural cases we descend into methods on other structs (like protocols or signatures), and it looks like currently we "unpack" the new struct into multiple arguments again when we do that. Seems like those methods could just take the decider/relation struct instead? Maybe better split into follow-up to keep the diff smaller, I guess.

Yeah, as I said in the PR description:

If we like this refactor, this approach can be extended further: Signature::has_relation_to_impl method could be replaced with a HasRelationToDecider::visit_signature_pair method, etc. -- but for now, this seems like a good checkpoint to see what folks think of the overall idea.

which is partly why I went with the visit_* name -- if we extended this approach, then the "Decider" wouldn't just visit pairs of types, it would also visit pairs of CallableTypes, of NominalInstanceTypes, of FunctionTypes, etc. -- every FooType::has_relation_to_impl method would instead become a visit_foo_pair method on the decider. (Not all methods would need to be implemented in relation.rs, of course -- we could have them split across lots of modules in the same way we do today.)

@carljm
Copy link
Contributor

carljm commented Mar 9, 2026

If we do the extended refactor that way (moving all the methods to the decider struct), another method name could be check (relation.check(typ1, typ2)), which lends itself to check_signature etc.

@AlexWaygood
Copy link
Member Author

that works, I can make that change!

Copy link
Member

@dcreager dcreager left a comment

Choose a reason for hiding this comment

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

Ah, I see now that you only meant you're done introducing merge conflicts that are due to splitting up large files! 😂

j/k, I think this is a great refactoring

@carljm carljm removed their request for review March 9, 2026 23:44
@AlexWaygood AlexWaygood force-pushed the alex/relation-visitation branch from f336b5b to 317bcc0 Compare March 10, 2026 01:18
@AlexWaygood AlexWaygood marked this pull request as draft March 10, 2026 01:25
@AlexWaygood AlexWaygood force-pushed the alex/relation-visitation branch 9 times, most recently from 21a7117 to 76306b2 Compare March 10, 2026 17:50
@AlexWaygood AlexWaygood force-pushed the alex/relation-visitation branch 2 times, most recently from 598c479 to c3732ad Compare March 10, 2026 21:38
@AlexWaygood
Copy link
Member Author

OK... I think this is roughly where I would want it to be?

@AlexWaygood AlexWaygood marked this pull request as ready for review March 10, 2026 21:39
@AlexWaygood AlexWaygood force-pushed the alex/relation-visitation branch from c3732ad to fcb3c07 Compare March 10, 2026 21:39
@AlexWaygood AlexWaygood requested a review from dcreager March 10, 2026 23:14
@AlexWaygood AlexWaygood force-pushed the alex/relation-visitation branch 3 times, most recently from 19003c2 to df76e32 Compare March 13, 2026 12:06
Copy link
Member

@charliermarsh charliermarsh left a comment

Choose a reason for hiding this comment

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

I read the summary closely which makes sense to me. I only skimmed the changes but assuming they match the summary, LGTM.

@AlexWaygood AlexWaygood force-pushed the alex/relation-visitation branch 2 times, most recently from fe6244a to 8b7feb8 Compare March 13, 2026 13:53
@AlexWaygood AlexWaygood enabled auto-merge (squash) March 13, 2026 13:55
@AlexWaygood AlexWaygood force-pushed the alex/relation-visitation branch from 8b7feb8 to 6e0953f Compare March 13, 2026 13:55
@AlexWaygood AlexWaygood disabled auto-merge March 13, 2026 13:57
…ssing around 7 arguments every time we recurse
@AlexWaygood AlexWaygood force-pushed the alex/relation-visitation branch from 6e0953f to 79bee72 Compare March 13, 2026 14:02
@AlexWaygood AlexWaygood enabled auto-merge (squash) March 13, 2026 14:02
@AlexWaygood AlexWaygood merged commit 2effb77 into main Mar 13, 2026
50 checks passed
@AlexWaygood AlexWaygood deleted the alex/relation-visitation branch March 13, 2026 14:09
carljm added a commit that referenced this pull request Mar 13, 2026
* main: (94 commits)
  Fix shell injection via `shell=True` in subprocess calls (#23894)
  [ty] Refactor `relation.rs` to store state on a struct rather than passing around 7 arguments every time we recurse (#23837)
  Don't return code actions for non-Python documents (#23905)
  [ty] Make the default database truly statically infallible (#23929)
  [ty] Add `Download` button to ty playground which creates a zip export (#23478)
  [ty] Respect `kw_only` overwrites in dataclasses (#23930)
  [ty] Clarify in diagnostics that `from __future__ import annotations` only stringifies type annotations (#23928)
  [ty]  Add a `Copy Markdown` button to playground (#23002)
  [ty] Fix folding range classification of lines starting with `#` (#23831)
  [ty] Fix folding ranges for notebooks (#23830)
  [ty] fix too-many-cycle panics when inferring literal type loop variables (#23875)
  Add `RegularCallableTypeOf` and `into_regular_callable` in `ty_extensions` (#23909)
  [ty] treat properties as full structural types (#23925)
  [ty] Avoid duplicated work during multi-inference (#23923)
  [ty]: make `possibly-missing-attribute` ignored by default
  [ty]: split out `possibly-missing-submodule` from `possibly-missing-attribute`
  Update astral-sh/setup-uv action to v7.5.0 (#23922)
  [ty] Show truthiness in ConstraintSet display and simplify falsy error message (#23913)
  Bump 0.15.6 (#23919)
  [ty] Narrow type context during collection literal inference (#23844)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal An internal refactor or improvement ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants