Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
21 changes: 20 additions & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5941,7 +5941,12 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F
ctx_else_type = self.analyze_cond_branch(
else_map, e.else_expr, context=ctx, allow_none_return=allow_none_return
)
ctx = make_simplified_union([ctx_if_type, ctx_else_type])
if has_ambiguous_uninhabited_component(ctx_if_type):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without this change, testUnificationDictWithEmptyListLeft and testUnificationDictWithEmptyListRight start failing

ctx = ctx_else_type
elif has_ambiguous_uninhabited_component(ctx_else_type):
ctx = ctx_if_type
else:
ctx = make_simplified_union([ctx_if_type, ctx_else_type])

if_type = self.analyze_cond_branch(
if_map, e.if_expr, context=ctx, allow_none_return=allow_none_return
Expand Down Expand Up @@ -6636,6 +6641,20 @@ def visit_uninhabited_type(self, t: UninhabitedType) -> bool:
return True


def has_ambiguous_uninhabited_component(t: Type) -> bool:
return t.accept(HasAmbiguousUninhabitedComponentsQuery())


class HasAmbiguousUninhabitedComponentsQuery(types.BoolTypeQuery):
"""Visitor for querying whether a type has an ambiguous UninhabitedType component."""

def __init__(self) -> None:
super().__init__(types.ANY_STRATEGY)

def visit_uninhabited_type(self, t: UninhabitedType) -> bool:
return t.ambiguous


def arg_approximate_similarity(actual: Type, formal: Type) -> bool:
"""Return if caller argument (actual) is roughly compatible with signature arg (formal).

Expand Down
33 changes: 4 additions & 29 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
ArgKind,
TypeInfo,
)
from mypy.type_visitor import ALL_STRATEGY, BoolTypeQuery
from mypy.types import (
TUPLE_LIKE_INSTANCE_NAMES,
AnyType,
Expand Down Expand Up @@ -397,13 +396,14 @@ def _infer_constraints(
# be a supertype of the potential subtype, some item of the Union
# must be a supertype of it.
if direction == SUBTYPE_OF and isinstance(actual, UnionType):
# If some of items is not a complete type, disregard that.
items = simplify_away_incomplete_types(actual.items)
# We infer constraints eagerly -- try to find constraints for a type
# variable if possible. This seems to help with some real-world
# use cases.
return any_constraints(
[infer_constraints_if_possible(template, a_item, direction) for a_item in items],
[
infer_constraints_if_possible(template, a_item, direction)
for a_item in actual.items
],
eager=True,
)
if direction == SUPERTYPE_OF and isinstance(template, UnionType):
Expand Down Expand Up @@ -652,31 +652,6 @@ def _is_similar_constraints(x: list[Constraint], y: list[Constraint]) -> bool:
return True


def simplify_away_incomplete_types(types: Iterable[Type]) -> list[Type]:
complete = [typ for typ in types if is_complete_type(typ)]
if complete:
return complete
else:
return list(types)


def is_complete_type(typ: Type) -> bool:
"""Is a type complete?

A complete doesn't have uninhabited type components or (when not in strict
optional mode) None components.
"""
return typ.accept(CompleteTypeVisitor())


class CompleteTypeVisitor(BoolTypeQuery):
def __init__(self) -> None:
super().__init__(ALL_STRATEGY)

def visit_uninhabited_type(self, t: UninhabitedType) -> bool:
return False


class ConstraintBuilderVisitor(TypeVisitor[list[Constraint]]):
"""Visitor class for inferring type constraints."""

Expand Down
18 changes: 18 additions & 0 deletions test-data/unit/check-inference-context.test
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,24 @@ reveal_type(c.f([])) # N: Revealed type is "builtins.list[builtins.int]"
reveal_type(c.f(None)) # N: Revealed type is "builtins.list[builtins.int] | None"
[builtins fixtures/list.pyi]

[case testUnionWithTupleNeverBranch]
# https://github.com/python/mypy/issues/20608
from __future__ import annotations
from typing import Callable, Never, Generic, TypeVar

T = TypeVar('T')
class ValueType(Generic[T]): pass
class Bundle(Generic[T]): pass

F = TypeVar('F')

def fail_value_type_inference(self_bundle: ValueType[F]) -> tuple[Never, Bundle[ValueType[F]]] | tuple[int, Bundle[ValueType[int]]]:
raise

x = fail_value_type_inference
x = fail_value_type_inference
[builtins fixtures/tuple.pyi]

[case testGenericMethodCalledInGenericContext]
from typing import TypeVar, Generic

Expand Down