spec: expand Never/NoReturn with precise subtyping rules#2309
Open
ashishpatel26 wants to merge 5 commits into
Open
spec: expand Never/NoReturn with precise subtyping rules#2309ashishpatel26 wants to merge 5 commits into
ashishpatel26 wants to merge 5 commits into
Conversation
…hon#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 python#1458
JelleZijlstra
requested changes
Jun 22, 2026
JelleZijlstra
left a comment
Member
There was a problem hiding this comment.
I don't think this verbiage is necessary. This isn't the kind of issue that can be fixed by adding some text without much understanding.
grievejia
reviewed
Jun 22, 2026
- Use int instead of object as the non-Never witness (object is already expected to not be a subtype of most things; int is more illuminating) - Rephrase to avoid the too-strong 'No type other than Never' claim (uninhabitable types like tuple[Never, int] are also subtypes of Never) - Qualify all 'every type T' as 'every fully static type T' throughout (the subtype relation is undefined for gradual types like Any) - Replace list[Never] with Sequence[Never] in the covariant example (list is invariant, so list[Never] is not assignable to list[int]; Sequence is covariant and correctly demonstrates the bottom-type behaviour)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves #1458.
The
Neversection in the spec was four lines with no subtyping rules. Type checkers (mypy, pyright, pyrefly, zuban) all converge on a consistent set of behaviours, but none were written down, making the spec unexecutable for implementers and leaving the community guessing.This PR codifies what the implementations already agree on:
Neveris a subtype of every fully static type (bottom type property)Never(andAny) is a subtype ofNeverNever | Tis equivalent toTfor anyTContainer[Never]is assignable toContainer[T]for anyT; in invariant positions, the normal invariance rules applytype[Never]is a valid annotation representing an uninhabitable classNoReturnmay appear in non-return-type positions (the old restriction was removed)Changes
docs/spec/special-types.rstNeverfrom 4 lines to a full subtyping specificationconformance/tests/specialtypes_never.pytype[Never], and invarianceconformance/results/*/specialtypes_never.tomlConformance results snapshot
object → Neverandlist[Never] → list[int]errorsOut of scope
The LSP/mutable-attribute debate from #1458 (whether
class B(A): foo: Nevershould be an error) is left for a separate issue. That requires formalising mutable attribute override rules — a larger change that should not block landing the uncontroversial subtyping rules documented here.