False positive about self argument with union of namedtuple and Any
The following code produces a false positive:
from collections import namedtuple
from typing import Any
T = namedtuple("T", ["x"])
class C(T):
def f(self) -> bool:
return True
c: C | Any
c.f() # Error: Invalid self argument "C" to attribute function "f" with type "Callable[[C], bool]"
c2: C
c2.f() # Ok
It looks like the self argument check fails when we have a union with a namedtuple type. It's okay if the base class is a regular class, or if there is no union.
Note that the bug is specific to a union with Any, union with another type (that has the attribute) doesn't cause the problem. This is happening because self argument check does something like is_subtype(meet_types(base_type, instance_type), erase_type_vars(declared_self_type)), and meet_types() is broken for Any. Namely, in this case meet_types(Union[tuple[Any, fallback=C], Any], C) returns C, because meet_types(Any, C) returns C. And then is_subtype(C, tuple[Any, fallback=C]) obviously returns False, causing the error.
But the problem is actually much wider, meet_types(C, Any) returning C is both wrong and inconsistent (with joins returning Any). Here is a more abstract example: imagine arbitrary pair of types C <: B, then if meet(X, Any) = X, and meet(X | Y, Z) = meet(X, Z) | meet(Y, Z), we get a contradiction meet(C | Any, B) = meet(C, B) | meet(Any, B) = C | B = B, while by definition meet must be a subtype of both arguments. Btw @JukkaL proposed to switch meet(X, Any) from X to Any a long time ago in https://github.com/python/mypy/issues/3194#issuecomment-432690811. This may be a good argument to actually do this.
Unfortunately, switching meet(X, Any) to return Any causes 22 test failures, most of them look like something that just needs to be updated, but also there are few tests that fail because if isinstance(x, list): ... behaves differently (since in such cases both meet and Any are present). So this is not a super-easy fix as I hoped.