From fbea041dde3917e289cb25e84eede0ec6e683f0c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 28 Jul 2021 14:50:58 +0100 Subject: [PATCH] Improve type inference special case that involves union with Any When trying to match `list[T]` and `list[str] | Any`, previously we gave up, because the union items caused conflicting inferred values for `T` (`str` and `Any`). Work around the issue by dropping `Any` constraints if there are multiple sets of contraints, since we prefer more precise types. This fixes false positives resulting from python/typeshed#5557, which changed some return types to contain unions with `Any` items. See mypy primer results in #10881 for a real-world example of this in sphinx. --- mypy/constraints.py | 10 ++++++++++ test-data/unit/check-inference-context.test | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/mypy/constraints.py b/mypy/constraints.py index caf90fa6a5bb..48e1be357d3b 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -216,6 +216,16 @@ def any_constraints(options: List[Optional[List[Constraint]]], eager: bool) -> L # TODO: More generally, if a given (variable, direction) pair appears in # every option, combine the bounds with meet/join. return valid_options[0] + elif len(valid_options) > 1: + # Drop constraints that only refer to "Any" and try again. This way Any types + # in unions don't interfere with type inference. + narrowed_options = [option + for option in valid_options + if not (option and + all(isinstance(get_proper_type(c.target), AnyType) + for c in option))] + if len(narrowed_options) < len(valid_options): + return any_constraints([option for option in narrowed_options], eager) # Otherwise, there are either no valid options or multiple, inconsistent valid # options. Give up and deduce nothing. diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index 82a412eeb359..536675ef6bef 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -1375,3 +1375,11 @@ def f(x: Callable[..., T]) -> T: x: G[str] = f(G) [out] + +[case testConditionalExpressionWithEmptyListAndUnionWithAny] +from typing import Union, List, Any + +def f(x: Union[List[str], Any]) -> None: + a = x if x else [] + reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.str], Any]" +[builtins fixtures/list.pyi]