diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 603acbe84f0f..5dabf850a337 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2069,8 +2069,12 @@ def infer_function_type_arguments_using_context( # in this case external context is almost everything we have. if not is_generic_instance(ctx) and not is_literal_type_like(ctx): return callable.copy_modified() + + # GH#19304 + # needs is_supertype=True since the purpose is to allow sound upcasting + # if the context requires it, such as e.g. `x: list[object] = [x for x in (1,2,3)]` args = infer_type_arguments( - callable.variables, ret_type, erased_ctx, skip_unsatisfied=True + callable.variables, ret_type, erased_ctx, is_supertype=True, skip_unsatisfied=True ) # Only substitute non-Uninhabited and non-erased types. new_args: list[Type | None] = [] diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 33271a3cc04c..41cc3cf8ddde 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -308,6 +308,24 @@ main:5: error: Unsupported operand types for ^ ("A" and "A") main:6: error: Unsupported operand types for << ("A" and "B") main:7: error: Unsupported operand types for >> ("A" and "A") +[case testBinaryOperatorContext] +from typing import TypeVar, Generic, Iterable, Iterator, Union + +T = TypeVar("T") +S = TypeVar("S") + +class Vec(Generic[T]): + def __init__(self, iterable: Iterable[T], /) -> None: ... + def __iter__(self) -> Iterator[T]: yield from [] + def __add__(self, value: "Vec[S]", /) -> "Vec[Union[S, T]]": return Vec([]) + +def fmt(arg: Iterable[Union[int, str]]) -> None: ... + +l1: Vec[int] = Vec([1]) +l2: Vec[int] = Vec([1]) +fmt(l1 + l2) +[builtins fixtures/list.pyi] + [case testBooleanAndOr] a: A b: bool @@ -460,10 +478,6 @@ class A: def __contains__(self, x: 'A') -> str: pass [builtins fixtures/bool.pyi] -[case testInWithInvalidArgs] -a = 1 in ([1] + ['x']) # E: List item 0 has incompatible type "str"; expected "int" -[builtins fixtures/list.pyi] - [case testEq] a: A b: bool diff --git a/test-data/unit/fixtures/list.pyi b/test-data/unit/fixtures/list.pyi index 3dcdf18b2faa..032abfc6beed 100644 --- a/test-data/unit/fixtures/list.pyi +++ b/test-data/unit/fixtures/list.pyi @@ -1,8 +1,9 @@ # Builtins stub used in list-related test cases. -from typing import TypeVar, Generic, Iterable, Iterator, Sequence, overload +from typing import TypeVar, Generic, Iterable, Iterator, Sequence, overload, Union T = TypeVar('T') +_S = TypeVar("_S") class object: def __init__(self) -> None: pass @@ -19,7 +20,7 @@ class list(Sequence[T]): def __iter__(self) -> Iterator[T]: pass def __len__(self) -> int: pass def __contains__(self, item: object) -> bool: pass - def __add__(self, x: list[T]) -> list[T]: pass + def __add__(self, x: list[_S]) -> list[Union[_S, T]]: pass def __mul__(self, x: int) -> list[T]: pass def __getitem__(self, x: int) -> T: pass def __setitem__(self, x: int, v: T) -> None: pass