Skip to content

Commit 809ee86

Browse files
committed
Allow gradual return types for ambiguous overloads
1 parent b41e509 commit 809ee86

5 files changed

Lines changed: 45 additions & 19 deletions

File tree

conformance/results/mypy/overloads_evaluation.toml

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ notes = """
33
Does not expand boolean arguments to `Literal[True]` and `Literal[False]`.
44
Does not expand enum arguments to literal variants.
55
Does not expand tuple arguments to possible combinations.
6-
Does not evaluate `Any` in some cases where overload is ambiguous.
76
Evaluates `Any` in some cases where overload is not ambiguous.
87
"""
98
conformance_automated = "Fail"
@@ -14,10 +13,7 @@ Line 161: Unexpected errors ['overloads_evaluation.py:161: error: No overload va
1413
Line 162: Unexpected errors ['overloads_evaluation.py:162: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]']
1514
Line 205: Unexpected errors ['overloads_evaluation.py:205: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]']
1615
Line 206: Unexpected errors ['overloads_evaluation.py:206: error: Expression is of type "int", not "int | str" [assert-type]']
17-
Line 265: Unexpected errors ['overloads_evaluation.py:265: error: Expression is of type "list[Any]", not "Any" [assert-type]']
18-
Line 281: Unexpected errors ['overloads_evaluation.py:281: error: Expression is of type "list[Any]", not "Any" [assert-type]']
19-
Line 303: Unexpected errors ['overloads_evaluation.py:303: error: Expression is of type "Any", not "float" [assert-type]']
20-
Line 347: Unexpected errors ['overloads_evaluation.py:347: error: Expression is of type "list[Any]", not "Any" [assert-type]']
16+
Line 312: Unexpected errors ['overloads_evaluation.py:312: error: Expression is of type "Any", not "float" [assert-type]']
2117
"""
2218
output = """
2319
overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload]
@@ -46,8 +42,5 @@ overloads_evaluation.py:161: note: def expand_enum(x: Literal[Color.BLUE]) -
4642
overloads_evaluation.py:162: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]
4743
overloads_evaluation.py:205: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]
4844
overloads_evaluation.py:206: error: Expression is of type "int", not "int | str" [assert-type]
49-
overloads_evaluation.py:265: error: Expression is of type "list[Any]", not "Any" [assert-type]
50-
overloads_evaluation.py:281: error: Expression is of type "list[Any]", not "Any" [assert-type]
51-
overloads_evaluation.py:303: error: Expression is of type "Any", not "float" [assert-type]
52-
overloads_evaluation.py:347: error: Expression is of type "list[Any]", not "Any" [assert-type]
45+
overloads_evaluation.py:312: error: Expression is of type "Any", not "float" [assert-type]
5346
"""

conformance/results/pyright/overloads_evaluation.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
conformant = "Partial"
22
notes = """
3-
Does not evaluate `Any` in some cases where overload is ambiguous.
3+
Infers `list[int]` for ambiguous overloads with `list[Any]` arguments, rejecting use as `list[str]`.
44
"""
55
conformance_automated = "Fail"
66
errors_diff = """
7-
Line 281: Unexpected errors ['overloads_evaluation.py:281:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure)']
7+
Line 290: Unexpected errors ['overloads_evaluation.py:290:27 - error: Type "list[int]" is not assignable to declared type "list[str]"']
88
"""
99
output = """
1010
overloads_evaluation.py:38:1 - error: No overloads for "example1_1" match the provided arguments
@@ -20,5 +20,8 @@ overloads_evaluation.py:116:14 - error: Argument of type "int | str" cannot be a
2020
overloads_evaluation.py:116:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2"
2121
  Type "int | str" is not assignable to type "int"
2222
    "str" is not assignable to "int" (reportArgumentType)
23-
overloads_evaluation.py:281:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure)
23+
overloads_evaluation.py:290:27 - error: Type "list[int]" is not assignable to declared type "list[str]"
24+
  "list[int]" is not assignable to "list[str]"
25+
    Type parameter "_T@list" is invariant, but "int" is not the same as "str"
26+
    Consider switching from "list" to "Sequence" which is covariant (reportAssignmentType)
2427
"""

conformance/results/results.html

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

conformance/tests/overloads_evaluation.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,12 @@ def check_variadic(v: list[int]) -> None:
239239
# > :term:`materializations <materialize>` of the argument's type are assignable to
240240
# > the corresponding parameter type for each of the remaining overloads. If so,
241241
# > eliminate all of the subsequent remaining overloads.
242+
# >
243+
# > If the return types are not equivalent, overload matching is ambiguous. In
244+
# > this case, infer a return type that is :term:`assignable` to each of the
245+
# > remaining return types and permits operations supported by any of them without
246+
# > errors, then stop. A type checker may infer ``Any`` or a more precise gradual
247+
# > type that satisfies these requirements.
242248

243249

244250
@overload
@@ -262,7 +268,8 @@ def check_example4(v1: list[Any], v2: Any) -> None:
262268
assert_type(ret1, list[int])
263269

264270
ret2 = example4(v2, 1)
265-
assert_type(ret2, Any)
271+
int_list: list[int] = ret2
272+
str_list: list[str] = ret2
266273

267274

268275
@overload
@@ -278,7 +285,9 @@ def example5(obj: Any) -> list[Any]:
278285

279286

280287
def check_example5(b: list[Any]) -> None:
281-
assert_type(example5(b), Any)
288+
ret = example5(b)
289+
int_list: list[int] = ret
290+
str_list: list[str] = ret
282291

283292

284293
@overload
@@ -344,4 +353,17 @@ def check_example7(v1: list[Any], v2: Any) -> None:
344353
assert_type(ret2, list[str])
345354

346355
ret3 = example7(v1, v2)
347-
assert_type(ret3, Any)
356+
int_list: list[int] = ret3
357+
str_list: list[str] = ret3
358+
359+
360+
# The earlier ambiguity cases check assignability of list return types. This
361+
# checks distinct scalar return types and operations specific to each type.
362+
363+
364+
def check_ambiguous_scalar_return(v: Any) -> None:
365+
ret = example2(1, v, 1)
366+
int_result: int = ret
367+
str_result: str = ret
368+
_ = ret + 1
369+
ret.upper()

docs/spec/overload.rst

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,15 @@ they should be replaced with their solved types. If the resulting return types
300300
for all remaining overloads are :term:`equivalent`, proceed to step 6.
301301

302302
If the return types are not equivalent, overload matching is ambiguous. In
303-
this case, assume a return type of ``Any`` and stop.
303+
this case, infer a return type that is :term:`assignable` to each of the
304+
remaining return types and permits operations supported by any of them without
305+
errors, then stop. A type checker may infer ``Any`` or a more precise gradual
306+
type that satisfies these requirements.
307+
308+
For example, if the remaining return types are ``int`` and ``str``, the result
309+
should be assignable to both ``int`` and ``str``, and both ``result + 1`` and
310+
``result.upper()`` should be accepted. Inferring the ordinary union ``int | str``
311+
would not satisfy these requirements.
304312

305313
Step 6
306314
~~~~~~
@@ -395,7 +403,8 @@ Example 4::
395403
# both apply and are ambiguous due to Any, and
396404
# the return types are inconsistent.
397405
r2 = example4(v2, 1)
398-
reveal_type(r2) # Should reveal Any
406+
int_result: int = r2 # OK
407+
list_result: list[int] = r2 # OK
399408

400409

401410
.. _argument-type-expansion:

0 commit comments

Comments
 (0)