diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index 61c5af6a..2c9c6048 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -3,7 +3,6 @@ notes = """ Does not expand boolean arguments to `Literal[True]` and `Literal[False]`. Does not expand enum arguments to literal variants. Does not expand tuple arguments to possible combinations. -Does not evaluate `Any` in some cases where overload is ambiguous. Evaluates `Any` in some cases where overload is not ambiguous. """ conformance_automated = "Fail" @@ -14,10 +13,7 @@ Line 161: Unexpected errors ['overloads_evaluation.py:161: error: No overload va Line 162: Unexpected errors ['overloads_evaluation.py:162: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] 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]'] Line 206: Unexpected errors ['overloads_evaluation.py:206: error: Expression is of type "int", not "int | str" [assert-type]'] -Line 265: Unexpected errors ['overloads_evaluation.py:265: error: Expression is of type "list[Any]", not "Any" [assert-type]'] -Line 281: Unexpected errors ['overloads_evaluation.py:281: error: Expression is of type "list[Any]", not "Any" [assert-type]'] -Line 303: Unexpected errors ['overloads_evaluation.py:303: error: Expression is of type "Any", not "float" [assert-type]'] -Line 347: Unexpected errors ['overloads_evaluation.py:347: error: Expression is of type "list[Any]", not "Any" [assert-type]'] +Line 312: Unexpected errors ['overloads_evaluation.py:312: error: Expression is of type "Any", not "float" [assert-type]'] """ output = """ 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]) - overloads_evaluation.py:162: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] overloads_evaluation.py:205: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type] overloads_evaluation.py:206: error: Expression is of type "int", not "int | str" [assert-type] -overloads_evaluation.py:265: error: Expression is of type "list[Any]", not "Any" [assert-type] -overloads_evaluation.py:281: error: Expression is of type "list[Any]", not "Any" [assert-type] -overloads_evaluation.py:303: error: Expression is of type "Any", not "float" [assert-type] -overloads_evaluation.py:347: error: Expression is of type "list[Any]", not "Any" [assert-type] +overloads_evaluation.py:312: error: Expression is of type "Any", not "float" [assert-type] """ diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index cb63679b..58c20bfe 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -1,10 +1,10 @@ conformant = "Partial" notes = """ -Does not evaluate `Any` in some cases where overload is ambiguous. +Infers `list[int]` for ambiguous overloads with `list[Any]` arguments, rejecting use as `list[str]`. """ conformance_automated = "Fail" errors_diff = """ -Line 281: Unexpected errors ['overloads_evaluation.py:281:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure)'] +Line 290: Unexpected errors ['overloads_evaluation.py:290:27 - error: Type "list[int]" is not assignable to declared type "list[str]"'] """ output = """ 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 overloads_evaluation.py:116:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2"   Type "int | str" is not assignable to type "int"     "str" is not assignable to "int" (reportArgumentType) -overloads_evaluation.py:281:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure) +overloads_evaluation.py:290:27 - error: Type "list[int]" is not assignable to declared type "list[str]" +  "list[int]" is not assignable to "list[str]" +    Type parameter "_T@list" is invariant, but "int" is not the same as "str" +    Consider switching from "list" to "Sequence" which is covariant (reportAssignmentType) """ diff --git a/conformance/results/results.html b/conformance/results/results.html index f0438d58..827a16c4 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -1814,7 +1814,6 @@

Python Type System Conformance Test Results

  • Does not expand boolean arguments to Literal[True] and Literal[False].
  • Does not expand enum arguments to literal variants.
  • Does not expand tuple arguments to possible combinations.
  • -
  • Does not evaluate Any in some cases where overload is ambiguous.
  • Evaluates Any in some cases where overload is not ambiguous.
  • @@ -1823,7 +1822,7 @@

    Python Type System Conformance Test Results

    Partial Pass diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index c89f9d0b..d9ba88d5 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -239,6 +239,12 @@ def check_variadic(v: list[int]) -> None: # > :term:`materializations ` of the argument's type are assignable to # > the corresponding parameter type for each of the remaining overloads. If so, # > eliminate all of the subsequent remaining overloads. +# > +# > If the return types are not equivalent, overload matching is ambiguous. In +# > this case, infer a return type that is :term:`assignable` to each of the +# > remaining return types and permits operations supported by any of them without +# > errors, then stop. A type checker may infer ``Any`` or a more precise gradual +# > type that satisfies these requirements. @overload @@ -262,7 +268,8 @@ def check_example4(v1: list[Any], v2: Any) -> None: assert_type(ret1, list[int]) ret2 = example4(v2, 1) - assert_type(ret2, Any) + int_list: list[int] = ret2 + str_list: list[str] = ret2 @overload @@ -278,7 +285,9 @@ def example5(obj: Any) -> list[Any]: def check_example5(b: list[Any]) -> None: - assert_type(example5(b), Any) + ret = example5(b) + int_list: list[int] = ret + str_list: list[str] = ret @overload @@ -344,4 +353,17 @@ def check_example7(v1: list[Any], v2: Any) -> None: assert_type(ret2, list[str]) ret3 = example7(v1, v2) - assert_type(ret3, Any) + int_list: list[int] = ret3 + str_list: list[str] = ret3 + + +# The earlier ambiguity cases check assignability of list return types. This +# checks distinct scalar return types and operations specific to each type. + + +def check_ambiguous_scalar_return(v: Any) -> None: + ret = example2(1, v, 1) + int_result: int = ret + str_result: str = ret + _ = ret + 1 + ret.upper() diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 7d9ef1bc..85e8acb6 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -300,7 +300,15 @@ they should be replaced with their solved types. If the resulting return types for all remaining overloads are :term:`equivalent`, proceed to step 6. If the return types are not equivalent, overload matching is ambiguous. In -this case, assume a return type of ``Any`` and stop. +this case, infer a return type that is :term:`assignable` to each of the +remaining return types and permits operations supported by any of them without +errors, then stop. A type checker may infer ``Any`` or a more precise gradual +type that satisfies these requirements. + +For example, if the remaining return types are ``int`` and ``str``, the result +should be assignable to both ``int`` and ``str``, and both ``result + 1`` and +``result.upper()`` should be accepted. Inferring the ordinary union ``int | str`` +would not satisfy these requirements. Step 6 ~~~~~~ @@ -395,7 +403,8 @@ Example 4:: # both apply and are ambiguous due to Any, and # the return types are inconsistent. r2 = example4(v2, 1) - reveal_type(r2) # Should reveal Any + int_result: int = r2 # OK + list_result: list[int] = r2 # OK .. _argument-type-expansion: