Skip to content

Consider separate Rust types and Type enum variants for bound vs unbound type variables #926

@dcreager

Description

@dcreager

"Bind" can confusingly mean ~3 different things when talking about Python type variables. Here I mean that a legacy typevar is defined before it is used (or "bound"), and that it can be used/bound multiple times:

from typing import Generic, TypeVar

T = TypeVar("T")                                   # [1]

def first_binding(t: T) -> T: ...                  # [2]

class SecondBinding(Generic[T]):                   # [3]
    def not_a_new_binding(self, t: T) -> T: ...

As of astral-sh/ruff#19604, we create a TypeVarInstance for the legacy typevar when it is first defined ([1]), and then additional distinct TypeVarInstances each time it is used/bound ([2] and [3]).

@MichaReiser suggested trying to use separate Rust types to track the unbound and bound typevars. This turned out to be a beefy enough change that we decided not to fold it into astral-sh/ruff#19604, but it is still worth considering as follow-on work. The hope is that this would bring a performance win, since TypeVarInstance is a salsa-interned struct that we are now creating many more instances of. (We would presumably not have salsa track or intern the proposed BoundTypeVarInstance.)

Note that it would require separate Type variants for bound and unbound typevars, since there are places where we need to refer to (the type of) the legacy typevar before it has been bound:

from typing import Generic, TypeVar

T = TypeVar("T")
U = TypeVar("U", default=T)

Metadata

Metadata

Assignees

No one assigned

    Labels

    genericsBugs or features relating to ty's generics implementation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions