pyrefly icon indicating copy to clipboard operation
pyrefly copied to clipboard

Validate typevar instantiation against constraints and bounds

Open grievejia opened this issue 10 months ago • 1 comments

Minimal repro:

def foo[T: str](x: T) -> T: ...
def bar[T: (str, bytes)](x: T) -> T: ...

def test() -> None:
    foo(42)
    bar(42)

Expected: Both foo(42) and bar(42) should be invalid -- the callees have already declared that they only accept bytes/str. Actual: No type error

grievejia avatar Apr 16 '25 23:04 grievejia

Ah, this was in the diff I had a few weeks back that got reverted because of deadlock

yangdanny97 avatar Apr 17 '25 03:04 yangdanny97

Some more tests -- we need to check on instantiation too, not just type application.

from typing import *

@overload
def foo[T: int](x: T) -> T: ...
@overload
def foo(x: object) -> object: ...
def foo(x: object) -> object:
    return x

assert_type(foo(0), int)
assert_type(foo(True), bool)
assert_type(foo("foo"), object) # should pass

def bar[T: int](x: T) -> T:
    return x
bar("foo") # should error

class C[T: int]:
    pass
x: C[str] = C() # should error

Another fun(?) case -- Variable::Contained should remember bounds.

class C[T: int]:
    def __init__(self) -> None: ...
    def set(self, x: T) -> None: ...
c = C() # C[X] s.t. X <: int
c.set("foo") # str </: int, but no error

samwgoldman avatar Aug 01 '25 00:08 samwgoldman