From 9dadf2724c22f58a05fc3801bfc60cac6e07289a Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 29 Dec 2025 19:43:17 +0000 Subject: [PATCH] [ty] Add documentation for `ty_extensions.Top` and `ty_extensions.Bottom` (#22245) Co-authored-by: Carl Meyer --- crates/ty_ide/src/inlay_hints.rs | 80 +++++++++++++++++++ .../ty_extensions/ty_extensions.pyi | 63 ++++++++++++++- 2 files changed, 139 insertions(+), 4 deletions(-) diff --git a/crates/ty_ide/src/inlay_hints.rs b/crates/ty_ide/src/inlay_hints.rs index 802f48965f..59d700b191 100644 --- a/crates/ty_ide/src/inlay_hints.rs +++ b/crates/ty_ide/src/inlay_hints.rs @@ -6924,6 +6924,86 @@ mod tests { "); } + #[test] + fn hover_narrowed_type_with_top_materialization() { + let mut test = inlay_hint_test( + r#" + def f(xyxy: object): + if isinstance(xyxy, list): + x = xyxy + "#, + ); + + assert_snapshot!(test.inlay_hints(), @r#" + def f(xyxy: object): + if isinstance(xyxy, list): + x[: Top[list[Unknown]]] = xyxy + + --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/ty_extensions.pyi:24:1 + | + 22 | CallableTypeOf: _SpecialForm + 23 | + 24 | Top: _SpecialForm + | ^^^ + 25 | """ + 26 | `Top[T]` represents the "top materialization" of `T`. + | + info: Source + --> main2.py:4:13 + | + 2 | def f(xyxy: object): + 3 | if isinstance(xyxy, list): + 4 | x[: Top[list[Unknown]]] = xyxy + | ^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/builtins.pyi:2829:7 + | + 2828 | @disjoint_base + 2829 | class list(MutableSequence[_T]): + | ^^^^ + 2830 | """Built-in mutable sequence. + | + info: Source + --> main2.py:4:17 + | + 2 | def f(xyxy: object): + 3 | if isinstance(xyxy, list): + 4 | x[: Top[list[Unknown]]] = xyxy + | ^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/ty_extensions.pyi:14:1 + | + 13 | # Types + 14 | Unknown = object() + | ^^^^^^^ + 15 | AlwaysTruthy = object() + 16 | AlwaysFalsy = object() + | + info: Source + --> main2.py:4:22 + | + 2 | def f(xyxy: object): + 3 | if isinstance(xyxy, list): + 4 | x[: Top[list[Unknown]]] = xyxy + | ^^^^^^^ + | + + --------------------------------------------- + info[inlay-hint-edit]: File after edits + info: Source + + def f(xyxy: object): + if isinstance(xyxy, list): + x: Top[list[Unknown]] = xyxy + "#); + } + struct InlayHintLocationDiagnostic { source: FileRange, target: FileRange, diff --git a/crates/ty_vendored/ty_extensions/ty_extensions.pyi b/crates/ty_vendored/ty_extensions/ty_extensions.pyi index ba3b1a1c45..eb4756a091 100644 --- a/crates/ty_vendored/ty_extensions/ty_extensions.pyi +++ b/crates/ty_vendored/ty_extensions/ty_extensions.pyi @@ -20,12 +20,67 @@ Not: _SpecialForm Intersection: _SpecialForm TypeOf: _SpecialForm CallableTypeOf: _SpecialForm -# Top[T] evaluates to the top materialization of T, a type that is a supertype -# of every materialization of T. + Top: _SpecialForm -# Bottom[T] evaluates to the bottom materialization of T, a type that is a subtype -# of every materialization of T. +""" +`Top[T]` represents the "top materialization" of `T`. + +For any type `T`, the top [materialization] of `T` is a type that is +a supertype of all materializations of `T`. + +For a [fully static] type `T`, `Top[T]` is always exactly the same type +as `T` itself. For example, the top materialization of `Sequence[int]` +is simply `Sequence[int]`. + +For a [gradual type] `T` that contains [`Any`][Any] or `Unknown` inside +it, however, `Top[T]` will not be equivalent to `T`. `Top[Sequence[Any]]` +evaluates to `Sequence[object]`: since `Sequence` is covariant, no +possible materialization of `Any` exists such that a fully static +materialization of `Sequence[Any]` would not be a subtype of +`Sequence[object]`. + +`Top[T]` cannot be simplified further for invariant gradual types. +`Top[list[Any]]` cannot be simplified to any other type: because `list` +is invariant, `list[object]` is not a supertype of `list[int]`. The +top materialization of `list[Any]` is simply `Top[list[Any]]`: the +infinite union of `list[T]` for every possible fully static type `T`. + +[materialization]: https://typing.python.org/en/latest/spec/concepts.html#materialization +[fully static]: https://typing.python.org/en/latest/spec/concepts.html#fully-static-types +[gradual type]: https://typing.python.org/en/latest/spec/concepts.html#gradual-types +[Any]: https://typing.python.org/en/latest/spec/special-types.html#any +""" + Bottom: _SpecialForm +""" +`Bottom[T]` represents the "bottom materialization" of `T`. + +For any type `T`, the bottom [materialization] of `T` is a type that is +a subtype of all materializations of `T`. + +For a [fully static] type `T`, `Bottom[T]` is always exactly the same type +as `T` itself. For example, the bottom materialization of `Sequence[int]` +is simply `Sequence[int]`. + +For a [gradual type] `T` that contains [`Any`][Any] or `Unknown` inside it, +however, `Bottom[T]` will not be equivalent to `T`. `Bottom[Sequence[Any]]` +evaluates to `Sequence[Never]`: since `Sequence` is covariant, no +possible materialization of `Any` exists such that a fully static +materialization of `Sequence[Any]` would not be a supertype of +`Sequence[Never]`. (`Sequence[Never]` is not the same type as the +uninhabited type `Never`: for example, it is inhabited by the empty tuple, +`()`.) + +For many invariant gradual types `T`, `Bottom[T]` is equivalent to +[`Never`][Never], although ty will not necessarily apply this simplification +eagerly. + +[materialization]: https://typing.python.org/en/latest/spec/concepts.html#materialization +[fully static]: https://typing.python.org/en/latest/spec/concepts.html#fully-static-types +[gradual type]: https://typing.python.org/en/latest/spec/concepts.html#gradual-types +[Never]: https://typing.python.org/en/latest/spec/special-types.html#never +[Any]: https://typing.python.org/en/latest/spec/special-types.html#any +""" # ty treats annotations of `float` to mean `float | int`, and annotations of `complex` # to mean `complex | float | int`. This is to support a typing-system special case [1].