From 2df844bd80ad6696fa5d28c3c0738faf523e6863 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Wed, 22 Jul 2026 15:30:59 +0100 Subject: [PATCH 1/2] Add documentation for some `rustc_type_ir` macros --- .../sharing-crates-with-rust-analyzer.md | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/src/solve/sharing-crates-with-rust-analyzer.md b/src/solve/sharing-crates-with-rust-analyzer.md index bf0d4fe3a1..0780c72366 100644 --- a/src/solve/sharing-crates-with-rust-analyzer.md +++ b/src/solve/sharing-crates-with-rust-analyzer.md @@ -143,6 +143,115 @@ solver. This infrastructure is used by the external fuzzing project: . + +## `rustc_type_ir` Macros +`rustc_type_ir` makes _heavy_ use of a few macros, in particular +- [`Lift_Generic`][lift_generic] +- [`TypeFoldable_Generic`][typefoldable_generic] +- [`TypeVisitable_Generic`][typevisitable_generic] +- [`GenericTypeVisitable`][generictypevisitable] + +Of which primarily exist to reduce the amount of boilerplate otherwise +required to implement `TypeFoldable`, `TypeVisitable` and `Lift`. + +### `Lift_Generic` +[lift_generic]: #lift_generic + +Derives `Lift` for a struct or enum, with three curious properties: + +**quirk 1:** +The generic parameters `I` and `J` are reserved for `I: Interner` and `J` +being the interner it is being lifted to. + +**quirk 2:** +`PhantomData` is handled automatically, creating a new `PhantomData` but +_has_ to be included in the file through; `use std::marker::PhantomData;` +you cannot use `std::marker::PhantomData` directly on the field of a struct. + +**quirk 3:** +The bounds are deliberately written as associated type bounds on the `Interner` +trait rather than as `where` clauses on `LiftInto`. Given only `I: LiftInto`, +Rust can then treat bounds such as the following as implied: + +```rust +I::Ty: Lift +I::Const: Lift +``` + +This allows `Lift_Generic` to emit the bound `I: LiftInto` while still +calling `lift_to_interner` on fields of type `I::Ty`, `I::Const`, and the other +declared associated types. It also guarantees that each call produces the +destination field type expected after the derive rewrites `I::Assoc` to +`J::Assoc`. + +Without `declare_lift_into!`, the derive would need to generate a separate bound +for every interner-associated type used by every field. If a new `Interner` +associated type is expected to work with `Lift_Generic`, it needs an appropriate +`Lift` implementation and normally needs to be included in the +`declare_lift_into!` invocation. + +If you want to ignore a file, such as a primitive like a `u32` which can't be +lifted you can skip the field with `#[lift(ignore)]`. + +### `TypeFoldable_Generic` +[typefoldable_generic]: #typefoldable_generic + +`TypeFoldable_Generic` derives `rustc_type_ir::TypeFoldable` for a struct or +enum. + +It consumes a value and reconstructs the same struct or enum variant after +folding its fields. It generates both fallible and infallible folding methods. + +Use `#[type_foldable(identity)]` for a field whose value must be preserved +unchanged. The macro moves that field directly into the reconstructed value +instead of passing it to the folder. Its type therefore does not need to +implement `TypeFoldable`. + +For an enum, the generated match contains one reconstruction arm per variant. + +### `TypeVisitable_Generic` +[typevisitable_generic]: #typevisitable_generic + +`TypeVisitable_Generic` derives `rustc_type_ir::TypeVisitable` for a struct +or enum. It visits the value's fields in declaration order, delegating each +field to that field's own `TypeVisitable` implementation. The traversal can +stop early if the visitor returns a residual result. + +Use `#[type_visitable(ignore)]` to ignore a field; it will not be part of the +traversal and will not need to implement `TypeVisitable`. This should only +be used when the field does not need to be traversed. + +### `GenericTypeVisitable` +[generictypevisitable]: #generictypevisitable + +Derives `rustc_type_ir::GenericTypeVisitable` for a struct or enum. + +This is the non-nightly, rust-analyzer-facing generic traversal. The visitor +type is a parameter of the trait rather than a parameter of the method, and +visiting neither returns a result nor supports short-circuiting. + +There is intentionally no ignore attribute. The traversal must visit every +field. This is a soundness requirement for rust-analyzer's use of the traversal +when tracing and garbage-collecting interned types. + +When the macro crate's `nightly` feature is enabled, the derive macro remains +registered but emits no tokens. The `GenericTypeVisitable` trait and its +traversal module are also excluded from the nightly configuration of +`rustc_type_ir`; they exist only in its non-nightly configuration. + +### `GenericTypeVisitable` & `TypeVisitable_Generic` + +While ostensibly similar due to their names, they implement two different +visiting systems. + +- `TypeVisitable_Generic` means: derive the ordinary `TypeVisitable` trait + generically over an `Interner`. +- `GenericTypeVisitable` means: derive the separate `GenericTypeVisitable` trait + used by non-nightly consumers such as rust-analyzer. + +As such a struct or enum can derive both `TypeVisitable_Generic` and +`GenericTypeVisitable` + ## Long-term plans for supporting rust-analyzer In general, we aim to support rust-analyzer just as well as rustc in these shared crates—provided From f56faf6b2ed48bdf5cc09a1e24ee0c6f69623fac Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Wed, 29 Jul 2026 08:41:42 +0100 Subject: [PATCH 2/2] PR Feedback - Improve intro to the macros, compare the Vistiable triats earlier in the doc & reword GenericTypeVisitable intro --- .../sharing-crates-with-rust-analyzer.md | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/solve/sharing-crates-with-rust-analyzer.md b/src/solve/sharing-crates-with-rust-analyzer.md index 0780c72366..8a96937b76 100644 --- a/src/solve/sharing-crates-with-rust-analyzer.md +++ b/src/solve/sharing-crates-with-rust-analyzer.md @@ -145,6 +145,7 @@ This infrastructure is used by the external fuzzing project: ## `rustc_type_ir` Macros + `rustc_type_ir` makes _heavy_ use of a few macros, in particular - [`Lift_Generic`][lift_generic] - [`TypeFoldable_Generic`][typefoldable_generic] @@ -154,6 +155,10 @@ This infrastructure is used by the external fuzzing project: Of which primarily exist to reduce the amount of boilerplate otherwise required to implement `TypeFoldable`, `TypeVisitable` and `Lift`. +These macros are variants of the `TypeFoldable`, `TypeVisitable`, and `Lift` +macros used in crates which have access to the `TyCtxt` using a `I: Interner` +parameter instead of `TyCtxt`. + ### `Lift_Generic` [lift_generic]: #lift_generic @@ -224,23 +229,6 @@ be used when the field does not need to be traversed. ### `GenericTypeVisitable` [generictypevisitable]: #generictypevisitable -Derives `rustc_type_ir::GenericTypeVisitable` for a struct or enum. - -This is the non-nightly, rust-analyzer-facing generic traversal. The visitor -type is a parameter of the trait rather than a parameter of the method, and -visiting neither returns a result nor supports short-circuiting. - -There is intentionally no ignore attribute. The traversal must visit every -field. This is a soundness requirement for rust-analyzer's use of the traversal -when tracing and garbage-collecting interned types. - -When the macro crate's `nightly` feature is enabled, the derive macro remains -registered but emits no tokens. The `GenericTypeVisitable` trait and its -traversal module are also excluded from the nightly configuration of -`rustc_type_ir`; they exist only in its non-nightly configuration. - -### `GenericTypeVisitable` & `TypeVisitable_Generic` - While ostensibly similar due to their names, they implement two different visiting systems. @@ -252,6 +240,22 @@ visiting systems. As such a struct or enum can derive both `TypeVisitable_Generic` and `GenericTypeVisitable` +`GenericTypeVisitable` derives `rustc_type_ir::GenericTypeVisitable` for a +struct or enum. + +This a separate more general traversal trait purely used by rust-analyzer. +The visitor type is a parameter of the trait rather than a parameter of the +method, and visiting neither returns a result nor supports short-circuiting. + +There is intentionally no ignore attribute. The traversal must visit every +field. This is a soundness requirement for rust-analyzer's use of the traversal +when tracing and garbage-collecting interned types. + +When the macro crate's `nightly` feature is enabled, the derive macro remains +registered but emits no tokens. The `GenericTypeVisitable` trait and its +traversal module are also excluded from the nightly configuration of +`rustc_type_ir`; they exist only in its non-nightly configuration. + ## Long-term plans for supporting rust-analyzer In general, we aim to support rust-analyzer just as well as rustc in these shared crates—provided