Skip to content

[ty] Detect disjointness due to incompatible generic specializations#24822

Merged
carljm merged 14 commits into
mainfrom
charlie/generic-disjoint
Jun 3, 2026
Merged

[ty] Detect disjointness due to incompatible generic specializations#24822
carljm merged 14 commits into
mainfrom
charlie/generic-disjoint

Conversation

@charliermarsh

@charliermarsh charliermarsh commented Apr 24, 2026

Copy link
Copy Markdown
Member

Summary

Prior to this change, we did not recognize that generic specializations with
incompatible invariant type arguments are disjoint. A single value cannot simultaneously inhabit two specializations of an invariant generic class when their fully static arguments are not equivalent:

class Invariant[T]: ...
class A: ...
class B: ...

static_assert(is_disjoint_from(Invariant[A], Invariant[B]))

We now recognize disjointness between classes whose inherited bases contain incompatible specializations of the same invariant generic class.

(Covariant generic specializations are not treated as disjoint by this change; for example, an empty container can inhabit both Sequence[int] and Sequence[str]).

Closes astral-sh/ty#3314.

@astral-sh-bot astral-sh-bot Bot added the ty Multi-file analysis & type inference label Apr 24, 2026
@astral-sh-bot

astral-sh-bot Bot commented Apr 24, 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 Apr 24, 2026

Copy link
Copy Markdown

Memory usage report

Summary

Project Old New Diff Outcome
prefect 685.08MB 685.14MB +0.01% (61.34kB)
sphinx 252.41MB 252.45MB +0.02% (38.91kB)
trio 105.55MB 105.57MB +0.02% (26.16kB)
flake8 41.86MB 41.86MB +0.00% (696.00B)

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
infer_expression_types_impl 60.98MB 60.99MB +0.03% (19.11kB)
StaticClassLiteral<'db>::try_mro_ 5.35MB 5.36MB +0.18% (10.01kB)
infer_definition_types 89.16MB 89.17MB +0.01% (9.69kB)
infer_scope_types_impl 50.88MB 50.88MB +0.01% (5.02kB)
Specialization 2.64MB 2.64MB +0.09% (2.50kB)
StaticClassLiteral<'db>::try_mro_::interned_arguments 1.26MB 1.26MB +0.16% (2.04kB)
IntersectionType<'db>::from_two_elements_ 165.64kB 167.48kB +1.11% (1.84kB)
FunctionType 10.06MB 10.06MB +0.02% (1.69kB)
GenericAlias 1.13MB 1.13MB +0.13% (1.55kB)
infer_statement_types_impl 1.06MB 1.07MB +0.08% (936.00B)
CallableType 2.80MB 2.80MB +0.03% (928.00B)
FunctionType<'db>::signature_ 4.56MB 4.56MB +0.02% (896.00B)
Type<'db>::apply_specialization_ 3.57MB 3.57MB +0.02% (844.00B)
infer_expression_type_impl 8.45MB 8.45MB +0.01% (768.00B)
is_redundant_with_impl 2.18MB 2.18MB +0.03% (696.00B)
... 19 more

sphinx

Name Old New Diff Outcome
StaticClassLiteral<'db>::try_mro_ 2.54MB 2.55MB +0.52% (13.58kB)
infer_expression_types_impl 21.93MB 21.94MB +0.04% (9.09kB)
Specialization 1.28MB 1.29MB +0.24% (3.17kB)
StaticClassLiteral<'db>::try_mro_::interned_arguments 576.35kB 579.09kB +0.48% (2.74kB)
infer_scope_types_impl 13.43MB 13.43MB +0.02% (2.71kB)
infer_definition_types 23.39MB 23.39MB +0.01% (2.26kB)
GenericAlias 562.29kB 564.33kB +0.36% (2.04kB)
all_narrowing_constraints_for_expression 2.69MB 2.69MB +0.02% (576.00B)
is_redundant_with_impl 991.22kB 991.76kB +0.05% (552.00B)
all_negative_narrowing_constraints_for_expression 2.63MB 2.63MB +0.02% (420.00B)
IntersectionType<'db>::from_two_elements_ 107.55kB 107.86kB +0.28% (312.00B)
infer_statement_types_impl 552.81kB 553.09kB +0.05% (288.00B)
infer_expression_type_impl 3.01MB 3.01MB +0.01% (240.00B)
is_redundant_with_impl::interned_arguments 1.16MB 1.16MB +0.01% (176.00B)
IntersectionType 540.20kB 540.36kB +0.03% (168.00B)
... 8 more

