From 47637e8abee15e6df39fbbeda33d2521e5938cac Mon Sep 17 00:00:00 2001 From: Rebecca Chen Date: Tue, 7 Apr 2026 18:32:14 -0700 Subject: [PATCH 1/6] Improve Step 5 of overload call evaluation. Makes two modifications to step 5 of overload call evaluation: 1. When materializing arguments to check whether we can eliminate overloads, skip arguments that have the same parameter type in all overloads. 2. When we still have multiple overloads after the materialization filter, try to find a return type that all materializations of all other return types are assignable to. Updates the conformance tests as well. All type checkers already do (1). Mypy and pyrefly pass the conformance test for (2). I believe mypy has a heuristic that approximates the new rule; pyrefly implements it exactly. --- .../results/mypy/overloads_evaluation.toml | 16 ++-- .../results/pyrefly/callables_annotation.toml | 4 +- .../pyrefly/directives_no_type_check.toml | 2 - .../results/pyrefly/enums_member_values.toml | 5 +- .../generics_paramspec_components.toml | 12 +-- .../results/pyrefly/generics_self_usage.toml | 2 +- .../results/pyrefly/protocols_modules.toml | 8 +- .../pyrefly/qualifiers_final_annotation.toml | 21 ++--- .../pyrefly/qualifiers_final_decorator.toml | 2 +- .../pyrefly/typeddicts_alt_syntax.toml | 2 +- .../results/pyrefly/typeforms_typeform.toml | 88 +++++-------------- conformance/results/pyrefly/version.toml | 2 +- .../results/pyright/overloads_evaluation.toml | 8 +- conformance/results/results.html | 16 ++-- .../results/ty/overloads_evaluation.toml | 8 +- .../results/zuban/overloads_evaluation.toml | 8 +- conformance/tests/overloads_evaluation.py | 59 ++++++++++++- conformance/uv.lock | 21 ++--- docs/spec/overload.rst | 72 +++++++++++---- 19 files changed, 197 insertions(+), 159 deletions(-) diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index 4ddeaca2d..f6bc2e979 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -14,10 +14,10 @@ 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 268: Unexpected errors ['overloads_evaluation.py:268: error: Expression is of type "list[Any]", not "Any" [assert-type]'] +Line 284: Unexpected errors ['overloads_evaluation.py:284: error: Expression is of type "list[Any]", not "Any" [assert-type]'] +Line 306: Unexpected errors ['overloads_evaluation.py:306: error: Expression is of type "Any", not "float" [assert-type]'] +Line 350: Unexpected errors ['overloads_evaluation.py:350: error: Expression is of type "list[Any]", not "Any" [assert-type]'] """ output = """ overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload] @@ -46,8 +46,8 @@ 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:268: error: Expression is of type "list[Any]", not "Any" [assert-type] +overloads_evaluation.py:284: error: Expression is of type "list[Any]", not "Any" [assert-type] +overloads_evaluation.py:306: error: Expression is of type "Any", not "float" [assert-type] +overloads_evaluation.py:350: error: Expression is of type "list[Any]", not "Any" [assert-type] """ diff --git a/conformance/results/pyrefly/callables_annotation.toml b/conformance/results/pyrefly/callables_annotation.toml index 7ddb09654..efbdf98e8 100644 --- a/conformance/results/pyrefly/callables_annotation.toml +++ b/conformance/results/pyrefly/callables_annotation.toml @@ -14,10 +14,10 @@ ERROR callables_annotation.py:29:8-9: Unexpected keyword argument `a` [unexpecte ERROR callables_annotation.py:29:8-11: Expected 2 more positional arguments [bad-argument-count] ERROR callables_annotation.py:29:13-14: Unexpected keyword argument `b` [unexpected-keyword] ERROR callables_annotation.py:35:8-9: Expected 0 positional arguments, got 1 [bad-argument-count] -ERROR callables_annotation.py:55:5-18: `Callable` requires exactly two arguments but 1 was found [bad-specialization] +ERROR callables_annotation.py:55:5-18: Expected 2 arguments for `Callable`, got 1 [bad-specialization] ERROR callables_annotation.py:56:14-17: Callable types can only have `ParamSpec` in this position, got `int` [bad-specialization] ERROR callables_annotation.py:57:18-23: Expected a type form, got instance of `list[type[int]]` [not-a-type] -ERROR callables_annotation.py:58:5-28: `Callable` requires exactly two arguments but 3 was found [bad-specialization] +ERROR callables_annotation.py:58:5-28: Expected 2 arguments for `Callable`, got 3 [bad-specialization] ERROR callables_annotation.py:59:15-18: Invalid position for `...` [invalid-argument] ERROR callables_annotation.py:91:7-15: `() -> str` is not assignable to variable `cb3` with type `(int, ...) -> str` [bad-assignment] ERROR callables_annotation.py:93:7-15: `(*, a: int) -> str` is not assignable to variable `cb3` with type `(int, ...) -> str` [bad-assignment] diff --git a/conformance/results/pyrefly/directives_no_type_check.toml b/conformance/results/pyrefly/directives_no_type_check.toml index e3669041a..52623ac35 100644 --- a/conformance/results/pyrefly/directives_no_type_check.toml +++ b/conformance/results/pyrefly/directives_no_type_check.toml @@ -4,8 +4,6 @@ errors_diff = """ """ output = """ ERROR directives_no_type_check.py:15:14-16: `Literal['']` is not assignable to `int` [bad-assignment] -ERROR directives_no_type_check.py:29:7-17: Argument `Literal[b'invalid']` is not assignable to parameter `a` with type `int` in function `func1` [bad-argument-type] -ERROR directives_no_type_check.py:29:19-31: Argument `Literal[b'arguments']` is not assignable to parameter `b` with type `str` in function `func1` [bad-argument-type] ERROR directives_no_type_check.py:32:6-8: Missing argument `a` in function `func1` [missing-argument] ERROR directives_no_type_check.py:32:6-8: Missing argument `b` in function `func1` [missing-argument] """ diff --git a/conformance/results/pyrefly/enums_member_values.toml b/conformance/results/pyrefly/enums_member_values.toml index e90c4678c..5ac05b3dd 100644 --- a/conformance/results/pyrefly/enums_member_values.toml +++ b/conformance/results/pyrefly/enums_member_values.toml @@ -3,12 +3,9 @@ conformance_automated = "Pass" errors_diff = """ """ output = """ -ERROR enums_member_values.py:21:12-43: assert_type(int, Literal[1]) failed [assert-type] -ERROR enums_member_values.py:22:12-41: assert_type(int, Literal[1]) failed [assert-type] -ERROR enums_member_values.py:26:16-50: assert_type(int, Literal[1, 3]) failed [assert-type] ERROR enums_member_values.py:30:16-51: assert_type(int, Literal[1, 2, 3]) failed [assert-type] ERROR enums_member_values.py:54:12-46: assert_type(Any, Literal[1]) failed [assert-type] ERROR enums_member_values.py:68:12-42: assert_type(int, Literal[1]) failed [assert-type] -ERROR enums_member_values.py:78:5-10: Enum member `GREEN` has type `str`, must match the `_value_` attribute annotation of `int` [bad-assignment] +ERROR enums_member_values.py:78:5-10: Enum member `GREEN` has type `Literal['green']`, must match the `_value_` attribute annotation of `int` [bad-assignment] ERROR enums_member_values.py:85:24-29: `int` is not assignable to attribute `_value_` with type `str` [bad-assignment] """ diff --git a/conformance/results/pyrefly/generics_paramspec_components.toml b/conformance/results/pyrefly/generics_paramspec_components.toml index 1dbe5ff1e..03d98dbde 100644 --- a/conformance/results/pyrefly/generics_paramspec_components.toml +++ b/conformance/results/pyrefly/generics_paramspec_components.toml @@ -1,10 +1,5 @@ -conformant = "Partial" -notes = """ -Does not reject usage of args/kwargs for out-of-scope ParamSpec -""" -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 30: Expected 1 errors """ output = """ ERROR generics_paramspec_components.py:17:25-33: `ParamSpec` **kwargs is only allowed in a **kwargs annotation [invalid-annotation] @@ -13,6 +8,8 @@ ERROR generics_paramspec_components.py:20:23-29: `ParamSpec` *args is only allow ERROR generics_paramspec_components.py:23:5-24:13: `ParamSpec` *args and **kwargs must be used together [invalid-param-spec] ERROR generics_paramspec_components.py:23:46-52: `ParamSpec` *args is only allowed in an *args annotation [invalid-annotation] ERROR generics_paramspec_components.py:26:5-27:13: `ParamSpec` *args and **kwargs must be used together [invalid-param-spec] +ERROR generics_paramspec_components.py:30:25-31: Expected a type form, got instance of `ParamSpecArgs` [not-a-type] +ERROR generics_paramspec_components.py:30:43-51: Expected a type form, got instance of `ParamSpecKwargs` [not-a-type] ERROR generics_paramspec_components.py:35:18-24: `ParamSpec` *args is only allowed in an *args annotation [invalid-annotation] ERROR generics_paramspec_components.py:36:20-28: `ParamSpec` **kwargs is only allowed in a **kwargs annotation [invalid-annotation] ERROR generics_paramspec_components.py:38:5-39:13: `ParamSpec` *args and **kwargs must be used together [invalid-param-spec] @@ -22,8 +19,7 @@ ERROR generics_paramspec_components.py:51:11-12: Expected 0 positional arguments ERROR generics_paramspec_components.py:60:28-34: Keyword-only parameter `s` may not appear after ParamSpec args parameter [bad-function-definition] ERROR generics_paramspec_components.py:70:10-30: Expected *-unpacked P.args and **-unpacked P.kwargs [invalid-param-spec] ERROR generics_paramspec_components.py:72:10-27: Expected 1 more positional argument [bad-argument-count] -ERROR generics_paramspec_components.py:83:13-14: Unexpected keyword argument `x` in function `foo` [unexpected-keyword] -ERROR generics_paramspec_components.py:83:13-16: Expected 1 more positional argument in function `foo` [bad-argument-count] +ERROR generics_paramspec_components.py:83:13-14: Expected argument `x` to be positional in function `foo` [unexpected-keyword] ERROR generics_paramspec_components.py:98:20-23: Argument `Literal['A']` is not assignable to parameter `a` with type `int` in function `twice` [bad-argument-type] ERROR generics_paramspec_components.py:98:25-26: Argument `Literal[1]` is not assignable to parameter `b` with type `str` in function `twice` [bad-argument-type] """ diff --git a/conformance/results/pyrefly/generics_self_usage.toml b/conformance/results/pyrefly/generics_self_usage.toml index 7505dd888..8d26f2592 100644 --- a/conformance/results/pyrefly/generics_self_usage.toml +++ b/conformance/results/pyrefly/generics_self_usage.toml @@ -4,13 +4,13 @@ Does not implement some restrictions on where Self can be used """ conformance_automated = "Fail" errors_diff = """ -Line 82: Expected 1 errors Line 87: Expected 1 errors """ output = """ ERROR generics_self_usage.py:73:14-18: `Self` must appear within a class [invalid-annotation] ERROR generics_self_usage.py:73:23-27: `Self` must appear within a class [invalid-annotation] ERROR generics_self_usage.py:76:6-10: `Self` must appear within a class [invalid-annotation] +ERROR generics_self_usage.py:82:9-37: `Self` cannot be used when `self` has an explicit TypeVar annotation [invalid-annotation] ERROR generics_self_usage.py:103:15-19: `Self` must appear within a class [invalid-annotation] ERROR generics_self_usage.py:105:12-16: `Self` must appear within a class [invalid-annotation] ERROR generics_self_usage.py:105:12-16: Invalid base class: `Self` [invalid-inheritance] diff --git a/conformance/results/pyrefly/protocols_modules.toml b/conformance/results/pyrefly/protocols_modules.toml index 88eae7936..08e55a351 100644 --- a/conformance/results/pyrefly/protocols_modules.toml +++ b/conformance/results/pyrefly/protocols_modules.toml @@ -1,13 +1,7 @@ -conformant = "Partial" -notes = """ -Fails one subtyping example of protocol modules -""" -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 25: Unexpected errors ['`Module[_protocols_modules1]` is not assignable to `Options1` [bad-assignment]'] """ output = """ -ERROR protocols_modules.py:25:17-36: `Module[_protocols_modules1]` is not assignable to `Options1` [bad-assignment] ERROR protocols_modules.py:26:17-36: `Module[_protocols_modules1]` is not assignable to `Options2` [bad-assignment] ERROR protocols_modules.py:48:18-37: `Module[_protocols_modules2]` is not assignable to `Reporter2` [bad-assignment] ERROR protocols_modules.py:49:18-37: `Module[_protocols_modules2]` is not assignable to `Reporter3` [bad-assignment] diff --git a/conformance/results/pyrefly/qualifiers_final_annotation.toml b/conformance/results/pyrefly/qualifiers_final_annotation.toml index 10c83546a..eda91ff00 100644 --- a/conformance/results/pyrefly/qualifiers_final_annotation.toml +++ b/conformance/results/pyrefly/qualifiers_final_annotation.toml @@ -1,13 +1,5 @@ -conformant = "Partial" -notes = """ -Does not reject Final inside ClassVar. -Issues with NamedTuple interaction. -""" -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 107: Expected 1 errors -Line 131: Unexpected errors ['Expected first item to be a string literal [invalid-argument]', 'Expected first item to be a string literal [invalid-argument]'] -Line 133: Unexpected errors ['Unexpected keyword argument `x` in function `N.__new__` [unexpected-keyword]', 'Unexpected keyword argument `y` in function `N.__new__` [unexpected-keyword]'] """ output = """ ERROR qualifiers_final_annotation.py:16:7-12: Expected a type argument for `Final` [invalid-annotation] @@ -22,17 +14,16 @@ ERROR qualifiers_final_annotation.py:67:9-17: Cannot set field `ID7` [read-only] ERROR qualifiers_final_annotation.py:71:8-11: Cannot assign to variable `RATE` because it is marked final [bad-assignment] ERROR qualifiers_final_annotation.py:81:1-18: Cannot set field `DEFAULT_ID` [read-only] ERROR qualifiers_final_annotation.py:94:5-17: `BORDER_WIDTH` is declared as final in parent class `ClassC` [bad-override] +ERROR qualifiers_final_annotation.py:107:5-11: `Final` may not be nested inside `ClassVar` [invalid-annotation] ERROR qualifiers_final_annotation.py:108:19-27: `ClassVar` may not be nested inside `Final` [invalid-annotation] ERROR qualifiers_final_annotation.py:118:9-19: `Final` is not allowed in this context [invalid-annotation] ERROR qualifiers_final_annotation.py:121:14-19: `Final` is only allowed on a class or local variable annotation [invalid-annotation] ERROR qualifiers_final_annotation.py:121:14-30: `Final` is not allowed in this context [invalid-annotation] -ERROR qualifiers_final_annotation.py:131:23-24: Expected first item to be a string literal [invalid-argument] -ERROR qualifiers_final_annotation.py:131:33-34: Expected first item to be a string literal [invalid-argument] -ERROR qualifiers_final_annotation.py:133:3-4: Unexpected keyword argument `x` in function `N.__new__` [unexpected-keyword] -ERROR qualifiers_final_annotation.py:133:8-9: Unexpected keyword argument `y` in function `N.__new__` [unexpected-keyword] +ERROR qualifiers_final_annotation.py:134:2-7: Missing argument `x` in function `N.__new__` [missing-argument] +ERROR qualifiers_final_annotation.py:134:2-7: Missing argument `y` in function `N.__new__` [missing-argument] ERROR qualifiers_final_annotation.py:134:3-4: Unexpected keyword argument `a` in function `N.__new__` [unexpected-keyword] -ERROR qualifiers_final_annotation.py:135:3-4: Unexpected keyword argument `x` in function `N.__new__` [unexpected-keyword] -ERROR qualifiers_final_annotation.py:135:9-10: Unexpected keyword argument `y` in function `N.__new__` [unexpected-keyword] +ERROR qualifiers_final_annotation.py:135:5-7: Argument `Literal['']` is not assignable to parameter `x` with type `int` in function `N.__new__` [bad-argument-type] +ERROR qualifiers_final_annotation.py:135:11-13: Argument `Literal['']` is not assignable to parameter `y` with type `int` in function `N.__new__` [bad-argument-type] ERROR qualifiers_final_annotation.py:141:11-12: Cannot assign to variable `ID1` because it is marked final [bad-assignment] ERROR qualifiers_final_annotation.py:145:5-11: Cannot assign to variable `x` because it is marked final [bad-assignment] ERROR qualifiers_final_annotation.py:147:15-16: Cannot assign to variable `x` because it is marked final [bad-assignment] diff --git a/conformance/results/pyrefly/qualifiers_final_decorator.toml b/conformance/results/pyrefly/qualifiers_final_decorator.toml index aba68da0a..381d74150 100644 --- a/conformance/results/pyrefly/qualifiers_final_decorator.toml +++ b/conformance/results/pyrefly/qualifiers_final_decorator.toml @@ -12,5 +12,5 @@ ERROR qualifiers_final_decorator.py:81:9-15: `method` is declared as final in pa ERROR qualifiers_final_decorator.py:86:9-15: `@final` should only be applied to the implementation of an overloaded function. [invalid-overload] ERROR qualifiers_final_decorator.py:95:9-15: `method` is declared as final in parent class `Base4` [bad-override] ERROR qualifiers_final_decorator.py:118:9-15: `method` is declared as final in parent class `Base5_2` [bad-override] -ERROR qualifiers_final_decorator.py:125:1-7: Decorator `@final` can only be used on methods. [invalid-decorator] + WARN qualifiers_final_decorator.py:125:1-7: Decorator `@final` can only be used on methods. [invalid-decorator] """ diff --git a/conformance/results/pyrefly/typeddicts_alt_syntax.toml b/conformance/results/pyrefly/typeddicts_alt_syntax.toml index 6781d8452..ab8a7e821 100644 --- a/conformance/results/pyrefly/typeddicts_alt_syntax.toml +++ b/conformance/results/pyrefly/typeddicts_alt_syntax.toml @@ -5,7 +5,7 @@ errors_diff = """ output = """ ERROR typeddicts_alt_syntax.py:23:1-14: Expected valid functional typed dictionary definition [invalid-argument] ERROR typeddicts_alt_syntax.py:27:45-46: Expected first item to be a string literal [invalid-argument] -ERROR typeddicts_alt_syntax.py:31:27-38: Expected string literal "BadTypedDict3" [invalid-argument] + WARN typeddicts_alt_syntax.py:31:27-38: Expected string literal "BadTypedDict3" [name-mismatch] ERROR typeddicts_alt_syntax.py:35:72-83: Unrecognized keyword argument `other` in typed dictionary definition [invalid-argument] ERROR typeddicts_alt_syntax.py:41:1-7: Expected valid functional typed dictionary definition [invalid-argument] ERROR typeddicts_alt_syntax.py:41:30-38: Unrecognized keyword argument `name` in typed dictionary definition [invalid-argument] diff --git a/conformance/results/pyrefly/typeforms_typeform.toml b/conformance/results/pyrefly/typeforms_typeform.toml index 5a6215d4f..0b882d7fa 100644 --- a/conformance/results/pyrefly/typeforms_typeform.toml +++ b/conformance/results/pyrefly/typeforms_typeform.toml @@ -1,78 +1,32 @@ -conformant = "Unsupported" +conformant = "Partial" +notes = """ +Does not allow assigning a TypeForm to types.GenericAlias. +Does not allow passing a forward reference to a function accepting a TypeForm. +""" conformance_automated = "Fail" errors_diff = """ -Line 59: Expected 1 errors -Line 15: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 16: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 17: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 18: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 19: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 20: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 21: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 29: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 30: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 31: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 32: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 40: Unexpected errors ['Expected a type form, got instance of `_SpecialForm` [not-a-type]'] -Line 41: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 46: Unexpected errors ['`type[str | None]` is not assignable to `UnionType` [bad-assignment]'] -Line 47: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] Line 49: Unexpected errors ['`type[list[int]]` is not assignable to `GenericAlias` [bad-assignment]', 'Expected `v2_actual` to be a type alias, got `GenericAlias` [invalid-type-alias]'] -Line 50: Unexpected errors ['Expected a type form, got instance of `_SpecialForm` [not-a-type]'] -Line 52: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 53: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 80: Unexpected errors ['Expected a callable, got `_SpecialForm` [not-callable]'] -Line 81: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 83: Unexpected errors ['Expected a callable, got `_SpecialForm` [not-callable]'] -Line 84: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 93: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 97: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] -Line 107: Unexpected errors ['Expected a type form, got instance of `object` [not-a-type]'] +Line 58: Unexpected errors ["Argument `Literal['int']` is not assignable to parameter `x` with type `TypeForm[Any]` in function `func1` [bad-argument-type]"] """ output = """ -ERROR typeforms_typeform.py:15:6-26: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:16:6-26: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:17:6-26: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:18:6-26: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:19:6-26: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:20:6-26: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:21:6-26: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:23:7-27: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:24:7-27: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:29:8-21: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:30:8-21: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:31:20-33: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:32:20-33: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:40:14-22: Expected a type form, got instance of `_SpecialForm` [not-a-type] -ERROR typeforms_typeform.py:41:20-33: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:46:30-40: `type[str | None]` is not assignable to `UnionType` [bad-assignment] -ERROR typeforms_typeform.py:47:15-35: Expected a type form, got instance of `object` [not-a-type] +ERROR typeforms_typeform.py:23:30-39: `type[int | str]` is not assignable to `TypeForm[str | None]` [bad-assignment] +ERROR typeforms_typeform.py:24:30-46: `type[list[str | None]]` is not assignable to `TypeForm[str | None]` [bad-assignment] ERROR typeforms_typeform.py:49:33-42: `type[list[int]]` is not assignable to `GenericAlias` [bad-assignment] ERROR typeforms_typeform.py:49:33-42: Expected `v2_actual` to be a type alias, got `GenericAlias` [invalid-type-alias] -ERROR typeforms_typeform.py:50:15-23: Expected a type form, got instance of `_SpecialForm` [not-a-type] -ERROR typeforms_typeform.py:52:5-24: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:53:5-23: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:67:7-15: Expected a type form, got instance of `_SpecialForm` [not-a-type] -ERROR typeforms_typeform.py:68:7-15: Expected a type form, got instance of `_SpecialForm` [not-a-type] -ERROR typeforms_typeform.py:69:7-15: Expected a type form, got instance of `_SpecialForm` [not-a-type] -ERROR typeforms_typeform.py:70:7-15: Expected a type form, got instance of `_SpecialForm` [not-a-type] +ERROR typeforms_typeform.py:58:7-12: Argument `Literal['int']` is not assignable to parameter `x` with type `TypeForm[Any]` in function `func1` [bad-argument-type] +ERROR typeforms_typeform.py:59:7-19: Argument `Literal['not a type']` is not assignable to parameter `x` with type `TypeForm[Any]` in function `func1` [bad-argument-type] +ERROR typeforms_typeform.py:67:18-25: `tuple[@_, ...]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:68:18-24: `tuple[Literal[1], Literal[2]]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:69:18-19: `Literal[1]` is not assignable to `TypeForm[Any]` [bad-assignment] ERROR typeforms_typeform.py:70:18-22: `Self` must appear within a class [invalid-annotation] -ERROR typeforms_typeform.py:71:7-15: Expected a type form, got instance of `_SpecialForm` [not-a-type] +ERROR typeforms_typeform.py:70:18-22: `type[Self]` is not assignable to `TypeForm[Any]` [bad-assignment] ERROR typeforms_typeform.py:71:18-31: `ClassVar` is not allowed in this context [invalid-annotation] -ERROR typeforms_typeform.py:72:7-15: Expected a type form, got instance of `_SpecialForm` [not-a-type] ERROR typeforms_typeform.py:72:18-28: `Final` is not allowed in this context [invalid-annotation] -ERROR typeforms_typeform.py:73:7-15: Expected a type form, got instance of `_SpecialForm` [not-a-type] -ERROR typeforms_typeform.py:74:7-15: Expected a type form, got instance of `_SpecialForm` [not-a-type] -ERROR typeforms_typeform.py:75:7-15: Expected a type form, got instance of `_SpecialForm` [not-a-type] -ERROR typeforms_typeform.py:80:6-14: Expected a callable, got `_SpecialForm` [not-callable] -ERROR typeforms_typeform.py:81:17-37: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:83:6-14: Expected a callable, got `_SpecialForm` [not-callable] -ERROR typeforms_typeform.py:84:17-36: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:86:6-14: Expected a callable, got `_SpecialForm` [not-callable] -ERROR typeforms_typeform.py:88:6-14: Expected a callable, got `_SpecialForm` [not-callable] -ERROR typeforms_typeform.py:93:24-37: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:97:5-24: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:98:5-18: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:107:5-24: Expected a type form, got instance of `object` [not-a-type] -ERROR typeforms_typeform.py:108:5-18: Expected a type form, got instance of `object` [not-a-type] +ERROR typeforms_typeform.py:73:18-28: `type[*TypeVarTuple[Ts]]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:74:18-26: `type[Optional]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:75:19-28: `+` is not supported between `type[int]` and `type[str]` [unsupported-operation] +ERROR typeforms_typeform.py:86:16-23: Function call cannot be used in annotations [invalid-annotation] +ERROR typeforms_typeform.py:88:15-22: Function call cannot be used in annotations [invalid-annotation] +ERROR typeforms_typeform.py:98:21-36: `TypeForm[int]` is not assignable to `TypeForm[str]` [bad-assignment] +ERROR typeforms_typeform.py:108:21-31: `type[int]` is not assignable to `TypeForm[str]` [bad-assignment] """ diff --git a/conformance/results/pyrefly/version.toml b/conformance/results/pyrefly/version.toml index 58851b2f1..271a6135d 100644 --- a/conformance/results/pyrefly/version.toml +++ b/conformance/results/pyrefly/version.toml @@ -1 +1 @@ -version = "pyrefly 0.58.0" +version = "pyrefly 0.60.0" diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index 7b41dc4e1..01afcd21b 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -1,10 +1,12 @@ conformant = "Partial" notes = """ Does not evaluate Any in some cases where overload is ambiguous. +Picks first overload instead of most general return type in some cases where overload is ambiguous. """ 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 284: Unexpected errors ['overloads_evaluation.py:284:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure)'] +Line 398: Unexpected errors ['overloads_evaluation.py:398:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure)'] """ output = """ overloads_evaluation.py:38:1 - error: No overloads for "example1_1" match the provided arguments @@ -20,5 +22,7 @@ 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:284:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure) +overloads_evaluation.py:354:5 - error: Overload 1 for "example8" overlaps overload 2 and returns an incompatible type (reportOverlappingOverload) +overloads_evaluation.py:398:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure) """ diff --git a/conformance/results/results.html b/conformance/results/results.html index 3ddb751cd..7d4380fa0 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -178,7 +178,7 @@

