Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ def _infer_constraints(
return handle_recursive_union(template, actual, direction)
return []

if isinstance(actual, TypeVarType) and not actual.id.is_meta_var():
# Unless template is also a type variable (that is handled above), using the upper
# bound for inference will usually give better result for actual that is a type variable.
actual = get_proper_type(actual.upper_bound)

# Remaining cases are handled by ConstraintBuilderVisitor.
return template.accept(ConstraintBuilderVisitor(actual, direction, skip_neg_op))

Expand Down
12 changes: 12 additions & 0 deletions test-data/unit/check-inference.test
Original file line number Diff line number Diff line change
Expand Up @@ -3686,3 +3686,15 @@ def g(*args: str) -> None: pass
reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \
# E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]"
[builtins fixtures/list.pyi]

[case testInferenceAgainstTypeVarActualBound]
from typing import Callable, TypeVar

T = TypeVar("T")
S = TypeVar("S")
def test(f: Callable[[T], S]) -> Callable[[T], S]: ...

F = TypeVar("F", bound=Callable[..., object])
def dec(f: F) -> F:
reveal_type(test(f)) # N: Revealed type is "def (Any) -> builtins.object"
return f