diff --git a/mypy/checker.py b/mypy/checker.py index 96b55f321a73..3bee7b633339 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1801,7 +1801,8 @@ def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None: "but must return a subtype of", ) elif not isinstance( - get_proper_type(bound_type.ret_type), (AnyType, Instance, TupleType, UninhabitedType) + get_proper_type(bound_type.ret_type), + (AnyType, Instance, TupleType, UninhabitedType, LiteralType), ): self.fail( message_registry.NON_INSTANCE_NEW_TYPE.format( diff --git a/mypy/checkmember.py b/mypy/checkmember.py index f19a76ec6a34..719b48b14e07 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -410,6 +410,8 @@ def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: Member ret_type = tuple_fallback(ret_type) if isinstance(ret_type, TypedDictType): ret_type = ret_type.fallback + if isinstance(ret_type, LiteralType): + ret_type = ret_type.fallback if isinstance(ret_type, Instance): if not mx.is_operator: # When Python sees an operator (eg `3 == 4`), it automatically translates that diff --git a/mypy/typeops.py b/mypy/typeops.py index 298ad4d16f8c..d058bb8201d3 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -319,7 +319,7 @@ def class_callable( default_ret_type = fill_typevars(info) explicit_type = init_ret_type if is_new else orig_self_type if ( - isinstance(explicit_type, (Instance, TupleType, UninhabitedType)) + isinstance(explicit_type, (Instance, TupleType, UninhabitedType, LiteralType)) # We have to skip protocols, because it can be a subtype of a return type # by accident. Like `Hashable` is a subtype of `object`. See #11799 and isinstance(default_ret_type, Instance) diff --git a/mypy/types.py b/mypy/types.py index 38c17e240ccf..426d560c2bf7 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2278,6 +2278,8 @@ def type_object(self) -> mypy.nodes.TypeInfo: ret = ret.partial_fallback if isinstance(ret, TypedDictType): ret = ret.fallback + if isinstance(ret, LiteralType): + ret = ret.fallback assert isinstance(ret, Instance) return ret.type diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 498a2c12b6e8..c0b1114db512 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -472,6 +472,27 @@ class B(A): def __new__(cls) -> B: pass +[case testOverride__new__WithLiteralReturnPassing] +from typing import Literal + +class Falsy: + def __bool__(self) -> Literal[False]: pass + +reveal_type(bool(Falsy())) # N: Revealed type is "Literal[False]" +reveal_type(int()) # N: Revealed type is "Literal[0]" + +[builtins fixtures/literal__new__.pyi] +[typing fixtures/typing-medium.pyi] + +[case testOverride__new__WithLiteralReturnFailing] +from typing import Literal + +class Foo: + def __new__(cls) -> Literal[1]: pass # E: Incompatible return type for "__new__" (returns "Literal[1]", but must return a subtype of "Foo") + +[builtins fixtures/__new__.pyi] +[typing fixtures/typing-medium.pyi] + [case testOverride__new__AndCallObject] from typing import TypeVar, Generic diff --git a/test-data/unit/fixtures/__new__.pyi b/test-data/unit/fixtures/__new__.pyi index 401de6fb9cd1..57d3624ce92c 100644 --- a/test-data/unit/fixtures/__new__.pyi +++ b/test-data/unit/fixtures/__new__.pyi @@ -12,6 +12,7 @@ class object: class type: def __init__(self, x) -> None: pass +class float: pass class int: pass class bool: pass class str: pass diff --git a/test-data/unit/fixtures/literal__new__.pyi b/test-data/unit/fixtures/literal__new__.pyi new file mode 100644 index 000000000000..971bc39bfff4 --- /dev/null +++ b/test-data/unit/fixtures/literal__new__.pyi @@ -0,0 +1,25 @@ +from typing import Literal, Protocol, overload + +class object: + def __init__(self) -> None: pass + +class type: + def __init__(self, x) -> None: pass + +class str: pass +class dict: pass +class float: pass +class int: + def __new__(cls) -> Literal[0]: pass + +class _Truthy(Protocol): + def __bool__(self) -> Literal[True]: pass + +class _Falsy(Protocol): + def __bool__(self) -> Literal[False]: pass + +class bool(int): + @overload + def __new__(cls, __o: _Truthy) -> Literal[True]: pass + @overload + def __new__(cls, __o: _Falsy) -> Literal[False]: pass