[ty] Do not carry the generic context of Protocol or Generic in the ClassBase enum (#17989)

## Summary

It doesn't seem to be necessary for our generics implementation to carry
the `GenericContext` in the `ClassBase` variants. Removing it simplifies
the code, fixes many TODOs about `Generic` or `Protocol` appearing
multiple times in MROs when each should only appear at most once, and
allows us to more accurately detect runtime errors that occur due to
`Generic` or `Protocol` appearing multiple times in a class's bases.

In order to remove the `GenericContext` from the `ClassBase` variant, it
turns out to be necessary to emulate
`typing._GenericAlias.__mro_entries__`, or we end up with a large number
of false-positive `inconsistent-mro` errors. This PR therefore also does
that.

Lastly, this PR fixes the inferred MROs of PEP-695 generic classes,
which implicitly inherit from `Generic` even if they have no explicit
bases.

## Test Plan

mdtests
This commit is contained in:
Alex Waygood
2025-05-22 21:37:03 -04:00
committed by GitHub
parent 6c0a59ea78
commit d02c9ada5d
15 changed files with 271 additions and 215 deletions

View File

@@ -527,6 +527,45 @@ reveal_type(unknown_object) # revealed: Unknown
reveal_type(unknown_object.__mro__) # revealed: Unknown
```
## MROs of classes that use multiple inheritance with generic aliases and subscripted `Generic`
```py
from typing import Generic, TypeVar, Iterator
T = TypeVar("T")
class peekable(Generic[T], Iterator[T]): ...
# revealed: tuple[<class 'peekable[Unknown]'>, <class 'Iterator[T]'>, <class 'Iterable[T]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(peekable.__mro__)
class peekable2(Iterator[T], Generic[T]): ...
# revealed: tuple[<class 'peekable2[Unknown]'>, <class 'Iterator[T]'>, <class 'Iterable[T]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(peekable2.__mro__)
class Base: ...
class Intermediate(Base, Generic[T]): ...
class Sub(Intermediate[T], Base): ...
# revealed: tuple[<class 'Sub[Unknown]'>, <class 'Intermediate[T]'>, <class 'Base'>, typing.Generic, <class 'object'>]
reveal_type(Sub.__mro__)
```
## Unresolvable MROs involving generics have the original bases reported in the error message, not the resolved bases
<!-- snapshot-diagnostics -->
```py
from typing_extensions import Protocol, TypeVar, Generic
T = TypeVar("T")
class Foo(Protocol): ...
class Bar(Protocol[T]): ...
class Baz(Protocol[T], Foo, Bar[T]): ... # error: [inconsistent-mro]
```
## Classes that inherit from themselves
These are invalid, but we need to be able to handle them gracefully without panicking.