Skip to content

PEP 695: type parameter bound referencing an earlier type parameter in the same list fails with "Name ... is not defined" #21922

Description

@yobuntu

Bug Report
PEP 695 explicitly allows a type parameter's bound to reference an earlier type parameter declared in the same type parameter list ("Type Parameter Scopes" section: "Type parameters are visible to other type parameters declared elsewhere in the list", with the only restriction being that an earlier parameter cannot reference a later one). Mypy rejects this with a name-defined error, both for classes and for functions, and for both the new PEP 695 bracket syntax and the classic TypeVar syntax.

To Reproduce
Minimal reproduction (class):

class Container[T]:
pass

class Foo[T, S: Container[T]]:
x: S
Minimal reproduction (function, same failure):

class Container[T]:
pass

def foo[T, S: Container[T]](x: S) -> None:
pass
Both are plain generic classes, not Protocol — the failure is not specific to protocols; the exact same error occurs when Container is a Protocol.

Expected Behavior
Both snippets should type-check cleanly. T is declared before S in the same type parameter list, and S's bound (Container[T]) only refers back to an earlier parameter, which PEP 695 explicitly permits.

For context, a bound with no parameters of its own works exactly as expected:

from typing import Sized

def get_length[T: Sized](item: T) -> int:
return len(item)

get_length("ok") # accepted
get_length(42) # error: Value of type variable "T" of "get_length" cannot be "int"
The failure only appears once the bound itself needs a type parameter (Container[T]), and that parameter must come from a sibling in the same list.

Actual Behavior

error: Name "T" is not defined [name-defined]
Your Environment
Mypy version: 2.1.0 (reproduced) and 2.3.1, the latest released version as of this report (also reproduced, installed in isolation)
Python version used: 3.13.12
Operating system: Linux
Additional context
Closely related to #20190 / #20021 (a PEP 696 default value referencing a sibling, fixed by #20021) — but that fix doesn't cover this: re-tested on 2.3.1 (which already includes it), still fails identically. #20190 is about a default value (Y=X), this one is about a bound (Y: Bound[X]) — likely the same underlying scope-resolution issue, but seemingly not fixed by the same change.

Additional information:
I asked claude to help me in the redaction of this ticket, but this is a real problem i meet in the real world, and i am very surprised this bug has not been repported yet.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions