Skip to content

Commit 193456e

Browse files
committed
Auto merge of #12288 - PartiallyTyped:trait-type-checking-documentation, r=flip1995
Add documentation on trait checking and type construction Changes: Added documentation on how to create types via `Ty` and how to check specific trait implementations. changelog: none r? `@blyxyas`
2 parents 5214ab5 + 19c2aba commit 193456e

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

book/src/development/trait_checking.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,53 @@ impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
9494
}
9595
```
9696

97+
## Creating Types Programmatically
98+
99+
Traits are often generic over a type parameter, e.g. `Borrow<T>` is generic
100+
over `T`. Rust allows us to implement a trait for a specific type. For example,
101+
we can implement `Borrow<[u8]>` for a hypothetical type `Foo`. Let's suppose
102+
that we would like to find whether our type actually implements `Borrow<[u8]>`.
103+
104+
To do so, we can use the same `implements_trait` function as above, and supply
105+
a type parameter that represents `[u8]`. Since `[u8]` is a specialization of
106+
`[T]`, we can use the [`Ty::new_slice`][new_slice] method to create a type
107+
that represents `[T]` and supply `u8` as a type parameter.
108+
To create a `ty::Ty` programmatically, we rely on `Ty::new_*` methods. These
109+
methods create a `TyKind` and then wrap it in a `Ty` struct. This means we
110+
have access to all the primitive types, such as `Ty::new_char`,
111+
`Ty::new_bool`, `Ty::new_int`, etc. We can also create more complex types,
112+
such as slices, tuples, and references out of these basic building blocks.
113+
114+
For trait checking, it is not enough to create the types, we need to convert
115+
them into [GenericArg]. In rustc, a generic is an entity that the compiler
116+
understands and has three kinds, type, const and lifetime. By calling
117+
`.into()` on a constructed [Ty], we wrap the type into a generic which can
118+
then be used by the query system to decide whether the specialized trait
119+
is implemented.
120+
121+
The following code demonstrates how to do this:
122+
123+
```rust
124+
125+
use rustc_middle::ty::Ty;
126+
use clippy_utils::ty::implements_trait;
127+
use rustc_span::symbol::sym;
128+
129+
let ty = todo!("Get the `Foo` type to check for a trait implementation");
130+
let borrow_id = cx.tcx.get_diagnostic_item(sym::Borrow).unwrap(); // avoid unwrap in real code
131+
let slice_of_bytes_t = Ty::new_slice(cx.tcx, cx.tcx.types.u8);
132+
let generic_param = slice_of_bytes_t.into();
133+
if implements_trait(cx, ty, borrow_id, &[generic_param]) {
134+
todo!("Rest of lint implementation")
135+
}
136+
```
137+
138+
In essence, the [Ty] struct allows us to create types programmatically in a
139+
representation that can be used by the compiler and the query engine. We then
140+
use the `rustc_middle::Ty` of the type we are interested in, and query the
141+
compiler to see if it indeed implements the trait we are interested in.
142+
143+
97144
[DefId]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html
98145
[diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
99146
[lang_items]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/lang_items/struct.LanguageItems.html
@@ -102,4 +149,7 @@ impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
102149
[symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html
103150
[symbol_index]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/sym/index.html
104151
[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
152+
[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
105153
[rust]: https://github.com/rust-lang/rust
154+
[new_slice]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_slice
155+
[GenericArg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html

book/src/development/type_checking.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,22 @@ the [`TypeckResults::node_type()`][node_type] method inside of bodies.
123123

124124
> **Warning**: Don't use `hir_ty_to_ty` inside of bodies, because this can cause ICEs.
125125
126+
## Creating Types programmatically
127+
128+
A common usecase for creating types programmatically is when we want to check if a type implements a trait (see
129+
[Trait Checking](trait_checking.md)).
130+
131+
Here's an example of how to create a `Ty` for a slice of `u8`, i.e. `[u8]`
132+
133+
```rust
134+
use rustc_middle::ty::Ty;
135+
// assume we have access to a LateContext
136+
let ty = Ty::new_slice(cx.tcx, Ty::new_u8());
137+
```
138+
139+
In general, we rely on `Ty::new_*` methods. These methods define the basic building-blocks that the
140+
type-system and trait-system use to define and understand the written code.
141+
126142
## Useful Links
127143

128144
Below are some useful links to further explore the concepts covered

0 commit comments

Comments
 (0)