trio

Name Old New Diff Outcome
StaticClassLiteral<'db>::try_mro_ 843.07kB 851.21kB +0.97% (8.14kB)
infer_expression_types_impl 6.99MB 7.00MB +0.11% (7.80kB)
infer_expression_type_impl 1.52MB 1.52MB +0.21% (3.20kB)
Specialization 466.77kB 468.52kB +0.37% (1.75kB)
StaticClassLiteral<'db>::try_mro_::interned_arguments 190.41kB 192.02kB +0.85% (1.62kB)
GenericAlias 193.57kB 194.70kB +0.58% (1.12kB)
infer_definition_types 7.55MB 7.55MB +0.01% (1.01kB)
infer_scope_types_impl 4.14MB 4.14MB +0.02% (672.00B)
loop_header_reachability 139.36kB 139.61kB +0.18% (252.00B)
overloads_and_implementation_inner 327.40kB 327.64kB +0.08% (252.00B)
all_narrowing_constraints_for_expression 702.39kB 702.50kB +0.02% (108.00B)
IntersectionType<'db>::from_two_elements_ 27.95kB 28.03kB +0.29% (84.00B)
Type<'db>::apply_specialization_ 627.93kB 627.99kB +0.01% (60.00B)
infer_deferred_types 2.11MB 2.11MB +0.00% (60.00B)
infer_statement_types_impl 55.72kB 55.76kB +0.06% (36.00B)
... 1 more

flake8

Name Old New Diff Outcome
infer_expression_types_impl 1.09MB 1.09MB +0.03% (324.00B)
infer_definition_types 1.78MB 1.78MB +0.02% (300.00B)
infer_scope_types_impl 868.22kB 868.29kB +0.01% (72.00B)

@astral-sh-bot

astral-sh-bot Bot commented Apr 24, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

