Skip to content

Commit 48d22e1

Browse files
stroxlermeta-codesync[bot]
authored andcommitted
Add wrapped-return 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** Add tests for higher-order callables where overload residual reconstruction involves the residuals appearing not just at the top-level of a callable but nested inside some other type, like `list[A]`. Adding this in advance because there's already a noted tendency for agents to skip recursive traversals and hard-code non-composable "flat" transformations unless we have guardrails in place. Reviewed By: rchen152 Differential Revision: D102097728 fbshipit-source-id: 1a0561a8bb54464b98914b82507c86e1996ec6e1
1 parent 14b6b60 commit 48d22e1

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

pyrefly/lib/test/callable_residuals.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,50 @@ result(1, 1) # E: Argument `Literal[1]` is not assignable to parameter with typ
501501
"#,
502502
);
503503

504+
testcase!(
505+
bug = "Overload branch selection commits to first branch in wrapped Callable returns",
506+
test_typevar_overloaded_return_wraps_argument,
507+
r#"
508+
from typing import Callable, overload, reveal_type
509+
def higher_order[A, R](x: Callable[[A], R]) -> Callable[[list[A]], R]: ...
510+
511+
@overload
512+
def f(x: int) -> str: ... # E: Overload return type `str` is not assignable to implementation return type `None`
513+
@overload
514+
def f(x: str) -> int: ... # E: Overload return type `int` is not assignable to implementation return type `None`
515+
def f(x): ...
516+
517+
result = higher_order(f)
518+
reveal_type(result) # E: revealed type: (list[int]) -> str
519+
out_a = result([1])
520+
reveal_type(out_a) # E: revealed type: str
521+
out_b = result(["ok"]) # E: Argument `list[str]` is not assignable to parameter with type `list[int]`
522+
reveal_type(out_b) # E: revealed type: str
523+
"#,
524+
);
525+
526+
testcase!(
527+
bug = "Overload branch selection commits to first branch in wrapped Callable returns",
528+
test_typevar_overloaded_return_wraps_return,
529+
r#"
530+
from typing import Callable, overload, reveal_type
531+
def higher_order[A, R](x: Callable[[A], R]) -> Callable[[A], list[R]]: ...
532+
533+
@overload
534+
def f(x: int) -> str: ... # E: Overload return type `str` is not assignable to implementation return type `None`
535+
@overload
536+
def f(x: str) -> int: ... # E: Overload return type `int` is not assignable to implementation return type `None`
537+
def f(x): ...
538+
539+
result = higher_order(f)
540+
reveal_type(result) # E: revealed type: (int) -> list[str]
541+
out_a = result(1)
542+
reveal_type(out_a) # E: revealed type: list[str]
543+
out_b = result("ok") # E: Argument `Literal['ok']` is not assignable to parameter with type `int`
544+
reveal_type(out_b) # E: revealed type: list[str]
545+
"#,
546+
);
547+
504548
// Regression tests for https://github.com/facebook/pyrefly/issues/2105
505549
// Overloaded callable protocol passed to higher-order function with ParamSpec.
506550
// The solver commits to one overload branch too early and rejects valid calls.

0 commit comments

Comments
 (0)