Skip to content

Commit 14b6b60

Browse files
stroxlermeta-codesync[bot]
authored andcommitted
Add some more involved overload test cases
Summary: **This stack** Adds a "callable residual" type that allows us to capture structure when a generic or overloaded callable is passed to a higher-order function. The residuals can later be "exploded" to recover generic and overload structure in outputs (either Callable outputs, or classes whose methods capture the callable structure of an argument - e.g. callback protocol classes). The full design details are [here](https://github.com/stroxler/pyrefly-docs/tree/main/callable-residuals) **This diff** The initial test cases (driven by our bug report) emphasized just the ParamSpec case, but we should cover other cases as well. Reviewed By: rchen152 Differential Revision: D102011870 fbshipit-source-id: 0564de10a96ce8e23aa8fcdceaf46b1a0990e46f
1 parent 300ccde commit 14b6b60

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

pyrefly/lib/test/callable_residuals.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,51 @@ out_b = result("ok") # E: Argument `Literal['ok']` is not assignable to paramet
456456
"#,
457457
);
458458

459+
testcase!(
460+
bug = "Overloaded functions lose overloads through simple Callable identity",
461+
test_typevar_identity_overloaded,
462+
r#"
463+
from typing import Callable, overload, reveal_type
464+
def identity[A, R](x: Callable[[A], R]) -> Callable[[A], R]:
465+
return x
466+
467+
@overload
468+
def f(x: int) -> str: ... # E: Overload return type `str` is not assignable to implementation return type `None`
469+
@overload
470+
def f(x: str) -> int: ... # E: Overload return type `int` is not assignable to implementation return type `None`
471+
def f(x): ...
472+
473+
result = identity(f)
474+
reveal_type(result) # E: revealed type: (int) -> str
475+
out_a = result(1)
476+
reveal_type(out_a) # E: revealed type: str
477+
out_b = result("ok") # E: Argument `Literal['ok']` is not assignable to parameter with type `int`
478+
"#,
479+
);
480+
481+
testcase!(
482+
bug = "Overloaded signatures collapse to one branch through multi-arg Callable identity",
483+
test_typevar_identity_overloaded_two_arg,
484+
r#"
485+
from typing import Callable, overload, reveal_type
486+
def identity[A, B, R](x: Callable[[A, B], R]) -> Callable[[A, B], R]:
487+
return x
488+
489+
@overload
490+
def f(x: int, y: str) -> bool: ... # E: Overload return type `bool` is not assignable to implementation return type `None`
491+
@overload
492+
def f(x: str, y: int) -> bytes: ... # E: Overload return type `bytes` is not assignable to implementation return type `None`
493+
def f(x, y): ...
494+
495+
result = identity(f)
496+
reveal_type(result) # E: revealed type: (int, str) -> bool
497+
out_a = result(1, "ok")
498+
reveal_type(out_a) # E: revealed type: bool
499+
result("x", "ok") # E: Argument `Literal['x']` is not assignable to parameter with type `int`
500+
result(1, 1) # E: Argument `Literal[1]` is not assignable to parameter with type `str`
501+
"#,
502+
);
503+
459504
// Regression tests for https://github.com/facebook/pyrefly/issues/2105
460505
// Overloaded callable protocol passed to higher-order function with ParamSpec.
461506
// The solver commits to one overload branch too early and rejects valid calls.

0 commit comments

Comments
 (0)