Lint rule Added Removed Changed
unresolved-attribute 0 3 0
invalid-argument-type 0 0 2
type-assertion-failure 0 0 1
Total 0 3 3

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]`

pip (https://github.com/pypa/pip)
- src/pip/_vendor/urllib3/response.py:475:43 error[invalid-argument-type] Argument to `HTTPHeaderDict.__init__` is incorrect: Expected `Mapping[str, str] | Iterable[tuple[str, str]] | HasGettableStringKeys | None`, found `(Mapping[str, str] & ~HTTPHeaderDict) | (Mapping[bytes, bytes] & ~HTTPHeaderDict) | None`
+ src/pip/_vendor/urllib3/response.py:475:43 error[invalid-argument-type] Argument to `HTTPHeaderDict.__init__` is incorrect: Expected `Mapping[str, str] | Iterable[tuple[str, str]] | HasGettableStringKeys | None`, found `(Mapping[str, str] & ~HTTPHeaderDict) | Mapping[bytes, bytes] | None`

prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/server/orchestration/global_policy.py:462:13 error[unresolved-attribute] Attribute `state_details` is not defined on `None` in union `State | None`
- src/prefect/server/orchestration/global_policy.py:466:13 error[unresolved-attribute] Attribute `state_details` is not defined on `None` in union `State | None`
- src/prefect/server/orchestration/global_policy.py:467:13 error[unresolved-attribute] Attribute `state_details` is not defined on `None` in union `State | None`

urllib3 (https://github.com/urllib3/urllib3)
- src/urllib3/response.py:484:43 error[invalid-argument-type] Argument to `HTTPHeaderDict.__init__` is incorrect: Expected `Mapping[str, str] | Iterable[tuple[str, str]] | HasGettableStringKeys | None`, found `(Mapping[str, str] & ~HTTPHeaderDict) | (Mapping[bytes, bytes] & ~HTTPHeaderDict) | None`
+ src/urllib3/response.py:484:43 error[invalid-argument-type] Argument to `HTTPHeaderDict.__init__` is incorrect: Expected `Mapping[str, str] | Iterable[tuple[str, str]] | HasGettableStringKeys | None`, found `(Mapping[str, str] & ~HTTPHeaderDict) | Mapping[bytes, bytes] | None`

Full report with detailed diff (timing results)

@codspeed-hq

codspeed-hq Bot commented Apr 24, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 65 untouched benchmarks
⏩ 60 skipped benchmarks1


Comparing charlie/generic-disjoint (5d1cdab) with main (9f7591d)

Open in CodSpeed

Footnotes

  1. 60 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@charliermarsh charliermarsh force-pushed the charlie/generic-disjoint branch 6 times, most recently from e59076e to 07ec5a7 Compare April 26, 2026 00:36
@charliermarsh charliermarsh force-pushed the charlie/generic-disjoint branch 2 times, most recently from 4096f1c to ff5eb8e Compare May 2, 2026 12:42
@charliermarsh charliermarsh force-pushed the charlie/generic-disjoint branch 3 times, most recently from d2714f9 to d4595d9 Compare May 23, 2026 22:58
@charliermarsh charliermarsh changed the title [ty] View ecosystem results for generic disjointness [ty] Detect disjointness due to incompatible generic specializations May 23, 2026
@charliermarsh charliermarsh marked this pull request as ready for review May 24, 2026 21:19
@astral-sh-bot astral-sh-bot Bot requested a review from oconnor663 May 24, 2026 21:19
@charliermarsh charliermarsh assigned carljm and unassigned oconnor663 May 24, 2026
@charliermarsh charliermarsh force-pushed the charlie/generic-disjoint branch 2 times, most recently from fd77244 to 19e87fd Compare May 26, 2026 21:37
@charliermarsh charliermarsh marked this pull request as draft May 26, 2026 21:51
@charliermarsh charliermarsh force-pushed the charlie/generic-disjoint branch 6 times, most recently from 73769bc to 9a2ce88 Compare May 27, 2026 14:00
@charliermarsh charliermarsh force-pushed the charlie/generic-disjoint branch 2 times, most recently from 38f6527 to 946ed20 Compare May 28, 2026 08:58
@charliermarsh charliermarsh force-pushed the charlie/generic-disjoint branch from 946ed20 to 03ad757 Compare May 28, 2026 09:07
@charliermarsh charliermarsh marked this pull request as ready for review May 28, 2026 09:08

@carljm carljm 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.

I tried something a bit different here -- rather than making review comments, I iterated with Codex on the needed changes directly, and then put them up as a stacked PR, with comments explaining what I did and why: #25583

Take a look at the stacked PR and comments, and merge it into this PR if it looks good to you. Once that's done, this PR is good to merge as far as I'm concerned.

@carljm carljm merged commit 1a34347 into main Jun 3, 2026
58 checks passed
@carljm carljm deleted the charlie/generic-disjoint branch June 3, 2026 02:59
carljm added a commit that referenced this pull request Jun 3, 2026
* origin/main: (114 commits)
  [ty] Detect disjointness due to incompatible generic specializations (#24822)
  Update Rust crate memchr to v2.8.1 (#25586)
  [ty] Reject Self in type aliases (#25529)
  Update Rust crate log to v0.4.30 (#25572)
  Update Rust crate mimalloc to v0.1.52 (#25573)
  Update Rust crate similar to v3.1.1 (#25575)
  Update docker/setup-buildx-action action to v4.1.0 (#25580)
  Update docker/metadata-action action to v6.1.0 (#25579)
  Update docker/login-action action to v4.2.0 (#25578)
  Update docker/build-push-action action to v7.2.0 (#25577)
  Update taiki-e/install-action action to v2.79.9 (#25576)
  Update Rust crate serde_json to v1.0.150 (#25574)
  Update Rust crate assert_fs to v1.1.4 (#25570)
  Update prek dependencies (#25569)
  Update dependency ruff to v0.15.15 (#25568)
  Update dependency pyright to v1.1.410 (#25566)
  Update dependency astral-sh/uv to v0.11.18 (#25565)
  [ty] don't needlessly disambiguate same type alias (#25563)
  [ty] fix variance inference for nested type aliases (#25567)
  [ty] treat union-bound typevars like unions for possibly-missing-attribute (#25561)
  ...

# Conflicts:
#	crates/ty_python_semantic/src/types/class.rs
#	crates/ty_python_semantic/src/types/generics.rs
#	crates/ty_python_semantic/src/types/type_alias.rs
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.

detect disjointness due to incompatible generic specializations (including generic bases)

3 participants