Skip to content

Commit b41c8c1

Browse files
authored
Use upper bound as inference fallback more consistently (#16344)
Fixes #16331 Fix is straightforward: do not use the fallback, where we would not give the error in the first place.
1 parent 5ef9c82 commit b41c8c1

File tree

4 files changed

+32
-4
lines changed

4 files changed

+32
-4
lines changed

mypy/checkexpr.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1989,7 +1989,9 @@ def infer_function_type_arguments_using_context(
19891989
# in this case external context is almost everything we have.
19901990
if not is_generic_instance(ctx) and not is_literal_type_like(ctx):
19911991
return callable.copy_modified()
1992-
args = infer_type_arguments(callable.variables, ret_type, erased_ctx)
1992+
args = infer_type_arguments(
1993+
callable.variables, ret_type, erased_ctx, skip_unsatisfied=True
1994+
)
19931995
# Only substitute non-Uninhabited and non-erased types.
19941996
new_args: list[Type | None] = []
19951997
for arg in args:

mypy/infer.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,13 @@ def infer_function_type_arguments(
6363

6464

6565
def infer_type_arguments(
66-
type_vars: Sequence[TypeVarLikeType], template: Type, actual: Type, is_supertype: bool = False
66+
type_vars: Sequence[TypeVarLikeType],
67+
template: Type,
68+
actual: Type,
69+
is_supertype: bool = False,
70+
skip_unsatisfied: bool = False,
6771
) -> list[Type | None]:
6872
# Like infer_function_type_arguments, but only match a single type
6973
# against a generic type.
7074
constraints = infer_constraints(template, actual, SUPERTYPE_OF if is_supertype else SUBTYPE_OF)
71-
return solve_constraints(type_vars, constraints)[0]
75+
return solve_constraints(type_vars, constraints, skip_unsatisfied=skip_unsatisfied)[0]

mypy/solve.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def solve_constraints(
4343
constraints: list[Constraint],
4444
strict: bool = True,
4545
allow_polymorphic: bool = False,
46+
skip_unsatisfied: bool = False,
4647
) -> tuple[list[Type | None], list[TypeVarLikeType]]:
4748
"""Solve type constraints.
4849
@@ -54,6 +55,8 @@ def solve_constraints(
5455
If allow_polymorphic=True, then use the full algorithm that can potentially return
5556
free type variables in solutions (these require special care when applying). Otherwise,
5657
use a simplified algorithm that just solves each type variable individually if possible.
58+
59+
The skip_unsatisfied flag matches the same one in applytype.apply_generic_arguments().
5760
"""
5861
vars = [tv.id for tv in original_vars]
5962
if not vars:
@@ -110,7 +113,7 @@ def solve_constraints(
110113
candidate = AnyType(TypeOfAny.special_form)
111114
res.append(candidate)
112115

113-
if not free_vars:
116+
if not free_vars and not skip_unsatisfied:
114117
# Most of the validation for solutions is done in applytype.py, but here we can
115118
# quickly test solutions w.r.t. to upper bounds, and use the latter (if possible),
116119
# if solutions are actually not valid (due to poor inference context).

test-data/unit/check-inference.test

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3748,3 +3748,22 @@ empty: Dict[NoReturn, NoReturn]
37483748
def bar() -> Union[Dict[str, Any], Dict[int, Any]]:
37493749
return empty
37503750
[builtins fixtures/dict.pyi]
3751+
3752+
[case testUpperBoundInferenceFallbackNotOverused]
3753+
from typing import TypeVar, Protocol, List
3754+
3755+
S = TypeVar("S", covariant=True)
3756+
class Foo(Protocol[S]):
3757+
def foo(self) -> S: ...
3758+
def foo(x: Foo[S]) -> S: ...
3759+
3760+
T = TypeVar("T", bound="Base")
3761+
class Base:
3762+
def foo(self: T) -> T: ...
3763+
class C(Base):
3764+
pass
3765+
3766+
def f(values: List[T]) -> T: ...
3767+
x = foo(f([C()]))
3768+
reveal_type(x) # N: Revealed type is "__main__.C"
3769+
[builtins fixtures/list.pyi]

0 commit comments

Comments
 (0)