diff --git a/mypy/join.py b/mypy/join.py index 671924a75da1..4cd2c6b2534b 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -559,10 +559,10 @@ def join_similar_callables(t: CallableType, s: CallableType) -> CallableType: arg_types: list[Type] = [] for i in range(len(t.arg_types)): arg_types.append(meet_types(t.arg_types[i], s.arg_types[i])) - # TODO in combine_similar_callables also applies here (names and kinds) - # The fallback type can be either 'function' or 'type'. The result should have 'type' as - # fallback only if both operands have it as 'type'. - if t.fallback.type.fullname != "builtins.type": + # TODO in combine_similar_callables also applies here (names and kinds; user metaclasses) + # The fallback type can be either 'function', 'type', or some user-provided metaclass. + # The result should always use 'function' as a fallback if either operands are using it. + if t.fallback.type.fullname == "builtins.function": fallback = t.fallback else: fallback = s.fallback @@ -580,9 +580,10 @@ def combine_similar_callables(t: CallableType, s: CallableType) -> CallableType: for i in range(len(t.arg_types)): arg_types.append(join_types(t.arg_types[i], s.arg_types[i])) # TODO kinds and argument names - # The fallback type can be either 'function' or 'type'. The result should have 'type' as - # fallback only if both operands have it as 'type'. - if t.fallback.type.fullname != "builtins.type": + # TODO what should happen if one fallback is 'type' and the other is a user-provided metaclass? + # The fallback type can be either 'function', 'type', or some user-provided metaclass. + # The result should always use 'function' as a fallback if either operands are using it. + if t.fallback.type.fullname == "builtins.function": fallback = t.fallback else: fallback = s.fallback diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 30a900d63f2a..983a9b7e914f 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -865,6 +865,23 @@ class C(B): pass class D(C): pass class D2(C): pass +[case testConstructorJoinsWithCustomMetaclass] +# flags: --strict-optional +from typing import TypeVar +import abc + +def func() -> None: pass +class NormalClass: pass +class WithMetaclass(metaclass=abc.ABCMeta): pass + +T = TypeVar('T') +def join(x: T, y: T) -> T: pass + +f1 = join(func, WithMetaclass) +reveal_type(f1()) # N: Revealed type is "Union[__main__.WithMetaclass, None]" + +f2 = join(WithMetaclass, func) +reveal_type(f2()) # N: Revealed type is "Union[__main__.WithMetaclass, None]" -- Attribute access in class body -- ------------------------------