From 917d781d6c80efc1e0e0c073dabd45819644785b Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sat, 11 Oct 2025 01:08:24 +0200 Subject: [PATCH 1/9] Add a crashing test --- test-data/unit/check-overloading.test | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index be55a182b87b..c29d1d4c6c94 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6852,3 +6852,18 @@ if isinstance(headers, dict): reveal_type(headers) # N: Revealed type is "Union[__main__.Headers, typing.Iterable[tuple[builtins.bytes, builtins.bytes]]]" [builtins fixtures/isinstancelist.pyi] + +[case testOverloadSelectionIgnoresContext] +from typing import TypeVar, overload + +_T = TypeVar("_T") + +@overload # type: ignore[no-overload-impl] +def gather(f1: _T) -> tuple[_T]: ... +@overload +def gather(*fns: object) -> int: ... + +def crash() -> None: + foo: str + (foo,) = gather(0) +[builtins fixtures/tuple.pyi] From 6ff817fe48b315a79d8ea4c9ee9ade47817a7210 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sat, 11 Oct 2025 01:10:41 +0200 Subject: [PATCH 2/9] Experiment: literally just ignore the context altogether? --- mypy/checkexpr.py | 17 +++++++++++++++-- test-data/unit/check-overloading.test | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index b8f9bf087467..9c28663b9094 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2902,6 +2902,9 @@ def infer_overload_return_type( assert self.msg is self.chk.msg with self.msg.filter_errors() as w: with self.chk.local_type_map as m: + # Overload selection should not depend on the context. + # During this step pretend that we do not have any external information. + self.type_context.append(None) ret_type, infer_type = self.check_call( callee=typ, args=args, @@ -2911,13 +2914,23 @@ def infer_overload_return_type( callable_name=callable_name, object_type=object_type, ) + self.type_context.pop() is_match = not w.has_new_errors() if is_match: # Return early if possible; otherwise record info, so we can # check for ambiguity due to 'Any' below. if not args_contain_any: - self.chk.store_types(m) - return ret_type, infer_type + # Yes, just again + # FIXME: find a way to avoid doing this + return self.check_call( + callee=typ, + args=args, + arg_kinds=arg_kinds, + arg_names=arg_names, + context=context, + callable_name=callable_name, + object_type=object_type, + ) p_infer_type = get_proper_type(infer_type) if isinstance(p_infer_type, CallableType): # Prefer inferred types if possible, this will avoid false triggers for diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index c29d1d4c6c94..6aaa7a0d98f5 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6865,5 +6865,5 @@ def gather(*fns: object) -> int: ... def crash() -> None: foo: str - (foo,) = gather(0) + (foo,) = gather(0) # E: Incompatible types in assignment (expression has type "int", variable has type "str") [builtins fixtures/tuple.pyi] From c119fc89dd3c3c2430a1bff9d43a18916a6e5c19 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sat, 11 Oct 2025 17:03:02 +0200 Subject: [PATCH 3/9] Do it the crude way to see real implications --- mypy/checkexpr.py | 65 ++++++++++++++++++--------- test-data/unit/check-overloading.test | 2 +- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9c28663b9094..8167ebefa98b 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2892,33 +2892,31 @@ def infer_overload_return_type( Assumes all of the given targets have argument counts compatible with the caller. """ - matches: list[CallableType] = [] - return_types: list[Type] = [] - inferred_types: list[Type] = [] args_contain_any = any(map(has_any_type, arg_types)) - type_maps: list[dict[Expression, Type]] = [] + # First do a pass without external context and find all overloads that + # can be possibly matched. If no Any is present among args, bail out early + # on the first match. + candidates = [] for typ in plausible_targets: assert self.msg is self.chk.msg - with self.msg.filter_errors() as w: - with self.chk.local_type_map as m: - # Overload selection should not depend on the context. - # During this step pretend that we do not have any external information. - self.type_context.append(None) - ret_type, infer_type = self.check_call( - callee=typ, - args=args, - arg_kinds=arg_kinds, - arg_names=arg_names, - context=context, - callable_name=callable_name, - object_type=object_type, - ) - self.type_context.pop() + with self.msg.filter_errors() as w, self.chk.local_type_map as m: + # Overload selection should not depend on the context. + # During this step pretend that we do not have any external information. + self.type_context.append(None) + ret_type, infer_type = self.check_call( + callee=typ, + args=args, + arg_kinds=arg_kinds, + arg_names=arg_names, + context=context, + callable_name=callable_name, + object_type=object_type, + ) + self.type_context.pop() is_match = not w.has_new_errors() if is_match: - # Return early if possible; otherwise record info, so we can - # check for ambiguity due to 'Any' below. + # Return early if possible if not args_contain_any: # Yes, just again # FIXME: find a way to avoid doing this @@ -2931,6 +2929,31 @@ def infer_overload_return_type( callable_name=callable_name, object_type=object_type, ) + candidates.append(typ) + + # Repeat the same with outer context, but only for the select candidates. + matches: list[CallableType] = [] + return_types: list[Type] = [] + inferred_types: list[Type] = [] + type_maps: list[dict[Expression, Type]] = [] + + for typ in candidates: + assert self.msg is self.chk.msg + with self.msg.filter_errors() as w, self.chk.local_type_map as m: + # Overload selection should not depend on the context. + # During this step pretend that we do not have any external information. + ret_type, infer_type = self.check_call( + callee=typ, + args=args, + arg_kinds=arg_kinds, + arg_names=arg_names, + context=context, + callable_name=callable_name, + object_type=object_type, + ) + is_match = not w.has_new_errors() + if is_match: + # Record info, so we can check for ambiguity due to 'Any' below. p_infer_type = get_proper_type(infer_type) if isinstance(p_infer_type, CallableType): # Prefer inferred types if possible, this will avoid false triggers for diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 6aaa7a0d98f5..0d581c2ddcb6 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6865,5 +6865,5 @@ def gather(*fns: object) -> int: ... def crash() -> None: foo: str - (foo,) = gather(0) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + (foo,) = gather(0) # E: Argument 1 to "gather" has incompatible type "int"; expected "str" [builtins fixtures/tuple.pyi] From 654d2e394f004ef5339cff1238afd0668b4aae7f Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 24 Oct 2025 01:21:04 +0200 Subject: [PATCH 4/9] Work around the numpy failures for now --- mypy/applytype.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/mypy/applytype.py b/mypy/applytype.py index dfeaf7752d21..cd0f688fe257 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -18,6 +18,7 @@ ParamSpecType, PartialType, ProperType, + TupleType, Type, TypeAliasType, TypeVarId, @@ -27,10 +28,20 @@ UninhabitedType, UnpackType, get_proper_type, + get_proper_types, remove_dups, ) +def _is_tuple_any(typ: ProperType) -> bool: + return ( + isinstance(typ, Instance) + and typ.type.fullname == "builtins.tuple" + and len(typ.args) == 1 + and isinstance(get_proper_type(typ.args[0]), AnyType) + ) + + def get_target_type( tvar: TypeVarLikeType, type: Type, @@ -56,6 +67,18 @@ def get_target_type( # is also a legal value of T. if all(any(mypy.subtypes.is_same_type(v, v1) for v in values) for v1 in p_type.values): return type + if _is_tuple_any(p_type) and all( + isinstance(v, TupleType) + or isinstance(v, Instance) + and v.type.fullname == "builtins.tuple" + for v in get_proper_types(values) + ): + # tuple[Any, ...] is compatible with any tuple bounds. It is important + # to not select one of the values in cases like numpy arrays shape. Given + # T = TypeVar("T", tuple[()], tuple[int], tuple[int, int]) + # and a proposed solution `tuple[Any, ...]`, we do not want to choose + # tuple[()] arbitrarily. + return type matching = [] for value in values: if mypy.subtypes.is_subtype(type, value): From 674b25f49ca361df5f90ead6a8a615d1686ba628 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 24 Oct 2025 04:05:36 +0200 Subject: [PATCH 5/9] Before you murder me, a constraint `T <: (A | B)` where `T: (A | C)` can be satisfied by `T = A` --- mypy/constraints.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 96c0c7ccaf35..7ecef2c29efd 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -571,21 +571,21 @@ def any_constraints(options: list[list[Constraint] | None], *, eager: bool) -> l def filter_satisfiable(option: list[Constraint] | None) -> list[Constraint] | None: """Keep only constraints that can possibly be satisfied. - Currently, we filter out constraints where target is not a subtype of the upper bound. + Currently, we filter out constraints where target does not overlap with the upper bound. Since those can be never satisfied. We may add more cases in future if it improves type inference. """ + from mypy.meet import is_overlapping_types + if not option: return option satisfiable = [] for c in option: if isinstance(c.origin_type_var, TypeVarType) and c.origin_type_var.values: - if any( - mypy.subtypes.is_subtype(c.target, value) for value in c.origin_type_var.values - ): + if any(is_overlapping_types(c.target, value) for value in c.origin_type_var.values): satisfiable.append(c) - elif mypy.subtypes.is_subtype(c.target, c.origin_type_var.upper_bound): + elif is_overlapping_types(c.target, c.origin_type_var.upper_bound): satisfiable.append(c) if not satisfiable: return None From dd0087d3a41156a4fb7d25e66cbe91389f745054 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 24 Oct 2025 04:07:13 +0200 Subject: [PATCH 6/9] Sync typevar IDs --- test-data/unit/check-plugin-attrs.test | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 42f21e945ef0..1e07f8be64ac 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -185,10 +185,10 @@ from attr import attrib, attrs class A: a: int reveal_type(A) # N: Revealed type is "def (a: builtins.int) -> __main__.A" -reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`3, other: _AT`3) -> builtins.bool" -reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" -reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" -reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" +reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" +reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" +reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" +reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`7, other: _AT`7) -> builtins.bool" A(1) < A(2) A(1) <= A(2) @@ -990,10 +990,10 @@ class C(A, B): pass @attr.s class D(A): pass -reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`29, other: _AT`29) -> builtins.bool" -reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`30, other: _AT`30) -> builtins.bool" -reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`31, other: _AT`31) -> builtins.bool" -reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`32, other: _AT`32) -> builtins.bool" +reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`33, other: _AT`33) -> builtins.bool" +reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`34, other: _AT`34) -> builtins.bool" +reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`35, other: _AT`35) -> builtins.bool" +reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`36, other: _AT`36) -> builtins.bool" A() < A() B() < B() From 2e83a0c1654fdb8c9efa604b0375f33c99eefc1d Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 24 Oct 2025 04:29:43 +0200 Subject: [PATCH 7/9] Use meets in pre_validate_solutions? --- mypy/solve.py | 13 +++++-------- test-data/unit/check-dataclasses.test | 6 +++--- test-data/unit/check-literal.test | 15 +++++++++++++++ test-data/unit/check-namedtuple.test | 2 +- test-data/unit/check-overloading.test | 8 ++++++++ 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/mypy/solve.py b/mypy/solve.py index fbbcac2520ad..4b7742c16eaf 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -576,16 +576,13 @@ def pre_validate_solutions( new_solutions.append(s) continue if s is not None and not is_subtype(s, t.upper_bound): - bound_satisfies_all = True + bound = t.upper_bound for c in constraints: - if c.op == SUBTYPE_OF and not is_subtype(t.upper_bound, c.target): - bound_satisfies_all = False + bound = meet_types(bound, c.target) + if isinstance(bound, UninhabitedType): break - if c.op == SUPERTYPE_OF and not is_subtype(c.target, t.upper_bound): - bound_satisfies_all = False - break - if bound_satisfies_all: - new_solutions.append(t.upper_bound) + else: + new_solutions.append(bound) continue new_solutions.append(s) return new_solutions diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index f43c49c200c8..bab045ff7ec9 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2122,7 +2122,7 @@ TA = TypeVar('TA', bound=A) TB = TypeVar('TB', bound=B) def f(b_or_t: Union[TA, TB, int]) -> None: - a2 = replace(b_or_t) # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[TA, TB, int]" + a2 = replace(b_or_t) # E: Argument 1 to "replace" has incompatible type "Union[TA, TB, int]"; expected "TA" [builtins fixtures/tuple.pyi] @@ -2202,9 +2202,9 @@ replace(None) # E: Value of type variable "_DataclassT" of "replace" cannot be from dataclasses import is_dataclass, replace def f(x: object) -> None: - _ = replace(x) # E: Value of type variable "_DataclassT" of "replace" cannot be "object" + _ = replace(x) # E: Argument 1 to "replace" has incompatible type "object"; expected "DataclassInstance" if is_dataclass(x): - _ = replace(x) # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[DataclassInstance, type[DataclassInstance]]" + _ = replace(x) # E: Argument 1 to "replace" has incompatible type "Union[DataclassInstance, type[DataclassInstance]]"; expected "DataclassInstance" if not isinstance(x, type): _ = replace(x) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index ce0ae2844bae..ac038a19cd30 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1662,6 +1662,21 @@ reveal_type(func2(b)) # N: Revealed type is "Literal[4]" reveal_type(func2(c)) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] [out] +main:13: error: Value of type variable "TLiteral" of "func1" cannot be "TInt" +main:20: note: Revealed type is "def [TLiteral <: Literal[3]] (x: TLiteral`-1) -> TLiteral`-1" +main:22: note: Revealed type is "Literal[3]" +main:23: note: Revealed type is "Literal[3]" +main:24: error: Value of type variable "TLiteral" of "func1" cannot be "Literal[4]" +main:24: note: Revealed type is "Literal[4]" +main:25: error: Value of type variable "TLiteral" of "func1" cannot be "Literal[4]" +main:25: note: Revealed type is "Literal[4]" +main:26: note: Revealed type is "Literal[3]" +main:26: error: Argument 1 to "func1" has incompatible type "int"; expected "Literal[3]" +main:28: note: Revealed type is "builtins.int" +main:29: note: Revealed type is "Literal[3]" +main:30: note: Revealed type is "builtins.int" +main:31: note: Revealed type is "Literal[4]" +main:32: note: Revealed type is "builtins.int" [case testLiteralAndGenericsRespectsValueRestriction] from typing import Literal, TypeVar diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 45de2a9e50ae..36e0935e8226 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1362,7 +1362,7 @@ class NT(NamedTuple, Generic[T]): return self._replace() class SNT(NT[int]): ... -reveal_type(SNT("test", 42).meth()) # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.SNT]" +reveal_type(SNT("test", 42).meth()) # N: Revealed type is "tuple[builtins.str, Never, fallback=__main__.SNT]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 0d581c2ddcb6..e0c49c662c4c 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1335,6 +1335,14 @@ def g(x: U, y: V) -> None: f([x, y]) # E: Value of type variable "T" of "f" cannot be "object" [builtins fixtures/list.pyi] [out] +tmp/foo.pyi:12: error: "mystr" not callable +tmp/foo.pyi:13: error: No overload variant of "f" matches argument type "V" +tmp/foo.pyi:13: note: Possible overload variants: +tmp/foo.pyi:13: note: def [T: str] f(x: T) -> T +tmp/foo.pyi:13: note: def [T: str] f(x: list[T]) -> None +tmp/foo.pyi:15: note: Revealed type is "None" +tmp/foo.pyi:16: error: Value of type variable "T" of "f" cannot be "V" +tmp/foo.pyi:17: error: List item 1 has incompatible type "V"; expected "str" [case testOverloadOverlapWithTypeVars] from foo import * From cab649db59a5158b636f0c18914991b7b1d449e3 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 24 Oct 2025 04:32:58 +0200 Subject: [PATCH 8/9] Two more affected tests that do not work with --update-data --- test-data/unit/check-incremental.test | 16 ++++++++-------- test-data/unit/pythoneval.test | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 94f65a950062..bd570415bdc3 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -3061,10 +3061,10 @@ main:15: error: Unsupported left operand type for >= ("NoCmp") [case testAttrsIncrementalDunder] from a import A reveal_type(A) # N: Revealed type is "def (a: builtins.int) -> a.A" -reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`3, other: _AT`3) -> builtins.bool" -reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" -reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" -reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" +reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" +reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" +reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" +reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`7, other: _AT`7) -> builtins.bool" A(1) < A(2) A(1) <= A(2) @@ -3098,10 +3098,10 @@ class A: [stale] [out2] main:2: note: Revealed type is "def (a: builtins.int) -> a.A" -main:3: note: Revealed type is "def [_AT] (self: _AT`3, other: _AT`3) -> builtins.bool" -main:4: note: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" -main:5: note: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" -main:6: note: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" +main:3: note: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" +main:4: note: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" +main:5: note: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" +main:6: note: Revealed type is "def [_AT] (self: _AT`7, other: _AT`7) -> builtins.bool" main:15: error: Unsupported operand types for < ("A" and "int") main:16: error: Unsupported operand types for <= ("A" and "int") main:17: error: Unsupported operand types for > ("A" and "int") diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 2069d082df17..fa500036b547 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -825,7 +825,7 @@ MyDDict(dict)[0] _program.py:6: error: Argument 1 to "defaultdict" has incompatible type "type[list[_T]]"; expected "Optional[Callable[[], str]]" _program.py:9: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int" _program.py:9: error: Incompatible types in assignment (expression has type "int", target has type "str") -_program.py:19: error: Argument 1 to "tst" has incompatible type "defaultdict[str, list[Never]]"; expected "defaultdict[int, list[Never]]" +_program.py:19: error: Dict entry 0 has incompatible type "str": "list[Never]"; expected "int": "list[Never]" _program.py:23: error: Invalid index type "str" for "MyDDict[dict[Never, Never]]"; expected type "int" [case testCollectionsAliases] From 4902af8ff778fd498d3ede4c22c86f27e75bd1b5 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 24 Oct 2025 04:48:05 +0200 Subject: [PATCH 9/9] Revert "Use meets in pre_validate_solutions?" This reverts commit 2e83a0c1654fdb8c9efa604b0375f33c99eefc1d. --- mypy/solve.py | 13 ++++++++----- test-data/unit/check-dataclasses.test | 6 +++--- test-data/unit/check-literal.test | 15 --------------- test-data/unit/check-namedtuple.test | 2 +- test-data/unit/check-overloading.test | 8 -------- 5 files changed, 12 insertions(+), 32 deletions(-) diff --git a/mypy/solve.py b/mypy/solve.py index 4b7742c16eaf..fbbcac2520ad 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -576,13 +576,16 @@ def pre_validate_solutions( new_solutions.append(s) continue if s is not None and not is_subtype(s, t.upper_bound): - bound = t.upper_bound + bound_satisfies_all = True for c in constraints: - bound = meet_types(bound, c.target) - if isinstance(bound, UninhabitedType): + if c.op == SUBTYPE_OF and not is_subtype(t.upper_bound, c.target): + bound_satisfies_all = False break - else: - new_solutions.append(bound) + if c.op == SUPERTYPE_OF and not is_subtype(c.target, t.upper_bound): + bound_satisfies_all = False + break + if bound_satisfies_all: + new_solutions.append(t.upper_bound) continue new_solutions.append(s) return new_solutions diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index bab045ff7ec9..f43c49c200c8 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2122,7 +2122,7 @@ TA = TypeVar('TA', bound=A) TB = TypeVar('TB', bound=B) def f(b_or_t: Union[TA, TB, int]) -> None: - a2 = replace(b_or_t) # E: Argument 1 to "replace" has incompatible type "Union[TA, TB, int]"; expected "TA" + a2 = replace(b_or_t) # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[TA, TB, int]" [builtins fixtures/tuple.pyi] @@ -2202,9 +2202,9 @@ replace(None) # E: Value of type variable "_DataclassT" of "replace" cannot be from dataclasses import is_dataclass, replace def f(x: object) -> None: - _ = replace(x) # E: Argument 1 to "replace" has incompatible type "object"; expected "DataclassInstance" + _ = replace(x) # E: Value of type variable "_DataclassT" of "replace" cannot be "object" if is_dataclass(x): - _ = replace(x) # E: Argument 1 to "replace" has incompatible type "Union[DataclassInstance, type[DataclassInstance]]"; expected "DataclassInstance" + _ = replace(x) # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[DataclassInstance, type[DataclassInstance]]" if not isinstance(x, type): _ = replace(x) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index ac038a19cd30..ce0ae2844bae 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1662,21 +1662,6 @@ reveal_type(func2(b)) # N: Revealed type is "Literal[4]" reveal_type(func2(c)) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] [out] -main:13: error: Value of type variable "TLiteral" of "func1" cannot be "TInt" -main:20: note: Revealed type is "def [TLiteral <: Literal[3]] (x: TLiteral`-1) -> TLiteral`-1" -main:22: note: Revealed type is "Literal[3]" -main:23: note: Revealed type is "Literal[3]" -main:24: error: Value of type variable "TLiteral" of "func1" cannot be "Literal[4]" -main:24: note: Revealed type is "Literal[4]" -main:25: error: Value of type variable "TLiteral" of "func1" cannot be "Literal[4]" -main:25: note: Revealed type is "Literal[4]" -main:26: note: Revealed type is "Literal[3]" -main:26: error: Argument 1 to "func1" has incompatible type "int"; expected "Literal[3]" -main:28: note: Revealed type is "builtins.int" -main:29: note: Revealed type is "Literal[3]" -main:30: note: Revealed type is "builtins.int" -main:31: note: Revealed type is "Literal[4]" -main:32: note: Revealed type is "builtins.int" [case testLiteralAndGenericsRespectsValueRestriction] from typing import Literal, TypeVar diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 36e0935e8226..45de2a9e50ae 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1362,7 +1362,7 @@ class NT(NamedTuple, Generic[T]): return self._replace() class SNT(NT[int]): ... -reveal_type(SNT("test", 42).meth()) # N: Revealed type is "tuple[builtins.str, Never, fallback=__main__.SNT]" +reveal_type(SNT("test", 42).meth()) # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.SNT]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index e0c49c662c4c..0d581c2ddcb6 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1335,14 +1335,6 @@ def g(x: U, y: V) -> None: f([x, y]) # E: Value of type variable "T" of "f" cannot be "object" [builtins fixtures/list.pyi] [out] -tmp/foo.pyi:12: error: "mystr" not callable -tmp/foo.pyi:13: error: No overload variant of "f" matches argument type "V" -tmp/foo.pyi:13: note: Possible overload variants: -tmp/foo.pyi:13: note: def [T: str] f(x: T) -> T -tmp/foo.pyi:13: note: def [T: str] f(x: list[T]) -> None -tmp/foo.pyi:15: note: Revealed type is "None" -tmp/foo.pyi:16: error: Value of type variable "T" of "f" cannot be "V" -tmp/foo.pyi:17: error: List item 1 has incompatible type "V"; expected "str" [case testOverloadOverlapWithTypeVars] from foo import *