Python Type System Conformance Test Results

zuban 0.6.2
-
pyrefly 0.58.0
+
pyrefly 0.60.0
ty 0.0.29
@@ -228,7 +228,7 @@

Python Type System Conformance Test Results

Partial

Does not support assigning Union and GenericAlias objects to their runtime types.

Unsupported Pass -Unsupported +
Partial

Does not allow assigning a TypeForm to types.GenericAlias.

Does not allow passing a forward reference to a function accepting a TypeForm.

Unsupported @@ -318,7 +318,7 @@

Python Type System Conformance Test Results

Pass Pass Pass -
Partial

Does not reject usage of args/kwargs for out-of-scope ParamSpec

+Pass
Partial

Incorrectly allows using `*args: P.args` and `**kwargs: P.kwargs` when `P` has not been put into scope by any other parameter annotation or enclosing scope.

     generics_paramspec_semantics @@ -496,7 +496,7 @@

Python Type System Conformance Test Results

Partial

Does not treat use of Final name as if it was replaced by the literal in NamedTuple definition.

Does not allow conditional assignment of Final instance variable in __init__ method.

Does not allow redefinition of private class variable that is marked Final in parent class.

Does not report modification of local Final variable via "for" statement.

Pass Pass -
Partial

Does not reject Final inside ClassVar.

Issues with NamedTuple interaction.

+Pass Pass      qualifiers_final_decorator @@ -648,7 +648,7 @@

Python Type System Conformance Test Results

Pass Pass Pass -
Partial

Fails one subtyping example of protocol modules

+Pass
Partial

Never considers a module as satisfying a protocol with a method member due to the fact that the method will never exist on the class `types.ModuleType`, only on a given specific module instance.

     protocols_recursive @@ -795,10 +795,10 @@

Python Type System Conformance Test Results

     overloads_evaluation
Partial

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.

-
Partial

Does not evaluate Any in some cases where overload is ambiguous.

-Pass -Pass +
Partial

Does not evaluate Any in some cases where overload is ambiguous.

Picks first overload instead of most general return type in some cases where overload is ambiguous.

+
Partial

Returns Any instead of most general return type for ambiguous calls.

Pass +
Partial

Returns Any instead of most general return type for ambiguous calls.

Exceptions diff --git a/conformance/results/ty/overloads_evaluation.toml b/conformance/results/ty/overloads_evaluation.toml index 3a9a963c2..3912766df 100644 --- a/conformance/results/ty/overloads_evaluation.toml +++ b/conformance/results/ty/overloads_evaluation.toml @@ -1,9 +1,15 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Returns Any instead of most general return type for ambiguous calls. +""" +conformance_automated = "Fail" errors_diff = """ +Line 398: Unexpected errors ['overloads_evaluation.py:398:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]`'] """ output = """ overloads_evaluation.py:38:1: error[no-matching-overload] No overload of function `example1_1` matches arguments overloads_evaluation.py:46:15: error[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]` overloads_evaluation.py:51:12: error[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]` overloads_evaluation.py:116:5: error[no-matching-overload] No overload of function `example2` matches arguments +overloads_evaluation.py:398:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]` """ diff --git a/conformance/results/zuban/overloads_evaluation.toml b/conformance/results/zuban/overloads_evaluation.toml index 0e3b48cf8..9f7d0741d 100644 --- a/conformance/results/zuban/overloads_evaluation.toml +++ b/conformance/results/zuban/overloads_evaluation.toml @@ -1,5 +1,10 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Returns Any instead of most general return type for ambiguous calls. +""" +conformance_automated = "Fail" errors_diff = """ +Line 398: Unexpected errors ['overloads_evaluation.py:398: error: Expression is of type "Any", not "A[Any]" [misc]'] """ output = """ overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload] @@ -16,4 +21,5 @@ overloads_evaluation.py:51: note: def example1_1(x: int, y: str) -> int overloads_evaluation.py:51: note: def example1_1(x: str) -> str overloads_evaluation.py:116: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] overloads_evaluation.py:116: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] +overloads_evaluation.py:398: error: Expression is of type "Any", not "A[Any]" [misc] """ diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index c89f9d0ba..12ecd84f7 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -235,10 +235,13 @@ def check_variadic(v: list[int]) -> None: assert_type(ret1, int) -# > Step 5: For all arguments, determine whether all possible -# > :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. +# > Step 5: For each of the remaining overloads, determine whether all +# > arguments satisfy at least one of the following conditions: +# > - All possible :term:`materializations ` of the argument's type are +# > assignable to the corresponding parameter type, or +# > - The parameter types corresponding to this argument in all of the remaining overloads +# > are :term:`equivalent`. +# > If so, eliminate all of the subsequent remaining overloads. @overload @@ -345,3 +348,51 @@ def check_example7(v1: list[Any], v2: Any) -> None: ret3 = example7(v1, v2) assert_type(ret3, Any) + + +@overload +def example8(x: str, y: Literal['o1']) -> str: ... # E? some type checkers report unsafe overlap between example8's overloads + + +@overload +def example8(x: str, y: str) -> int: ... + + +def example8(x: str, y: str) -> str | int: + return x if y == 'o1' else 0 + + +def check_example8(x: Any): + # The parameter type corresponding to argument `x` is `str` in both + # overloads, and all materializations of argument `y`'s type of + # `Literal['o1']` match the first overload, so the second overload can be + # eliminated. + ret = example8(x, 'o1') + assert_type(ret, str) + + +class A[T]: + x: T + + def f(self) -> T: + return self.x + + +@overload +def example9(x: A[None]) -> A[None]: ... + + +@overload +def example9(x: A[Any]) -> A[Any]: ... + + +def example9(x: A[Any]) -> A[Any]: + return x + + +def check_example9(x: Any): + # Steps 5 eliminates the first overload because there exists a + # materialization of `A[Any]` that is not assignable to `A[None]`. Step 6 + # picks the second overload. + ret = example9(x) + assert_type(ret, A[Any]) diff --git a/conformance/uv.lock b/conformance/uv.lock index a06be9bb9..b4b5d3d01 100644 --- a/conformance/uv.lock +++ b/conformance/uv.lock @@ -73,18 +73,19 @@ wheels = [ [[package]] name = "pyrefly" -version = "0.58.0" +version = "0.60.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/bb/5611669a79967250e0654c0cffb7b0bb892b4263d5dec9b4345917edd9cd/pyrefly-0.58.0.tar.gz", hash = "sha256:4512b89cd8db95e8994537895ff41ad60e6211643442f8e33ed93bb59f88a256", size = 5357858, upload-time = "2026-03-25T01:21:01.8Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/c7/28d14b64888e2d03815627ebff8d57a9f08389c4bbebfe70ae1ed98a1267/pyrefly-0.60.0.tar.gz", hash = "sha256:2499f5b6ff5342e86dfe1cd94bcce133519bbbc93b7ad5636195fea4f0fa3b81", size = 5500389, upload-time = "2026-04-06T19:57:30.643Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/fb/499e8efc0c3bc4e8f0e689922e616a8d406ff3297962c94b8053701b727f/pyrefly-0.58.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6521e3924d98c2fe118109de625c075d970f9d4a9682f11afa4f7c6b5f11ca1f", size = 12765022, upload-time = "2026-03-25T01:20:41.397Z" }, - { url = "https://files.pythonhosted.org/packages/b6/7f/25f329adfb191e78c22524858cce7d0e06d3961d44fe2ed5c302d3096dc2/pyrefly-0.58.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bd52dd8f330e08f3829791b914f170769f97e8a0e377abf554710decbf799f20", size = 12276729, upload-time = "2026-03-25T01:20:43.815Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ca/4982e8e3a177ed30e7ec23f51a429c2319653dcebbe20bac55ef3f275f23/pyrefly-0.58.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2334bea7d7b43a4d9061cce8e48ee5da2de185511637ed691dca5d649633f514", size = 35369879, upload-time = "2026-03-25T01:20:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/7f/56/02afb8f6584765d1fed08a81f0415504345229ff452384143864a8ed8062/pyrefly-0.58.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb7bf956beb01f2664a1b20ca7303dc617bc454457b869237945edd5a52fc39b", size = 38060979, upload-time = "2026-03-25T01:20:49.62Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ec/78ae846cba36a244db20d157948e6dd238713f63b812b701754e22fab4d5/pyrefly-0.58.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce58ec7d4a5bc4fdb4a8d90329259c15e221e840ce1da946c698c4954aa4098b", size = 40753678, upload-time = "2026-03-25T01:20:52.718Z" }, - { url = "https://files.pythonhosted.org/packages/98/ec/6d1461960bf2efbf7af679846edd29ff1fce9e20b4a653cb99338e1e847e/pyrefly-0.58.0-py3-none-win32.whl", hash = "sha256:43fa3b1abc0b55503c6238d09f333ce4f7a341cb9d5113b9d5e3089b8e0901b6", size = 11793278, upload-time = "2026-03-25T01:20:55.367Z" }, - { url = "https://files.pythonhosted.org/packages/3d/23/70c20ea234409e4ea3b4177ec6cdf221532fd34bfec012961972e2ab511f/pyrefly-0.58.0-py3-none-win_amd64.whl", hash = "sha256:5dbd270d6841ec7b772ee41dd2d83f376ad24b279b747ddb8c11550ad3648dde", size = 12626215, upload-time = "2026-03-25T01:20:57.329Z" }, - { url = "https://files.pythonhosted.org/packages/c3/20/fed053053785585cdd3a393aa3ab8e2e1f9c5078c16c9b659bb4b87ff22d/pyrefly-0.58.0-py3-none-win_arm64.whl", hash = "sha256:86425dfb43607027960ffed13722504992e83b45becbb83853d8f961c715dac2", size = 12124266, upload-time = "2026-03-25T01:20:59.641Z" }, + { url = "https://files.pythonhosted.org/packages/31/99/6c9984a09220e5eb7dd5c869b7a32d25c3d06b5e8854c6eb679db1145c3e/pyrefly-0.60.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:bf1691af0fee69d0c99c3c6e9d26ab6acd3c8afef96416f9ba2e74934833b7b5", size = 12921262, upload-time = "2026-04-06T19:57:00.745Z" }, + { url = "https://files.pythonhosted.org/packages/05/b3/6216aa3c00c88e59a27eb4149851b5affe86eeea6129f4224034a32dddb0/pyrefly-0.60.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3e71b70c9b95545cf3b479bc55d1381b531de7b2380eb64411088a1e56b634cb", size = 12424413, upload-time = "2026-04-06T19:57:03.417Z" }, + { url = "https://files.pythonhosted.org/packages/9b/87/eb8dd73abd92a93952ac27a605e463c432fb250fb23186574038c7035594/pyrefly-0.60.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:680ee5f8f98230ea145652d7344708f5375786209c5bf03d8b911fdb0d0d4195", size = 35940884, upload-time = "2026-04-06T19:57:06.909Z" }, + { url = "https://files.pythonhosted.org/packages/0d/34/dc6aeb67b840c745fcee6db358295d554abe6ab555a7eaaf44624bd80bf1/pyrefly-0.60.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d0b20dbbe4aff15b959e8d825b7521a144c4122c11e57022e83b36568c54470", size = 38677220, upload-time = "2026-04-06T19:57:11.235Z" }, + { url = "https://files.pythonhosted.org/packages/66/6b/c863fcf7ef592b7d1db91502acf0d1113be8bed7a2a7143fc6f0dd90616f/pyrefly-0.60.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2911563c8e6b2eaefff68885c94727965469a35375a409235a7a4d2b7157dc15", size = 36907431, upload-time = "2026-04-06T19:57:15.074Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a2/25ea095ab2ecca8e62884669b11a79f14299db93071685b73a97efbaf4f3/pyrefly-0.60.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a631d9d04705e303fe156f2e62551611bc7ef8066c34708ceebcfb3088bd55", size = 41447898, upload-time = "2026-04-06T19:57:19.382Z" }, + { url = "https://files.pythonhosted.org/packages/8e/2c/097bdc6e8d40676b28eb03710a4577bc3c7b803cd24693ac02bf15de3d67/pyrefly-0.60.0-py3-none-win32.whl", hash = "sha256:a08d69298da5626cf502d3debbb6944fd13d2f405ea6625363751f1ff570d366", size = 11913434, upload-time = "2026-04-06T19:57:22.887Z" }, + { url = "https://files.pythonhosted.org/packages/0a/d4/8d27fe310e830c8d11ab73db38b93f9fd2e218744b6efb1204401c9a74d5/pyrefly-0.60.0-py3-none-win_amd64.whl", hash = "sha256:56cf30654e708ae1dd635ffefcba4fa4b349dd7004a6ccc5c41e3a9bb944320c", size = 12745033, upload-time = "2026-04-06T19:57:25.517Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ad/8eea1f8fb8209f91f6dbfe48000c9d05fd0cdb1b5b3157283c9b1dada55d/pyrefly-0.60.0-py3-none-win_arm64.whl", hash = "sha256:b6d27fba970f4777063c0227c54167d83bece1804ea34f69e7118e409ba038d2", size = 12246390, upload-time = "2026-04-06T19:57:28.141Z" }, ] [[package]] diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 7d9ef1bc1..6f28746cd 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -268,39 +268,61 @@ If so, eliminate overloads that do not have a variadic parameter. Step 5 ~~~~~~ -For all arguments, determine whether all possible -: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. +For each of the remaining overloads, determine whether all arguments satisfy at +least one of the following conditions: -Consider the following example:: +- All possible :term:`materializations ` of the argument's type are + assignable to the corresponding parameter type, or +- The parameter types corresponding to this argument in all of the remaining overloads + are :term:`equivalent`. + +If so, eliminate all of the subsequent remaining overloads. + +Consider the following examples:: @overload - def example(x: list[int]) -> int: ... + def example1(x: list[int]) -> int: ... @overload - def example(x: list[Any]) -> str: ... + def example1(x: list[Any]) -> str: ... @overload - def example(x: Any) -> Any: ... + def example1(x: Any) -> Any: ... def test(a: list[Any]): # All materializations of list[Any] will match either the first or # second overload, so the third overload can be eliminated. - example(a) + example1(a) + +and:: + + @overload + def example2(x: str, y: Literal['o1']) -> str: ... + @overload + def example2(x: str, y: str) -> int: ... + + def test(x: Any): + # The parameter type corresponding to argument `x` is `str` in both + # overloads, and all materializations of argument `y`'s type of + # `Literal['o1']` match the first overload, so the second overload can be + # eliminated. + example2(x, 'o1') This rule eliminates overloads that will never be chosen even if the caller eliminates types that include ``Any``. -If the call involves more than one argument, all possible materializations of -every argument type must be assignable to its corresponding parameter type. -If this condition exists, all subsequent remaining overloads should be eliminated. +If the call involves more than one argument, every argument must satisfy one of +the above conditions. Once this filtering process is applied for all arguments, examine the return types of the remaining overloads. If these return types include type variables, -they should be replaced with their solved types. If the resulting return types -for all remaining overloads are :term:`equivalent`, proceed to step 6. +they should be replaced with their solved types. Eliminate every overload for +which there exists a :term:`materialization ` of another +overload's return type that is not assignable to this overload's return type. -If the return types are not equivalent, overload matching is ambiguous. In -this case, assume a return type of ``Any`` and stop. +This rule picks the most general return type, if one exists. + +- If no candidate overloads remain, overload matching is ambiguous. In this + case, assume a return type of ``Any`` and stop. +- If one or more candidate overloads remain, proceed to step 6. Step 6 ~~~~~~ @@ -397,6 +419,24 @@ Example 4:: r2 = example4(v2, 1) reveal_type(r2) # Should reveal Any +Example 5:: + + class A[T]: + x: T + def f(self) -> T: + return self.x + + @overload + def example5(x: A[None]) -> A[None]: ... + @overload + def example5(x: A[Any]) -> A[Any]: ... + + def test(x: Any): + # Steps 5 eliminates the first overload because there exists a + # materialization of `A[Any]` that is not assignable to `A[None]`. Step 6 + # picks the second overload. + reveal_type(example5(x)) # Should reveal `A[Any]` + .. _argument-type-expansion: From fbb847ea897e16c23f058645aa1efad22dc50e9c Mon Sep 17 00:00:00 2001 From: Rebecca Chen Date: Wed, 8 Apr 2026 11:59:48 -0700 Subject: [PATCH 2/6] Typo fix --- conformance/tests/overloads_evaluation.py | 2 +- docs/spec/overload.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index 12ecd84f7..509c2c8eb 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -391,7 +391,7 @@ def example9(x: A[Any]) -> A[Any]: def check_example9(x: Any): - # Steps 5 eliminates the first overload because there exists a + # Step 5 eliminates the first overload because there exists a # materialization of `A[Any]` that is not assignable to `A[None]`. Step 6 # picks the second overload. ret = example9(x) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 6f28746cd..1f3c24c9e 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -432,7 +432,7 @@ Example 5:: def example5(x: A[Any]) -> A[Any]: ... def test(x: Any): - # Steps 5 eliminates the first overload because there exists a + # Step 5 eliminates the first overload because there exists a # materialization of `A[Any]` that is not assignable to `A[None]`. Step 6 # picks the second overload. reveal_type(example5(x)) # Should reveal `A[Any]` From 89e61303803375d11824f4651a185af0ed773e1c Mon Sep 17 00:00:00 2001 From: Rebecca Chen Date: Wed, 8 Apr 2026 12:23:39 -0700 Subject: [PATCH 3/6] Remove unsafe overlap in overload example. --- conformance/results/pyright/overloads_evaluation.toml | 1 - conformance/tests/overloads_evaluation.py | 8 ++++---- docs/spec/overload.rst | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index 01afcd21b..e85f92067 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -23,6 +23,5 @@ overloads_evaluation.py:116:17 - error: Argument of type "int | str" cannot be a   Type "int | str" is not assignable to type "int"     "str" is not assignable to "int" (reportArgumentType) overloads_evaluation.py:284:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure) -overloads_evaluation.py:354:5 - error: Overload 1 for "example8" overlaps overload 2 and returns an incompatible type (reportOverlappingOverload) overloads_evaluation.py:398:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure) """ diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index 509c2c8eb..a92af4da4 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -351,15 +351,15 @@ def check_example7(v1: list[Any], v2: Any) -> None: @overload -def example8(x: str, y: Literal['o1']) -> str: ... # E? some type checkers report unsafe overlap between example8's overloads +def example8(x: str, y: Literal['o1']) -> bool: ... @overload def example8(x: str, y: str) -> int: ... -def example8(x: str, y: str) -> str | int: - return x if y == 'o1' else 0 +def example8(x: str, y: str) -> bool | int: + return True def check_example8(x: Any): @@ -368,7 +368,7 @@ def check_example8(x: Any): # `Literal['o1']` match the first overload, so the second overload can be # eliminated. ret = example8(x, 'o1') - assert_type(ret, str) + assert_type(ret, bool) class A[T]: diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 1f3c24c9e..45a9121ae 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -295,7 +295,7 @@ Consider the following examples:: and:: @overload - def example2(x: str, y: Literal['o1']) -> str: ... + def example2(x: str, y: Literal['o1']) -> bool: ... @overload def example2(x: str, y: str) -> int: ... From 4e7c475312291159dd0cc6090406a8e94c1b956c Mon Sep 17 00:00:00 2001 From: Rebecca Chen Date: Wed, 8 Apr 2026 13:00:12 -0700 Subject: [PATCH 4/6] Clarifications * "remaining" overloads => "candidate" overloads * moar tests --- .../results/mypy/overloads_evaluation.toml | 2 + .../results/pyright/overloads_evaluation.toml | 4 +- .../results/ty/overloads_evaluation.toml | 6 +- .../results/zuban/overloads_evaluation.toml | 6 +- conformance/tests/overloads_evaluation.py | 76 +++++++++++++++++-- docs/spec/overload.rst | 6 +- 6 files changed, 86 insertions(+), 14 deletions(-) diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index f6bc2e979..1cfb0cfe2 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -18,6 +18,7 @@ Line 268: Unexpected errors ['overloads_evaluation.py:268: error: Expression is Line 284: Unexpected errors ['overloads_evaluation.py:284: error: Expression is of type "list[Any]", not "Any" [assert-type]'] Line 306: Unexpected errors ['overloads_evaluation.py:306: error: Expression is of type "Any", not "float" [assert-type]'] Line 350: Unexpected errors ['overloads_evaluation.py:350: error: Expression is of type "list[Any]", not "Any" [assert-type]'] +Line 395: Unexpected errors ['overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [assert-type]'] """ output = """ overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload] @@ -50,4 +51,5 @@ overloads_evaluation.py:268: error: Expression is of type "list[Any]", not "Any" overloads_evaluation.py:284: error: Expression is of type "list[Any]", not "Any" [assert-type] overloads_evaluation.py:306: error: Expression is of type "Any", not "float" [assert-type] overloads_evaluation.py:350: error: Expression is of type "list[Any]", not "Any" [assert-type] +overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [assert-type] """ diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index e85f92067..97ff871d2 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -6,7 +6,7 @@ Picks first overload instead of most general return type in some cases where ove conformance_automated = "Fail" errors_diff = """ Line 284: Unexpected errors ['overloads_evaluation.py:284:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure)'] -Line 398: Unexpected errors ['overloads_evaluation.py:398:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure)'] +Line 464: Unexpected errors ['overloads_evaluation.py:464:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure)'] """ output = """ overloads_evaluation.py:38:1 - error: No overloads for "example1_1" match the provided arguments @@ -23,5 +23,5 @@ overloads_evaluation.py:116:17 - error: Argument of type "int | str" cannot be a   Type "int | str" is not assignable to type "int"     "str" is not assignable to "int" (reportArgumentType) overloads_evaluation.py:284:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure) -overloads_evaluation.py:398:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure) +overloads_evaluation.py:464:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure) """ diff --git a/conformance/results/ty/overloads_evaluation.toml b/conformance/results/ty/overloads_evaluation.toml index 3912766df..811a41714 100644 --- a/conformance/results/ty/overloads_evaluation.toml +++ b/conformance/results/ty/overloads_evaluation.toml @@ -4,12 +4,14 @@ Returns Any instead of most general return type for ambiguous calls. """ conformance_automated = "Fail" errors_diff = """ -Line 398: Unexpected errors ['overloads_evaluation.py:398:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]`'] +Line 395: Unexpected errors ['overloads_evaluation.py:395:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `int`'] +Line 464: Unexpected errors ['overloads_evaluation.py:464:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]`'] """ output = """ overloads_evaluation.py:38:1: error[no-matching-overload] No overload of function `example1_1` matches arguments overloads_evaluation.py:46:15: error[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]` overloads_evaluation.py:51:12: error[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]` overloads_evaluation.py:116:5: error[no-matching-overload] No overload of function `example2` matches arguments -overloads_evaluation.py:398:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]` +overloads_evaluation.py:395:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `int` +overloads_evaluation.py:464:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]` """ diff --git a/conformance/results/zuban/overloads_evaluation.toml b/conformance/results/zuban/overloads_evaluation.toml index 9f7d0741d..bcf6a440b 100644 --- a/conformance/results/zuban/overloads_evaluation.toml +++ b/conformance/results/zuban/overloads_evaluation.toml @@ -4,7 +4,8 @@ Returns Any instead of most general return type for ambiguous calls. """ conformance_automated = "Fail" errors_diff = """ -Line 398: Unexpected errors ['overloads_evaluation.py:398: error: Expression is of type "Any", not "A[Any]" [misc]'] +Line 395: Unexpected errors ['overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [misc]'] +Line 464: Unexpected errors ['overloads_evaluation.py:464: error: Expression is of type "Any", not "A[Any]" [misc]'] """ output = """ overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload] @@ -21,5 +22,6 @@ overloads_evaluation.py:51: note: def example1_1(x: int, y: str) -> int overloads_evaluation.py:51: note: def example1_1(x: str) -> str overloads_evaluation.py:116: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] overloads_evaluation.py:116: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] -overloads_evaluation.py:398: error: Expression is of type "Any", not "A[Any]" [misc] +overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [misc] +overloads_evaluation.py:464: error: Expression is of type "Any", not "A[Any]" [misc] """ diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index a92af4da4..b4bdcbdf3 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -371,6 +371,72 @@ def check_example8(x: Any): assert_type(ret, bool) +@overload +def example9(x: str, y: Literal['o1']) -> bool: ... + + +@overload +def example9(x: bytes, y: Literal['o1', 'o2']) -> bool: ... + + +@overload +def example9(x: bytes, y: str) -> int: ... + + +def example9(x: str | bytes, y: str) -> bool | int: + return True + + +def check_example9(x: Any): + # All three overloads are candidates. The parameter types corresponding to + # argument `x` are `str` and `bytes`, which are not equivalent, so none of + # the overloads can be eliminated. We pick the most general return type. + ret1 = example9(x, 'o1') + assert_type(ret1, int) + # The second and third overload are candidates. The parameter type + # corresponding to argument `x` is `bytes` in both candidates, so we can + # eliminate the third overload. + ret2 = example9(x, 'o2') + assert_type(ret2, bool) + + +@overload +def example10(x: int) -> bool: ... + + +@overload +def example10(*args: int) -> int: ... + + +def example10(*args: int, **kwargs: int) -> int: + return 0 + + +def check_example10(x: Any): + # The parameters corresponding to argument `x` (`x` in the first overload + # and `*args` in the second) both have type `int`, so the second overload + # can be eliminated. + assert_type(example10(x), bool) + + +@overload +def example11(x: int) -> bool: ... + + +@overload +def example11(**kwargs: int) -> int: ... + + +def example11(*args: int, **kwargs: int) -> int: + return 0 + +def check_example11(x: Any): + # The parameters corresponding to argument `x` (`x` in the first overload + # and `**kwargs` in the second) both have type `int`, so the second + # overload can be eliminated. + assert_type(example11(x=x), bool) + + class A[T]: x: T @@ -379,20 +445,20 @@ def f(self) -> T: @overload -def example9(x: A[None]) -> A[None]: ... +def example12(x: A[None]) -> A[None]: ... @overload -def example9(x: A[Any]) -> A[Any]: ... +def example12(x: A[Any]) -> A[Any]: ... -def example9(x: A[Any]) -> A[Any]: +def example12(x: A[Any]) -> A[Any]: return x -def check_example9(x: Any): +def check_example12(x: Any): # Step 5 eliminates the first overload because there exists a # materialization of `A[Any]` that is not assignable to `A[None]`. Step 6 # picks the second overload. - ret = example9(x) + ret = example12(x) assert_type(ret, A[Any]) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 45a9121ae..19d06cfad 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -268,15 +268,15 @@ If so, eliminate overloads that do not have a variadic parameter. Step 5 ~~~~~~ -For each of the remaining overloads, determine whether all arguments satisfy at +For each of the candidate overloads, determine whether all arguments satisfy at least one of the following conditions: - All possible :term:`materializations ` of the argument's type are assignable to the corresponding parameter type, or -- The parameter types corresponding to this argument in all of the remaining overloads +- The parameter types corresponding to this argument in all of the candidate overloads are :term:`equivalent`. -If so, eliminate all of the subsequent remaining overloads. +If so, eliminate all of the subsequent candidate overloads. Consider the following examples:: From 891f7ae7f39c13698f96dfcd994076adf3a7dcb3 Mon Sep 17 00:00:00 2001 From: Rebecca Chen Date: Wed, 8 Apr 2026 14:01:46 -0700 Subject: [PATCH 5/6] Clarification: unpacked arguments --- .../results/mypy/overloads_evaluation.toml | 3 +++ conformance/results/results.html | 4 ++-- .../results/zuban/overloads_evaluation.toml | 3 +++ conformance/tests/overloads_evaluation.py | 16 ++++++++-------- docs/spec/overload.rst | 11 +++++++---- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index 1cfb0cfe2..08093e5de 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -3,6 +3,7 @@ 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 handle unpacked arguments when checking for parameter type equivalence. Does not evaluate Any in some cases where overload is ambiguous. Evaluates Any in some cases where overload is not ambiguous. """ @@ -19,6 +20,7 @@ Line 284: Unexpected errors ['overloads_evaluation.py:284: error: Expression is Line 306: Unexpected errors ['overloads_evaluation.py:306: error: Expression is of type "Any", not "float" [assert-type]'] Line 350: Unexpected errors ['overloads_evaluation.py:350: error: Expression is of type "list[Any]", not "Any" [assert-type]'] Line 395: Unexpected errors ['overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [assert-type]'] +Line 437: Unexpected errors ['overloads_evaluation.py:437: error: Expression is of type "Any", not "bool" [assert-type]'] """ output = """ overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload] @@ -52,4 +54,5 @@ overloads_evaluation.py:284: error: Expression is of type "list[Any]", not "Any" overloads_evaluation.py:306: error: Expression is of type "Any", not "float" [assert-type] overloads_evaluation.py:350: error: Expression is of type "list[Any]", not "Any" [assert-type] overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [assert-type] +overloads_evaluation.py:437: error: Expression is of type "Any", not "bool" [assert-type] """ diff --git a/conformance/results/results.html b/conformance/results/results.html index d5c64dd01..8778932d0 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -794,9 +794,9 @@

Python Type System Conformance Test Results

Pass      overloads_evaluation -
Partial

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.

+
Partial

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 handle unpacked arguments when checking for parameter type equivalence.

Does not evaluate Any in some cases where overload is ambiguous.

Evaluates Any in some cases where overload is not ambiguous.

Partial

Does not evaluate Any in some cases where overload is ambiguous.

Picks first overload instead of most general return type in some cases where overload is ambiguous.

-
Partial

Returns Any instead of most general return type for ambiguous calls.

+
Partial

Does not handle unpacked arguments when checking for parameter type equivalence.

Returns Any instead of most general return type for ambiguous calls.

Pass
Partial

Returns Any instead of most general return type for ambiguous calls.

diff --git a/conformance/results/zuban/overloads_evaluation.toml b/conformance/results/zuban/overloads_evaluation.toml index bcf6a440b..22136ccbf 100644 --- a/conformance/results/zuban/overloads_evaluation.toml +++ b/conformance/results/zuban/overloads_evaluation.toml @@ -1,10 +1,12 @@ conformant = "Partial" notes = """ +Does not handle unpacked arguments when checking for parameter type equivalence. Returns Any instead of most general return type for ambiguous calls. """ conformance_automated = "Fail" errors_diff = """ Line 395: Unexpected errors ['overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [misc]'] +Line 437: Unexpected errors ['overloads_evaluation.py:437: error: Expression is of type "Any", not "bool" [misc]'] Line 464: Unexpected errors ['overloads_evaluation.py:464: error: Expression is of type "Any", not "A[Any]" [misc]'] """ output = """ @@ -23,5 +25,6 @@ overloads_evaluation.py:51: note: def example1_1(x: str) -> str overloads_evaluation.py:116: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] overloads_evaluation.py:116: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [misc] +overloads_evaluation.py:437: error: Expression is of type "Any", not "bool" [misc] overloads_evaluation.py:464: error: Expression is of type "Any", not "A[Any]" [misc] """ diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index b4bdcbdf3..318c5c095 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -420,21 +420,21 @@ def check_example10(x: Any): @overload -def example11(x: int) -> bool: ... +def example11(x: Literal['o1'], y: int, z: str) -> bool: ... @overload -def example11(**kwargs: int) -> int: ... +def example11(x: str, y: int, z: str) -> int: ... -def example11(*args: int, **kwargs: int) -> int: - return 0 +def example11(x: str, y: int, z: str) -> bool | int: + return True + def check_example11(x: Any): - # The parameters corresponding to argument `x` (`x` in the first overload - # and `**kwargs` in the second) both have type `int`, so the second - # overload can be eliminated. - assert_type(example11(x=x), bool) + # `*x` maps to `(y: int, z: str)` in both overloads, so the second overload + # can be eliminated. + assert_type(example11('o1', *x), bool) class A[T]: diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 19d06cfad..7751e2dd6 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -271,10 +271,13 @@ Step 5 For each of the candidate overloads, determine whether all arguments satisfy at least one of the following conditions: -- All possible :term:`materializations ` of the argument's type are - assignable to the corresponding parameter type, or -- The parameter types corresponding to this argument in all of the candidate overloads - are :term:`equivalent`. +- All possible :term:`materializations ` of the argument's type + are assignable to the corresponding parameter type, or +- The parameter types corresponding to this argument in all of the candidate + overloads are :term:`equivalent`. For an unpacked argument, this condition is + satisfied if the argument maps to the same number of parameters in all + candidate overloads, and for each parameter, the types in all candidate + overloads are equivalent. If so, eliminate all of the subsequent candidate overloads. From 137e3fcbcf112f1514e1d2eb8c236d67b78125b1 Mon Sep 17 00:00:00 2001 From: Rebecca Chen Date: Wed, 8 Apr 2026 14:08:26 -0700 Subject: [PATCH 6/6] Consistent style --- conformance/results/mypy/overloads_evaluation.toml | 4 ++-- conformance/results/pyright/overloads_evaluation.toml | 4 ++-- conformance/results/ty/overloads_evaluation.toml | 4 ++-- conformance/results/zuban/overloads_evaluation.toml | 8 ++++---- conformance/tests/overloads_evaluation.py | 6 ++++-- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index 08093e5de..d02df4565 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -20,7 +20,7 @@ Line 284: Unexpected errors ['overloads_evaluation.py:284: error: Expression is Line 306: Unexpected errors ['overloads_evaluation.py:306: error: Expression is of type "Any", not "float" [assert-type]'] Line 350: Unexpected errors ['overloads_evaluation.py:350: error: Expression is of type "list[Any]", not "Any" [assert-type]'] Line 395: Unexpected errors ['overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [assert-type]'] -Line 437: Unexpected errors ['overloads_evaluation.py:437: error: Expression is of type "Any", not "bool" [assert-type]'] +Line 439: Unexpected errors ['overloads_evaluation.py:439: error: Expression is of type "Any", not "bool" [assert-type]'] """ output = """ overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload] @@ -54,5 +54,5 @@ overloads_evaluation.py:284: error: Expression is of type "list[Any]", not "Any" overloads_evaluation.py:306: error: Expression is of type "Any", not "float" [assert-type] overloads_evaluation.py:350: error: Expression is of type "list[Any]", not "Any" [assert-type] overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [assert-type] -overloads_evaluation.py:437: error: Expression is of type "Any", not "bool" [assert-type] +overloads_evaluation.py:439: error: Expression is of type "Any", not "bool" [assert-type] """ diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index 97ff871d2..22388d132 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -6,7 +6,7 @@ Picks first overload instead of most general return type in some cases where ove conformance_automated = "Fail" errors_diff = """ Line 284: Unexpected errors ['overloads_evaluation.py:284:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure)'] -Line 464: Unexpected errors ['overloads_evaluation.py:464:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure)'] +Line 466: Unexpected errors ['overloads_evaluation.py:466:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure)'] """ output = """ overloads_evaluation.py:38:1 - error: No overloads for "example1_1" match the provided arguments @@ -23,5 +23,5 @@ overloads_evaluation.py:116:17 - error: Argument of type "int | str" cannot be a   Type "int | str" is not assignable to type "int"     "str" is not assignable to "int" (reportArgumentType) overloads_evaluation.py:284:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure) -overloads_evaluation.py:464:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure) +overloads_evaluation.py:466:17 - error: "assert_type" mismatch: expected "A[Any]" but received "A[None]" (reportAssertTypeFailure) """ diff --git a/conformance/results/ty/overloads_evaluation.toml b/conformance/results/ty/overloads_evaluation.toml index 811a41714..3c19a2062 100644 --- a/conformance/results/ty/overloads_evaluation.toml +++ b/conformance/results/ty/overloads_evaluation.toml @@ -5,7 +5,7 @@ Returns Any instead of most general return type for ambiguous calls. conformance_automated = "Fail" errors_diff = """ Line 395: Unexpected errors ['overloads_evaluation.py:395:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `int`'] -Line 464: Unexpected errors ['overloads_evaluation.py:464:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]`'] +Line 466: Unexpected errors ['overloads_evaluation.py:466:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]`'] """ output = """ overloads_evaluation.py:38:1: error[no-matching-overload] No overload of function `example1_1` matches arguments @@ -13,5 +13,5 @@ overloads_evaluation.py:46:15: error[invalid-argument-type] Argument to function overloads_evaluation.py:51:12: error[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]` overloads_evaluation.py:116:5: error[no-matching-overload] No overload of function `example2` matches arguments overloads_evaluation.py:395:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `int` -overloads_evaluation.py:464:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]` +overloads_evaluation.py:466:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]` """ diff --git a/conformance/results/zuban/overloads_evaluation.toml b/conformance/results/zuban/overloads_evaluation.toml index 22136ccbf..42cdfce0b 100644 --- a/conformance/results/zuban/overloads_evaluation.toml +++ b/conformance/results/zuban/overloads_evaluation.toml @@ -6,8 +6,8 @@ Returns Any instead of most general return type for ambiguous calls. conformance_automated = "Fail" errors_diff = """ Line 395: Unexpected errors ['overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [misc]'] -Line 437: Unexpected errors ['overloads_evaluation.py:437: error: Expression is of type "Any", not "bool" [misc]'] -Line 464: Unexpected errors ['overloads_evaluation.py:464: error: Expression is of type "Any", not "A[Any]" [misc]'] +Line 439: Unexpected errors ['overloads_evaluation.py:439: error: Expression is of type "Any", not "bool" [misc]'] +Line 466: Unexpected errors ['overloads_evaluation.py:466: error: Expression is of type "Any", not "A[Any]" [misc]'] """ output = """ overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload] @@ -25,6 +25,6 @@ overloads_evaluation.py:51: note: def example1_1(x: str) -> str overloads_evaluation.py:116: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] overloads_evaluation.py:116: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] overloads_evaluation.py:395: error: Expression is of type "Any", not "int" [misc] -overloads_evaluation.py:437: error: Expression is of type "Any", not "bool" [misc] -overloads_evaluation.py:464: error: Expression is of type "Any", not "A[Any]" [misc] +overloads_evaluation.py:439: error: Expression is of type "Any", not "bool" [misc] +overloads_evaluation.py:466: error: Expression is of type "Any", not "A[Any]" [misc] """ diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index 318c5c095..8a6a46758 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -416,7 +416,8 @@ def check_example10(x: Any): # The parameters corresponding to argument `x` (`x` in the first overload # and `*args` in the second) both have type `int`, so the second overload # can be eliminated. - assert_type(example10(x), bool) + ret = example10(x) + assert_type(ret, bool) @overload @@ -434,7 +435,8 @@ def example11(x: str, y: int, z: str) -> bool | int: def check_example11(x: Any): # `*x` maps to `(y: int, z: str)` in both overloads, so the second overload # can be eliminated. - assert_type(example11('o1', *x), bool) + ret = example11('o1', *x) + assert_type(ret, bool) class A[T]: