Skip to content

Generic callable assignability accepts incompatible bounded and constrained source typevars #3114

@Hugo-Polloli

Description

@Hugo-Polloli

Summary

ty accepts invalid assignability from a generic callable value to a fully static callable target when the source generic uses bounded or constrained typevars.

This relation should require one coherent specialization of the source type variables across the whole signature. Instead, bounded and constrained source generics still fall back to the legacy relation path and can treat repeated occurrences of the same source typevar independently in parameter and return positions.

Minimal repro

from typing import Callable, TypeVar
from ty_extensions import TypeOf, is_assignable_to, static_assert

T = TypeVar("T", bound=object)

def bounded(t: T) -> T:
    return t

static_assert(is_assignable_to(TypeOf[bounded], Callable[[str], str]))
static_assert(not is_assignable_to(TypeOf[bounded], Callable[[str], int]))

Expected

The second assertion should pass. bounded can be specialized to (str) -> str, but not to (str) -> int.

Actual

On main, is_assignable_to(TypeOf[bounded], Callable[[str], int]) evaluates to True, so the negative static_assert fails.

The same bug also affects constrained source typevars, for example:

T = TypeVar("T", str, bytes)

def constrained(t: T) -> T:
    return t

static_assert(not is_assignable_to(TypeOf[constrained], Callable[[str], int]))

Related issues

Version

main as of 0ed93d2a13

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions