Skip to content

Spec Violation: Ambiguous overloads are not resolved to Any #2552

@randolf-scholz

Description

@randolf-scholz

Describe the Bug

I encountered this issue when trying to improve numpy's annotations for np.datetime64 ops. (None is used to indicate the special "Not A Time" marker)

from typing import Any, overload, assert_type

class A[T]:  # covariant
    def get(self) -> T: ...

@overload
def op(l: A[None], r: A[None]) -> A[None]: ...
@overload
def op(l: A[None], r: A[Any]) -> A[None]: ...
@overload
def op(l: A[Any], r: A[None]) -> A[None]: ...
@overload
def op(l: A[Any], r: A[Any]) -> A[Any]: ...
def op(l, r):
    # dummy implementation:
    if l.get() is None or r.get() is None:
        return A[None]()
    return A[Any]()

def test(x: A[None], y: A[Any]) -> None:
    assert_type(op(x, x), A[None])
    assert_type(op(x, y), A[None])  # ❌️: (mypy, zuban)
    assert_type(op(y, x), A[None])  # ❌️: (mypy, zuban)
    assert_type(op(y, y), Any)   # ❌️: (pyright, ty, pyrefly)

According to the typing spec for overloads, in particular step 5 and 6, op(y,y) should be inferred to as Any, because:

  1. l=A[Any], r=A[Any] can be assigned to all signatures
  2. The return types are not equivalent, because not all materializations of A[Any] can be assigned to A[None].

Side note

To be honest I think the specification could be improved: instead of Any, it would also be fair, in my opinion, to return the union of all the return type of all overloads that could be caught by different materializations of Any, so in this case this would mean that op(y,y) is A[None] | A[Any], because if the first or second Any were materialized to None, we would get A[None] and if neither materialized to None, we would get A[Any]. (python/typing#2196)

Sandbox Link

https://pyrefly.org/sandbox/?project=N4IgZglgNgpgziAXKOBDAdgEwEYHsAeAdAA4CeS4ATrgLYAEALqcROgOZ0Q3G6UN0BBdKQA0dXADcYlKLlSYxqOHGkMA%2Bk2IwAOul0BjKEriCA2gBUAuojp0AxHX2TUlCBga7btzDDB02MAwAFCpQYACUdAC0AHx05jaESbq6AAKS0rLyuj5%2BuMRBUDYCpgByuOgwlmKUxWUVVZGxZuWV1nRJhGkZMnKYOb7iBUUtDdV0tWZCpJZNcSWtVYnJ6OlSvdlYg-mFddPjkwtjc6Nty12rPVn9W3nDe8IHDzMnJfvnA3eFNeGInl4OTAAVxoNFInG4sBoMHQDFQDAgFT%2B6C8nD8UEIAWCkQgJkW4koE0xgSCOLxDWRqNRlECQMoKKObVJ-1sNIYdIZpn2zL0t0Y8GC%2BDqi3GpGes2icUWlK8xlUGmYMCCO3wYnw4TEjMaLLocr4Cq0yoKqropA1p0atgcgBlyQDwfzYgmCyGIAF5A7AYcI6vXqTRKnaiOjqzX1NqRex0O0Op2Bt0e9BelGy5Tyv1GoKBs2a4ThiNRuhBMiuNgACwYYiYYiLvigZt0IBEICBCKgcBI5EQIAcAFUWxAmHQwED0PoERU4Ck%2BWBeDR4Wp0CDsNIgkLOLCTnAGLUdWyOYPtCBSovpDZgPgAL4H%2BuN6tgWuEBi0KAUBwABVINLv4LQWDw%2BEcFSQGwdLwoi6AXA4ADKMAwHQZYMMQcCIAA9Mht73rwbDITCyGYLg%2BhwMhTjoEBIFjugyGDrwuoSKg0CoNgsAASREDAZQoEVEM5FtroZAMCWFRROscBgXQAC8dAHgAzIQACMABMV7oCA56Nqgo4QFIABi0AwBQP44AQ7YqUAA

(Only applicable for extension issues) IDE Information

No response

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions