Skip to content

Generic function type argument inference sometimes commits too early #1467

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

Closed
rwbarton opened this issue May 3, 2016 · 3 comments
Closed
Labels

Comments

@rwbarton
Copy link
Contributor

rwbarton commented May 3, 2016

This was an example designed as a test case for another issue, but also happens to be plausible code.

from typing import TypeVar, Union, List
T = TypeVar('T')
U = TypeVar('U')
def interleave(x: List[T], y: List[U]) -> List[Union[T, U]]: pass

x = [1]
y = ['a']
z = interleave(x, y) # type: List[Union[int, str]]

Obviously we intend to call interleave with T = int and U = str. Without the type annotation on z, mypy correctly infers these type arguments and accepts the program.

But with the type annotation, mypy reasons that in order for interleave(x, y) to be a subtype of List[Union[int, str]], both T and U will need to be subtypes of Union[int, str]. (And it's true that this is necessary, though not sufficient.) Then check_call substitutes this upper bound for T and U before considering the types of the arguments, resulting in the errors

interleave.py:8: error: Argument 1 to "interleave" has incompatible type List[int]; expected List[Union[int, str]]
interleave.py:8: error: Argument 2 to "interleave" has incompatible type List[str]; expected List[Union[int, str]]

In a real program the context for interleave(x, y) might be provided by a surrounding function call rather than a type annotation.

Here's another example not involving unions:

from typing import TypeVar, List, Tuple
T = TypeVar('T')
def f(x: List[T]) -> Tuple[str, T]: pass
x = [1]
y = f(x) # type: Tuple[str, float]

This program should be valid because we can choose T = int (as Tuple[str, int] is a subtype of Tuple[str, float]), but mypy tries to choose T = float and therefore errors.

@rwbarton rwbarton added the bug mypy got something wrong label May 3, 2016
@rwbarton
Copy link
Contributor Author

There's also a special case in infer_function_type_arguments_using_context that refuses to infer generic function type arguments when the declared return type of the function is a bare type variable T. I think this is to avoid the kind of situation in this issue where we would infer that T is the type of the context when, in fact, any subtype of the context would also be possible (this is bad if, for example, the generic function has type def f(xs: List[T]) -> T, the argument has type List[int] and the context has type Float).

A strategy that seems promising is to only commit to values of type arguments in infer_function_type_arguments_using_context which are determined uniquely by the generated constraints; and then take the context into account again when generating constraints in infer_function_type_arguments. This is approximately what that special case does anyways, because constraint generation currently treats most type constructors as invariant.

@rwbarton rwbarton self-assigned this May 10, 2016
@rwbarton
Copy link
Contributor Author

rwbarton commented Jul 1, 2016

I tried the strategy mentioned above a while ago, but it turned out not to work as well as I hoped. I no longer recall why exactly.

@rwbarton rwbarton removed their assignment Jul 1, 2016
@gvanrossum gvanrossum added this to the Future milestone Jul 7, 2016
@gvanrossum gvanrossum removed this from the Future milestone Mar 29, 2017
@ilevkivskyi
Copy link
Member

Closing as a duplicate of #5874 (it is rather another way around, but the other issue has a wider scope).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants