Skip to content

Type context can be wrongly included in diagnostic type when there is no solution #2537

@randolf-scholz

Description

@randolf-scholz

Summary

Consider a dict[K, V] like class with a get method:

from typing import Any, reveal_type

class Map[K, V]:
    def set(self, key: K, value: V) -> None: ...
    def get[T=None](self, key: Any, default: T = None, /) -> V | T: ...

d: Map[str, int] = Map[str, int]()

# as expected, these are both `int | None`
reveal_type(d.get("key"))  # int | None  ✅️
reveal_type(d.get("key", None))  # int | None  ✅️
# outer context should not overrule the argument type!
result: str = reveal_type(d.get("key", None))  # int | None | str ❌️

d.get("key", None) alone produces int | None as expected, but in the last case we have str as outer context. This gives an unsolvable situation for the type variable T as we would need both T <: str and T :> None. It seems ty gives priority to the outer constraint T <: str here, which seems misguided, as it produces an illogical reveal_type result: int | None | str.

But d.get("key", None) cannot produce a str!

Instead, the inner constraint T :> None should probably be prioritized in this situation.

See also mirror issues in mypy and pyrefly tracker: python/mypy#20576, facebook/pyrefly#2136

Version

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bidirectional inferenceInference of types that takes into account the context of a declared type or expected typediagnosticsRelated to reporting of diagnostics.genericsBugs or features relating to ty's generics implementation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions