From 262193f71e2687cfedc365e5755dc94860c21c79 Mon Sep 17 00:00:00 2001 From: Reid Barton Date: Tue, 3 May 2016 10:44:25 -0700 Subject: [PATCH] Specialize type variables of generic functions that are arguments --- mypy/checkexpr.py | 17 +++++++++++++-- mypy/expandtype.py | 3 +-- mypy/test/data/check-inference-context.test | 23 +++++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 658c9be0a9ca2..9b268a34db78d 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -27,7 +27,7 @@ from mypy import messages from mypy.infer import infer_type_arguments, infer_function_type_arguments from mypy import join -from mypy.subtypes import is_subtype, is_equivalent +from mypy.subtypes import is_subtype, is_equivalent, unify_generic_callable from mypy import applytype from mypy import erasetype from mypy.checkmember import analyze_member_access, type_object_type @@ -327,7 +327,20 @@ def infer_arg_types_in_context2( for i, actuals in enumerate(formal_to_actual): for ai in actuals: if arg_kinds[ai] not in (nodes.ARG_STAR, nodes.ARG_STAR2): - res[ai] = self.accept(args[ai], callee.arg_types[i]) + callee_arg_type = callee.arg_types[i] + arg_type = self.accept(args[ai], callee_arg_type) + if (isinstance(arg_type, CallableType) and + isinstance(callee_arg_type, CallableType)): + # The argument is a function and might be generic. + # Specialize its type variables as necessary to + # match the expected argument type. + unified_type = unify_generic_callable( + arg_type, callee_arg_type, False) + if unified_type is not None: + arg_type = unified_type + # If unified_type is None, leave the type alone; + # we'll probably produce an error soon. + res[ai] = arg_type # Fill in the rest of the argument types. for i, t in enumerate(res): diff --git a/mypy/expandtype.py b/mypy/expandtype.py index d08a11fb8a4ca..1f8ee9da13cbb 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -57,8 +57,7 @@ def visit_deleted_type(self, t: DeletedType) -> Type: return t def visit_erased_type(self, t: ErasedType) -> Type: - # Should not get here. - raise RuntimeError() + return t def visit_instance(self, t: Instance) -> Type: args = self.expand_types(t.args) diff --git a/mypy/test/data/check-inference-context.test b/mypy/test/data/check-inference-context.test index a63d2e8ca18ae..3b9d9203602f6 100644 --- a/mypy/test/data/check-inference-context.test +++ b/mypy/test/data/check-inference-context.test @@ -678,6 +678,29 @@ class A: pass [builtins fixtures/list.py] [out] +[case testFunctionReferenceAndHigherOrderFunction] +from typing import TypeVar, Callable, List, Any +t = TypeVar('t') +s = TypeVar('s') +u = TypeVar('u', str, int) +def apply(func: Callable[[t], s], arg: t) -> s: pass +def f(x: t) -> List[t]: pass +def g(x: u) -> List[u]: pass + +a = '0' +a1 = apply(f, a) +a1() # E: List[str] not callable +a2 = apply(g, a) +a2() # E: List[str] not callable +b = '0' # type: Any +b1 = apply(f, b) +b1() # E: List[Any] not callable +c = [1] +# The error below is not great--the real issue is that c is not a valid argument to g. +# Important thing is that the call is rejected. +c1 = apply(g, c) # E: Argument 1 to "apply" has incompatible type Callable[[u], List[u]]; expected Callable[[List[int]], List[u]] +[builtins fixtures/list.py] + [case testChainedAssignmentInferenceContexts] from typing import List i = None # type: List[int]