-
Notifications
You must be signed in to change notification settings - Fork 292
Improve Step 5 of overload call evaluation. #2250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 7 commits
47637e8
045a46b
fbb847e
89e6130
4e7c475
891f7ae
137e3fc
88a046c
bef8d53
3e07524
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,17 @@ | ||
| conformance_automated = "Pass" | ||
| conformant = "Partial" | ||
| notes = """ | ||
| 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 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 | ||
| 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:395:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `int` | ||
| overloads_evaluation.py:466:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `A[Any]` | ||
| """ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -268,39 +268,64 @@ If so, eliminate overloads that do not have a variadic parameter. | |
| Step 5 | ||
| ~~~~~~ | ||
|
|
||
| For all arguments, determine whether all possible | ||
| :term:`materializations <materialize>` 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 candidate overloads, determine whether all arguments satisfy at | ||
| least one of the following conditions: | ||
|
|
||
| Consider the following example:: | ||
| - All possible :term:`materializations <materialize>` 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. | ||
|
|
||
| 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']) -> bool: ... | ||
| @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. | ||
rchen152 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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 <materialize>` of another | ||
| overload's return type that is not assignable to this overload's return type. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is clever. I like this much better than Mypy's type erasure (where
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed that this phrasing is clunky, but I spent a while wordsmithing it, and this was the best I could come up with :/
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can be phrased more simply in terms of "top materialization" (the union of all possible materializations of a gradual type), but that's not a term that has been formally defined in the spec yet, so that would just introduce a different problem :) |
||
|
|
||
| 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 +422,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): | ||
| # 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]` | ||
|
|
||
|
|
||
| .. _argument-type-expansion: | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.