Skip to content

[ty] Fix invariant generic disjointness via materialization overlap#25583

Merged
charliermarsh merged 2 commits into
charlie/generic-disjointfrom
pr-24822
Jun 3, 2026
Merged

[ty] Fix invariant generic disjointness via materialization overlap#25583
charliermarsh merged 2 commits into
charlie/generic-disjointfrom
pr-24822

Conversation

@carljm

@carljm carljm commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@astral-sh-bot astral-sh-bot Bot added the ty Multi-file analysis & type inference label Jun 3, 2026
@astral-sh-bot astral-sh-bot Bot changed the title Fix invariant generic disjointness via materialization overlap [ty] Fix invariant generic disjointness via materialization overlap Jun 3, 2026
@astral-sh-bot

astral-sh-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 91.94%. The percentage of expected errors that received a diagnostic held steady at 87.09%. The number of fully passing files held steady at 92/134.

@astral-sh-bot

astral-sh-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown

Memory usage report

Summary

Project Old New Diff Outcome
flake8 46.81MB 46.81MB -
trio 113.82MB 113.82MB -0.00% (960.00B) ⬇️
sphinx 267.51MB 267.50MB -0.00% (1.92kB) ⬇️
prefect 725.34MB 725.33MB -0.00% (15.84kB) ⬇️

Significant changes

Click to expand detailed breakdown

trio

Name Old New Diff Outcome
Type<'db>::expand_eagerly__::interned_arguments 3.80kB 3.38kB -11.11% (432.00B) ⬇️
Type<'db>::expand_eagerly__ 3.71kB 3.36kB -9.46% (360.00B) ⬇️
infer_expression_types_impl 6.75MB 6.75MB -0.00% (144.00B) ⬇️
infer_scope_types_impl 4.14MB 4.14MB -0.00% (48.00B) ⬇️
IntersectionType<'db>::from_two_elements_ 27.95kB 27.97kB +0.08% (24.00B) ⬇️

sphinx

Name Old New Diff Outcome
Type<'db>::expand_eagerly__::interned_arguments 10.69kB 9.91kB -7.24% (792.00B) ⬇️
Type<'db>::expand_eagerly__ 12.47kB 11.76kB -5.67% (724.00B) ⬇️
infer_expression_types_impl 21.95MB 21.95MB -0.00% (648.00B) ⬇️
infer_scope_types_impl 13.48MB 13.48MB -0.00% (576.00B) ⬇️
is_redundant_with_impl 992.41kB 992.62kB +0.02% (216.00B) ⬇️
StaticClassLiteral<'db>::try_mro_ 2.55MB 2.55MB +0.01% (196.00B) ⬇️
is_redundant_with_impl::interned_arguments 1.16MB 1.16MB +0.01% (176.00B) ⬇️
IntersectionType 533.04kB 533.20kB +0.03% (168.00B) ⬇️
infer_statement_types_impl 565.54kB 565.37kB -0.03% (168.00B) ⬇️
UnionType 647.42kB 647.53kB +0.02% (112.00B) ⬇️
Specialization 1.28MB 1.28MB +0.01% (112.00B) ⬇️
all_narrowing_constraints_for_expression 2.60MB 2.60MB -0.00% (96.00B) ⬇️
TypeVarInstance 175.88kB 175.97kB +0.05% (96.00B) ⬇️
IntersectionType<'db>::from_two_elements_ 107.87kB 107.78kB -0.09% (96.00B) ⬇️
all_negative_narrowing_constraints_for_expression 2.54MB 2.54MB -0.00% (96.00B) ⬇️
... 4 more

prefect

Name Old New Diff Outcome
infer_expression_types_impl 60.65MB 60.65MB -0.01% (5.25kB) ⬇️
Type<'db>::expand_eagerly__::interned_arguments 14.77kB 11.04kB -25.24% (3.73kB) ⬇️
Type<'db>::expand_eagerly__ 12.56kB 9.51kB -24.28% (3.05kB) ⬇️
Type<'db>::try_call_dunder_get_ 11.62MB 11.61MB -0.02% (2.77kB) ⬇️
infer_definition_types 90.13MB 90.13MB -0.00% (1.96kB) ⬇️
FunctionType 10.05MB 10.05MB +0.01% (864.00B) ⬇️
infer_scope_types_impl 51.08MB 51.08MB -0.00% (768.00B) ⬇️
infer_statement_types_impl 1.08MB 1.08MB -0.05% (528.00B) ⬇️
FunctionType<'db>::signature_ 4.55MB 4.55MB +0.01% (448.00B) ⬇️
CallableType 2.80MB 2.80MB -0.01% (304.00B) ⬇️
cached_protocol_interface 539.91kB 540.17kB +0.05% (264.00B) ⬇️
Type<'db>::apply_specialization_ 3.56MB 3.56MB +0.01% (244.00B) ⬇️
StaticClassLiteral<'db>::try_mro_ 5.36MB 5.36MB +0.00% (212.00B) ⬇️
IntersectionType<'db>::from_two_elements_ 166.77kB 166.59kB -0.11% (192.00B) ⬇️
ProtocolInterface 304.54kB 304.72kB +0.06% (184.00B) ⬇️
... 12 more

@astral-sh-bot

astral-sh-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

Lint rule Added Removed Changed
type-assertion-failure 0 0 1
Total 0 0 1

Raw diff:

pandas-stubs (https://github.com/pandas-dev/pandas-stubs)
- tests/test_api_types.py:372:9 error[type-assertion-failure] Type `Categorical[str]` does not match asserted type `Categorical[int]`
+ tests/test_api_types.py:372:9 error[type-assertion-failure] Type `Categorical[Unknown]` does not match asserted type `Categorical[int]`

Full report with detailed diff (timing results)

Comment on lines +184 to +187
static_assert(is_disjoint_from(Invariant[A | Any], Invariant[B]))
static_assert(is_disjoint_from(Invariant[B], Invariant[A | Any]))
static_assert(is_disjoint_from(Invariant[Intersection[A, Any]], Invariant[B]))
static_assert(is_disjoint_from(Invariant[B], Invariant[Intersection[A, Any]]))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The previous "must be fully static" guard did not handle these cases -- but we know these are disjoint.

static_assert(not is_disjoint_from(Invariant[Id[U]], Invariant[int]))

static_assert(not is_disjoint_from(Invariant[Id[int]], Invariant[int]))
static_assert(is_disjoint_from(Invariant[Id[int]], Invariant[str]))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The previous "must be fully static" guard also failed this case, because it didn't properly expand the generic typevar (expand_eagerly uses raw_value_type and throws away generics).

Comment on lines -1294 to -1300
&& self_alias
.specialization(db)
.generic_context(db)
.variables(db)
.any(|typevar| {
matches!(typevar.variance(db), TypeVarVariance::Invariant)
})

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This guard isn't needed for correctness; it's redundant with the behavior of specialization disjointness, which already handles the different variances correctly.

If this is here as a perf optimization (and you've already demonstrated that it's needed to avoid regression) then we can keep it.

Comment on lines +1676 to +1685
// `Bottom[L] <: Top[R]` asks whether the materialization ranges for `L`
// and `R` have any common materialization, so this is symmetric despite
// using a directional subtyping checker.
self.as_relation_checker(TypeRelation::Subtyping)
.check_subtyping_in_invariant_position(
db,
left_type,
MaterializationKind::Bottom,
right_type,
MaterializationKind::Top,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is the correct handling of gradual types here, which lets us still detect disjointness in the case of gradual types that can't possibly materialize to the same type.

db: &'db dyn Db,
other: Self,
constraints: &ConstraintSetBuilder<'db>,
) -> bool {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

All these changes are just threading through a common disjointness checker so we maintain our cycle guards when looking at cyclic MRO graphs. (It's probably clearest to look at the two commits in this PR separately: the first commit is just the semantic changes we need, the second looks more invasive but it's just threading things through to fix the stack overflow, no semantic change.)

@charliermarsh charliermarsh marked this pull request as ready for review June 3, 2026 02:46
@charliermarsh charliermarsh merged commit 5d1cdab into charlie/generic-disjoint Jun 3, 2026
59 checks passed
@charliermarsh charliermarsh deleted the pr-24822 branch June 3, 2026 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants