Function invocation pins type of arguments too early
Minimal repro:
def foo[T](a: T, b: T) -> T:
...
def test(x: int | None, y: int | None) -> int | None:
if x is None:
return foo(x, y)
return None
Expected: No error
Actual: Argument int | None is not assignable to parameter b with type None in function foo [bad-argument-type]
Sandbox URL: https://pyrefly.org/sandbox/?code=CYUwZgBGD20NoBUC6AKAhgLgggNBARlggJQQC0AfNhgFAT0QB0zNNokALiAM4coAeWAJYA7DhAA+EAHLQRIPAE9hYyTLkhSlCKPFTZ82gx2R+O7usN1jDAE4gOAV1siosAUuLX69py8sgQA
i don't understand the initial example - if x is None coerces the type of x to None and then foo(x, y) could indeed fail typechecking if y is int, so pyrefly is doing the right thing there.
@martindemello If the typevar T for foo gets instantiated into int | None then type checking won't fail. i.e.
def foo(a: int | None, b: int | None) -> int | None:
...
def test(x: int | None, y: int | None) -> int | None:
if x is None:
return foo(x, y) # This should type check
return None
We don't really want to assume the call foo(x, y) forces T to be None just because its first argument x is None -- the point here is that if we wait longer and get to see what the type of y is, we could have guessed better type for T.
If I understand, the following is a even simpler example where the eager pinning of generics can go awry:
def foo[T](a: T, b: T) -> T: ...
foo(0, 1.) # Argument `float` is not assignable to parameter `b` with type `int` in function `foo`
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!
Still working on this :)
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!
Now, no errors.
Huh, thanks for checking! I'm not sure why the original example doesn't error anymore (maybe it was fixed by one of the recent improvements that @stroxler made?), but it's easy to modify the example to error again:
def foo[T](a: T, b: T) -> T:
...
def test(x: int | None, y: int | None) -> int | None:
if x is None:
z = foo(x, y) # error: Argument `int | None` is not assignable to parameter `b` with type `None` in function `foo`
return z
return None