ruff/crates/ty_python_semantic/resources/mdtest
Carl Meyer 5a570c8e6d
[ty] fix deferred name loading in PEP695 generic classes/functions (#19888)
## Summary

For PEP 695 generic functions and classes, there is an extra "type
params scope" (a child of the outer scope, and wrapping the body scope)
in which the type parameters are defined; class bases and function
parameter/return annotations are resolved in that type-params scope.

This PR fixes some longstanding bugs in how we resolve name loads from
inside these PEP 695 type parameter scopes, and also defers type
inference of PEP 695 typevar bounds/constraints/default, so we can
handle cycles without panicking.

We were previously treating these type-param scopes as lazy nested
scopes, which is wrong. In fact they are eager nested scopes; the class
`C` here inherits `int`, not `str`, and previously we got that wrong:

```py
Base = int

class C[T](Base): ...

Base = str
```

But certain syntactic positions within type param scopes (typevar
bounds/constraints/defaults) are lazy at runtime, and we should use
deferred name resolution for them. This also means they can have cycles;
in order to handle that without panicking in type inference, we need to
actually defer their type inference until after we have constructed the
`TypeVarInstance`.

PEP 695 does specify that typevar bounds and constraints cannot be
generic, and that typevar defaults can only reference prior typevars,
not later ones. This reduces the scope of (valid from the type-system
perspective) cycles somewhat, although cycles are still possible (e.g.
`class C[T: list[C]]`). And this is a type-system-only restriction; from
the runtime perspective an "invalid" case like `class C[T: T]` actually
works fine.

I debated whether to implement the PEP 695 restrictions as a way to
avoid some cycles up-front, but I ended up deciding against that; I'd
rather model the runtime name-resolution semantics accurately, and
implement the PEP 695 restrictions as a separate diagnostic on top.
(This PR doesn't yet implement those diagnostics, thus some `# TODO:
error` in the added tests.)

Introducing the possibility of cyclic typevars made typevar display
potentially stack overflow. For now I've handled this by simply removing
typevar details (bounds/constraints/default) from typevar display. This
impacts display of two kinds of types. If you `reveal_type(T)` on an
unbound `T` you now get just `typing.TypeVar` instead of
`typing.TypeVar("T", ...)` where `...` is the bound/constraints/default.
This matches pyright and mypy; pyrefly uses `type[TypeVar[T]]` which
seems a bit confusing, but does include the name. (We could easily
include the name without cycle issues, if there's a syntax we like for
that.)

It also means that displaying a generic function type like `def f[T:
int](x: T) -> T: ...` now displays as `f[T](x: T) -> T` instead of `f[T:
int](x: T) -> T`. This matches pyright and pyrefly; mypy does include
bound/constraints/defaults of typevars in function/callable type
display. If we wanted to add this, we would either need to thread a
visitor through all the type display code, or add a `decycle` type
transformation that replaced recursive reoccurrence of a type with a
marker.

## Test Plan

Added mdtests and modified existing tests to improve their correctness.

After this PR, there's only a single remaining py-fuzzer seed in the
0-500 range that panics! (Before this PR, there were 10; the fuzzer
likes to generate cyclic PEP 695 syntax.)

## Ecosystem report

It's all just the changes to `TypeVar` display.
2025-08-13 15:51:59 -07:00
..
annotations [ty] simplify return type of place_from_declarations (#19884) 2025-08-13 14:17:08 +00:00
assignment [ty] Return Option<TupleType> from infer_tuple_type_expression (#19735) 2025-08-04 13:48:19 +01:00
binary [ty] Remove special casing for string-literal-in-tuple __contains__ (#19642) 2025-07-31 11:28:03 +01:00
boolean Revert "[ty] Better control flow for boolean expressions that are inside if (#18010)" (#18150) 2025-05-17 08:27:32 -04:00
boundness_declaredness
call [ty] Remove Type::Tuple (#19669) 2025-08-11 22:03:32 +01:00
class
comparison [ty] Enum literal types (#19328) 2025-07-15 21:31:53 +02:00
comprehensions [ty] Async for loops and async iterables (#19634) 2025-07-30 17:40:24 +02:00
conditional [ty] Support as-patterns in reachability analysis (#19728) 2025-08-04 20:13:50 +02:00
dataclasses [ty] Add basic support for dataclasses.field (#19553) 2025-07-25 14:56:04 +02:00
declaration [ty] Format conflicting types as an enumeration (#18956) 2025-06-26 14:29:33 +02:00
diagnostics [ty] simplify return type of place_from_declarations (#19884) 2025-08-13 14:17:08 +00:00
directives [ty] Exhaustiveness checking & reachability for match statements (#19508) 2025-07-23 22:45:45 +02:00
doc ty_python_semantic: add union type context to function call type errors 2025-05-09 13:40:51 -04:00
exception [ty] Use separate Rust types for bound and unbound type variables (#19796) 2025-08-11 15:29:58 -04:00
expression [ty] Support async/await, async with and yield from (#19595) 2025-07-30 11:51:21 +02:00
function [ty] Short circuit ReachabilityConstraints::analyze_single for dynamic types (#19867) 2025-08-11 21:58:34 +02:00
generics [ty] fix deferred name loading in PEP695 generic classes/functions (#19888) 2025-08-13 15:51:59 -07:00
ide_support [ty] Fix attribute access on TypedDicts (#19758) 2025-08-05 13:59:10 +02:00
import [ty] Implement module-level __getattr__ support (#19791) 2025-08-08 10:39:37 -07:00
literal [ty] Understand classes that inherit from subscripted Protocol[] as generic (#17832) 2025-05-09 17:39:15 +01:00
loops [ty] Async for loops and async iterables (#19634) 2025-07-30 17:40:24 +02:00
narrow [ty] more cases for the class body global fallback 2025-08-07 17:30:27 -07:00
regression
scopes [ty] fix deferred name loading in PEP695 generic classes/functions (#19888) 2025-08-13 15:51:59 -07:00
shadowing
snapshots [ty] fix deferred name loading in PEP695 generic classes/functions (#19888) 2025-08-13 15:51:59 -07:00
stubs [ty] Do not carry the generic context of Protocol or Generic in the ClassBase enum (#17989) 2025-05-22 21:37:03 -04:00
subscript [ty] Improve sys.version_info special casing (#19894) 2025-08-13 14:39:13 +01:00
suppressions [ty] Consistent use of American english (in rules) (#19488) 2025-07-22 16:10:38 +02:00
type_compendium [ty] Remove Type::Tuple (#19669) 2025-08-11 22:03:32 +01:00
type_of [ty] Improve the Display for generic type[] types (#19667) 2025-07-31 19:45:01 +01:00
type_properties [ty] Remove Type::Tuple (#19669) 2025-08-11 22:03:32 +01:00
type_qualifiers [ty] Keep track of type qualifiers in stub declarations without right-hand side (#19756) 2025-08-05 12:07:05 +02:00
unary
with [ty] Diagnostics for async context managers (#19704) 2025-08-05 07:41:37 -07:00
.mdformat.toml
async.md [ty] Support async/await, async with and yield from (#19595) 2025-07-30 11:51:21 +02:00
attributes.md [ty] Infer type[tuple[int, str]] as the meta-type of tuple[int, str] (#19741) 2025-08-04 13:10:47 +00:00
cycle.md [ty] Add cycle handling for unpacking targets (#18078) 2025-05-13 21:27:48 +00:00
decorators.md ty_python_semantic: add union type context to function call type errors 2025-05-09 13:40:51 -04:00
del.md [ty] Support __setitem__ and improve __getitem__ related diagnostics (#19578) 2025-08-01 09:23:27 +02:00
deprecated.md [ty] Consistent use of American english (in rules) (#19488) 2025-07-22 16:10:38 +02:00
descriptor_protocol.md [ty] Fix descriptor lookups for most types that overlap with None (#19120) 2025-07-05 19:34:23 +01:00
enums.md [ty] Exhaustiveness checking & reachability for match statements (#19508) 2025-07-23 22:45:45 +02:00
exhaustiveness_checking.md [ty] Support as-patterns in reachability analysis (#19728) 2025-08-04 20:13:50 +02:00
final.md
instance_layout_conflict.md [ty] Remove special casing for tuple addition (#19636) 2025-07-30 16:25:42 +00:00
intersection_types.md [ty] Expansion of enums into unions of literals (#19382) 2025-07-21 19:37:55 +02:00
invalid_syntax.md
known_constants.md
mdtest_config.md
mdtest_custom_typeshed.md [ty] Remove Type::Tuple (#19669) 2025-08-11 22:03:32 +01:00
metaclass.md
mro.md [ty] Track different uses of legacy typevars, including context when rendering typevars (#19604) 2025-08-01 12:20:32 -04:00
named_tuple.md [ty] Add precise inference for indexing, slicing and unpacking NamedTuple instances (#19560) 2025-08-13 15:19:44 +00:00
overloads.md [ty] Track different uses of legacy typevars, including context when rendering typevars (#19604) 2025-08-01 12:20:32 -04:00
pep695_type_aliases.md [ty] support recursive type aliases (#19805) 2025-08-12 09:03:10 -07:00
properties.md ty_python_semantic: add union type context to function call type errors 2025-05-09 13:40:51 -04:00
protocols.md [ty] fix deferred name loading in PEP695 generic classes/functions (#19888) 2025-08-13 15:51:59 -07:00
public_types.md [ty] improve lazy scope place lookup (#19321) 2025-07-25 07:11:11 +00:00
statically_known_branches.md [ty] Infer nonlocal types as unions of all reachable bindings (#18750) 2025-06-26 12:24:40 +02:00
sys_platform.md
sys_version_info.md
terminal_statements.md [ty] improve lazy scope place lookup (#19321) 2025-07-25 07:11:11 +00:00
ty_extensions.md [ty] Rename type_api => ty_extensions (#19523) 2025-07-24 08:24:26 +00:00
typed_dict.md [ty] Validate writes to TypedDict keys (#19782) 2025-08-06 15:19:13 -07:00
union_types.md [ty] Expansion of enums into unions of literals (#19382) 2025-07-21 19:37:55 +02:00
unpacking.md [ty] Remove Type::Tuple (#19669) 2025-08-11 22:03:32 +01:00
unreachable.md [ty] improve lazy scope place lookup (#19321) 2025-07-25 07:11:11 +00:00