Skip to content

Commit 214eb71

Browse files
committed
spec: expand Never/NoReturn section with precise subtyping rules (#1458)
The previous spec described Never in four lines without defining any of its subtyping behaviour. Type checkers converge on a consistent set of rules, but none were written down. This commit codifies them. Spec changes (docs/spec/special-types.rst): - Never is a subtype of every fully static type (bottom type property) - No type other than Never (and Any) is a subtype of Never - Never | T collapses to T for any type T - Never as a type argument: covariant containers accept it, invariant containers do not - type[Never] represents an uninhabitable class object - Clarify that NoReturn may appear in non-return positions Conformance test changes (conformance/tests/specialtypes_never.py): - Add func11: Never | int collapses to int - Add func12: object is not assignable to Never (E) - Add func13: raise in Never-returning function is OK - Add func14: type[Never] is a valid annotation - Add func15: list[Never] return is valid - Add func16: list[Never] is not assignable to list[int] (E) Result TOMLs updated for mypy, pyright, pyrefly, zuban (Pass). ty marked Partial: does not flag func12 and func16 errors. Closes #1458
1 parent 7c607df commit 214eb71

7 files changed

Lines changed: 129 additions & 3 deletions

File tree

conformance/results/mypy/specialtypes_never.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ specialtypes_never.py:85: error: Incompatible types in assignment (expression ha
55
specialtypes_never.py:85: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
66
specialtypes_never.py:85: note: Consider using "Sequence" instead, which is covariant
77
specialtypes_never.py:104: error: Incompatible return value type (got "ClassC[Never]", expected "ClassC[U]") [return-value]
8+
specialtypes_never.py:121: error: Return statement in function which does not return [misc]
9+
specialtypes_never.py:145: error: Incompatible return value type (got "list[Never]", expected "list[int]") [return-value]
10+
specialtypes_never.py:145: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
11+
specialtypes_never.py:145: note: Consider using "Sequence" instead, which is covariant
812
"""
913
conformance_automated = "Pass"
1014
errors_diff = """

conformance/results/pyrefly/specialtypes_never.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ output = """
66
ERROR specialtypes_never.py:19:22-30: Function declared to return `NoReturn` but is missing an explicit `return` [bad-return]
77
ERROR specialtypes_never.py:85:21-22: `list[Never]` is not assignable to `list[int]` [bad-assignment]
88
ERROR specialtypes_never.py:104:12-27: Returned type `ClassC[Never]` is not assignable to declared return type `ClassC[U]` [bad-return]
9+
ERROR specialtypes_never.py:121:12-13: Returned type `object` is not assignable to declared return type `Never` [bad-return]
10+
ERROR specialtypes_never.py:145:12-13: Returned type `list[Never]` is not assignable to declared return type `list[int]` [bad-return]
911
"""

conformance/results/pyright/specialtypes_never.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ specialtypes_never.py:85:21 - error: Type "list[Never]" is not assignable to dec
88
specialtypes_never.py:104:12 - error: Type "ClassC[Never]" is not assignable to return type "ClassC[U@func10]"
99
  "ClassC[Never]" is not assignable to "ClassC[U@func10]"
1010
    Type parameter "T@ClassC" is invariant, but "Never" is not the same as "U@func10" (reportReturnType)
11+
specialtypes_never.py:121:5 - error: Function with declared return type "NoReturn" cannot include a return statement (reportGeneralTypeIssues)
12+
specialtypes_never.py:145:12 - error: Type "list[Never]" is not assignable to return type "list[int]"
13+
  "list[Never]" is not assignable to "list[int]"
14+
    Type parameter "_T@list" is invariant, but "Never" is not the same as "int"
15+
    Consider switching from "list" to "Sequence" which is covariant (reportReturnType)
1116
"""
1217
conformance_automated = "Pass"
1318
errors_diff = """

conformance/results/ty/specialtypes_never.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
conformance_automated = "Pass"
1+
conformant = "Partial"
2+
notes = """
3+
Does not flag `return <object>` in a function declared `-> Never` (line 121).
4+
Does not flag `return list[Never]` where `list[int]` is expected (line 145).
5+
"""
6+
conformance_automated = "Partial"
27
errors_diff = """
8+
Line 121: Expected 1 errors
9+
Line 145: Expected 1 errors
310
"""
411
output = """
512
specialtypes_never.py:19:22: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Never`

conformance/results/zuban/specialtypes_never.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@ specialtypes_never.py:85: error: Incompatible types in assignment (expression ha
77
specialtypes_never.py:85: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
88
specialtypes_never.py:85: note: Consider using "Sequence" instead, which is covariant
99
specialtypes_never.py:104: error: Incompatible return value type (got "ClassC[Never]", expected "ClassC[U]") [return-value]
10+
specialtypes_never.py:121: error: Return statement in function which does not return [misc]
11+
specialtypes_never.py:145: error: Incompatible return value type (got "list[Never]", expected "list[int]") [return-value]
12+
specialtypes_never.py:145: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
13+
specialtypes_never.py:145: note: Consider using "Sequence" instead, which is covariant
1014
"""

conformance/tests/specialtypes_never.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,44 @@ class ClassC(Generic[T]):
102102
def func10(x: U) -> ClassC[U]:
103103
# Never is not compatible in an invariant context.
104104
return ClassC[Never]() # E
105+
106+
107+
# Never | T is equivalent to T — the union collapses.
108+
109+
type Alias1 = Never | int # Equivalent to int
110+
type Alias2 = int | Never # Equivalent to int
111+
112+
113+
def func11(x: Alias1) -> int:
114+
return x # OK — Alias1 is int
115+
116+
117+
# No type other than Never (and Any) is assignable to Never.
118+
119+
120+
def func12(x: object) -> Never:
121+
return x # E
122+
123+
124+
def func13() -> Never:
125+
raise RuntimeError("never") # OK
126+
127+
128+
# type[Never] is valid and represents an uninhabitable class object.
129+
130+
131+
def func14(cls: type[Never]) -> None:
132+
pass
133+
134+
135+
# Empty containers typed as list[Never] are assignable to list[T]
136+
# only when T is used covariantly; list itself is invariant, so
137+
# list[Never] is not assignable to list[int].
138+
139+
140+
def func15() -> list[Never]:
141+
return [] # OK
142+
143+
144+
def func16(x: list[Never]) -> list[int]:
145+
return x # E — list is invariant

docs/spec/special-types.rst

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,77 @@ is unreachable and will behave accordingly::
9595
---------
9696

9797
Since Python 3.11, the ``typing`` module contains a :term:`special form`
98-
``Never``. It represents the bottom type, a type that represents the empty set
99-
of Python objects.
98+
``Never``. It represents the **bottom type**: the type that denotes the empty
99+
set of Python objects. No Python object can be a runtime instance of
100+
``Never``.
100101

101102
The ``Never`` type is equivalent to ``NoReturn``, which is discussed above.
102103
The ``NoReturn`` type is conventionally used in return annotations of
103104
functions, and ``Never`` is typically used in other locations, but the two
104105
types are completely interchangeable.
105106

107+
Subtyping rules for ``Never``
108+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
109+
110+
Because ``Never`` is the bottom type, it is a :term:`subtype` of every fully
111+
static type. This means that a value of type ``Never`` is :term:`assignable`
112+
to a variable of any type ``T``::
113+
114+
from typing import Never
115+
116+
def f(x: Never) -> None:
117+
v1: int = x # OK — Never is a subtype of int
118+
v2: str = x # OK — Never is a subtype of str
119+
120+
No type other than ``Never`` itself (and :ref:`Any <any>`) is a subtype of
121+
``Never``. In particular, ``object`` is *not* a subtype of ``Never``::
122+
123+
def g(x: object) -> Never:
124+
return x # Error — object is not assignable to Never
125+
126+
Because ``Never`` is a subtype of every type ``T``, the union ``Never | T`` is
127+
equivalent to ``T``::
128+
129+
from typing import Never, Union
130+
131+
type Alias = Never | int # Equivalent to int
132+
133+
Code following a call to a function that returns ``Never`` is unreachable.
134+
Type checkers may suppress errors in unreachable blocks.
135+
136+
Using ``Never`` as a type argument
137+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
138+
139+
``Never`` may appear as a type argument. When used with a :term:`covariant`
140+
type parameter, ``Container[Never]`` is a subtype of ``Container[T]`` for
141+
every type ``T``, because ``Never`` is a subtype of every type. This is
142+
useful to type an empty container whose element type is not yet known::
143+
144+
from typing import Never
145+
146+
def empty_list() -> list[Never]:
147+
return []
148+
149+
When used with an :term:`invariant` type parameter, the normal invariance rules
150+
apply: ``Container[Never]`` is only assignable to ``Container[Never]``::
151+
152+
from typing import Generic, Never, TypeVar
153+
154+
T = TypeVar("T")
155+
156+
class Box(Generic[T]):
157+
pass
158+
159+
def f() -> Box[int]:
160+
return Box[Never]() # Error — Box is invariant in T
161+
162+
``type[Never]``
163+
^^^^^^^^^^^^^^^^^
164+
165+
``type[Never]`` represents the class object for a type that has no instances.
166+
In practice this type is rarely useful directly, but it is the correct result
167+
of narrowing a ``type[T]`` variable to an impossible branch.
168+
106169
.. _`numeric-promotions`:
107170

108171
Special cases for ``float`` and ``complex``

0 commit comments

Comments
 (0)