Skip to content

Commit

Permalink
Mitigate a regression in the external context (#6006)
Browse files Browse the repository at this point in the history
Mitigates #5738 by reverting heuristics for type variable return vs instance type outer context changed in #5699. Both versions are kind of unprincipled, but the old one _seems_ to work better in real codebases.

This doesn't touch the core fixes in #5699
  • Loading branch information
ilevkivskyi authored Dec 5, 2018
1 parent 8dafdc8 commit 393e9ab
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 3 deletions.
6 changes: 3 additions & 3 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
TupleType, TypedDictType, Instance, TypeVarType, ErasedType, UnionType,
PartialType, DeletedType, UninhabitedType, TypeType, TypeOfAny, LiteralType,
true_only, false_only, is_named_instance, function_type, callable_type, FunctionLike,
StarType, is_optional, remove_optional, is_invariant_instance
StarType, is_optional, remove_optional, is_generic_instance
)
from mypy.nodes import (
NameExpr, RefExpr, Var, FuncDef, OverloadedFuncDef, TypeInfo, CallExpr,
Expand Down Expand Up @@ -885,13 +885,13 @@ def infer_function_type_arguments_using_context(
# variables in an expression are inferred at the same time.
# (And this is hard, also we need to be careful with lambdas that require
# two passes.)
if isinstance(ret_type, TypeVarType) and not is_invariant_instance(ctx):
if isinstance(ret_type, TypeVarType) and not is_generic_instance(ctx):
# Another special case: the return type is a type variable. If it's unrestricted,
# we could infer a too general type for the type variable if we use context,
# and this could result in confusing and spurious type errors elsewhere.
#
# Give up and just use function arguments for type inference. As an exception,
# if the context is an invariant instance type, actually use it as context, as
# if the context is a generic instance type, actually use it as context, as
# this *seems* to usually be the reasonable thing to do.
#
# See also github issues #462 and #360.
Expand Down
4 changes: 4 additions & 0 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2056,6 +2056,10 @@ def union_items(typ: Type) -> List[Type]:
return [typ]


def is_generic_instance(tp: Type) -> bool:
return isinstance(tp, Instance) and bool(tp.args)


def is_invariant_instance(tp: Type) -> bool:
if not isinstance(tp, Instance) or not tp.args:
return False
Expand Down
26 changes: 26 additions & 0 deletions test-data/unit/check-inference-context.test
Original file line number Diff line number Diff line change
Expand Up @@ -1283,3 +1283,29 @@ def f(x: Optional[T] = None) -> List[T]: ...

y: List[str] = f() # E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[str]")
[builtins fixtures/list.pyi]

[case testUseCovariantGenericOuterContext]
from typing import TypeVar, Callable, Tuple

T = TypeVar('T')

def f(x: Callable[..., T]) -> T:
return x()

x: Tuple[str, ...] = f(tuple)
[builtins fixtures/tuple.pyi]
[out]

[case testUseCovariantGenericOuterContextUserDefined]
from typing import TypeVar, Callable, Generic

T_co = TypeVar('T_co', covariant=True)
T = TypeVar('T')

class G(Generic[T_co]): ...

def f(x: Callable[..., T]) -> T:
return x()

x: G[str] = f(G)
[out]

0 comments on commit 393e9ab

Please sign in to comment.