diff --git a/src/solve/sharing-crates-with-rust-analyzer.md b/src/solve/sharing-crates-with-rust-analyzer.md index bf0d4fe3a1..9ec4005079 100644 --- a/src/solve/sharing-crates-with-rust-analyzer.md +++ b/src/solve/sharing-crates-with-rust-analyzer.md @@ -143,6 +143,435 @@ 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 +- [`TypeVisitable_Generic`][typevisitable_generic] +- [`TypeFoldable_Generic`][typefoldable_generic] +- [`Lift_Generic`][lift_generic] +- [`GenericTypeVisitable`][generictypevisitable] + +of which are detailed below. + + +### `TypeVisitable_Generic` +[typevisitable_generic]: #typevisitable_generic + +`TypeVisitable_Generic` derives `rustc_type_ir::TypeVisitable` for a struct or enum. +Presently it cannot be derived for a union. + +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. + +The macro: +1. Uses an interner type parameter named `I`. +2. Adds `I` to the generated `impl` if the item does not already declare it. +3. Requires `I: Interner`. +4. Adds `TypeVisitable` bounds for the visited field types. +5. Pattern-matches the struct or enum and visits each field. +6. Propagates a visitor residual immediately, stopping the traversal. +7. Returns `VisitorResult::output()` after every field has been visited successfully. + +### How to use it + +```rust +use rustc_type_ir::Interner; +use rustc_type_ir_macros::TypeVisitable_Generic; + +#[derive(TypeVisitable_Generic)] +struct Example { + ty: I::Ty, + value: T, + #[type_visitable(ignore)] + cached_hash: u64, +} +``` + +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. + +### What its expansion does + +The expansion is approximately equivalent to: + +```rust +impl ::rustc_type_ir::TypeVisitable for Example +where + I: Interner, + I::Ty: ::rustc_type_ir::TypeVisitable, + T: ::rustc_type_ir::TypeVisitable, +{ + fn visit_with<__V: ::rustc_type_ir::TypeVisitor>( + &self, + __visitor: &mut __V, + ) -> __V::Result { + match *self { + Example { + ref ty, + ref value, + cached_hash: _, + } => { + match ::rustc_type_ir::VisitorResult::branch( + ::rustc_type_ir::TypeVisitable::visit_with(ty, __visitor), + ) { + ::core::ops::ControlFlow::Continue(()) => {} + ::core::ops::ControlFlow::Break(result) => { + return ::rustc_type_ir::VisitorResult::from_residual(result); + } + } + + match ::rustc_type_ir::VisitorResult::branch( + ::rustc_type_ir::TypeVisitable::visit_with(value, __visitor), + ) { + ::core::ops::ControlFlow::Continue(()) => {} + ::core::ops::ControlFlow::Break(result) => { + return ::rustc_type_ir::VisitorResult::from_residual(result); + } + } + } + } + + <__V::Result as ::rustc_type_ir::VisitorResult>::output() + } +} +``` + +The exact pattern bindings are generated by `synstructure`, fields are visited +sequentially and the first residual result is returned immediately. + +## `TypeFoldable_Generic` +[typefoldable_generic]: #typefoldable_generic + +`TypeFoldable_Generic` derives `rustc_type_ir::TypeFoldable` for a struct or enum. +Presently it cannot be derived for a union. + +It consumes a value and reconstructs the same struct or enum variant after folding its fields. It +generates both fallible and infallible folding methods. + +The macro: + +1. Uses an interner type parameter named `I`. +2. Adds `I` to the generated `impl` if necessary. +3. Requires `I: Interner`. +4. Finds ordinary generic type parameters used by folded fields. +5. Adds the required `TypeFoldable` bounds. +6. Generates `try_fold_with`, which uses `?` to propagate folder errors. +7. Generates `fold_with`, which performs the equivalent infallible fold. +8. Reconstructs the original struct or the same enum variant from the resulting fields. + +Folding does not change the interner parameter or the type of the containing +value. Both methods return `Self`. + +### How to use it + +```rust +use rustc_type_ir::Interner; +use rustc_type_ir_macros::TypeFoldable_Generic; + +#[derive(TypeFoldable_Generic)] +struct Example { + ty: I::Ty, + value: T, + #[type_foldable(identity)] + cached_hash: u64, +} +``` + +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`. + +### What its expansion does + +The expansion is approximately equivalent to: + +```rust +impl ::rustc_type_ir::TypeFoldable for Example +where + I: Interner, + I::Ty: ::rustc_type_ir::TypeFoldable, + T: ::rustc_type_ir::TypeFoldable, +{ + fn try_fold_with<__F: ::rustc_type_ir::FallibleTypeFolder>( + self, + __folder: &mut __F, + ) -> Result { + Ok(match self { + Example { + ty, + value, + cached_hash, + } => Example { + ty: ::rustc_type_ir::TypeFoldable::try_fold_with(ty, __folder)?, + value: ::rustc_type_ir::TypeFoldable::try_fold_with(value, __folder)?, + cached_hash, + }, + }) + } + + fn fold_with<__F: ::rustc_type_ir::TypeFolder>( + self, + __folder: &mut __F, + ) -> Self { + match self { + Example { + ty, + value, + cached_hash, + } => Example { + ty: ::rustc_type_ir::TypeFoldable::fold_with(ty, __folder), + value: ::rustc_type_ir::TypeFoldable::fold_with(value, __folder), + cached_hash, + }, + } + } +} +``` + +For an enum, the generated match contains one reconstruction arm per variant. + +## `Lift_Generic` +[lift_generic]: #lift_generic + +`Lift_Generic` derives `rustc_type_ir::lift::Lift` for a type parameterized by an interner `I`. +As before, it cannot be derived for a union. + +It converts a value associated with interner `I` into the corresponding value associated with a +destination interner `J`. In addition to transforming the value's fields, it computes a different +associated output type named `Lifted`. + +The macro adds a destination interner parameter `J` and generates an implementation with these +core requirements: + +```rust +J: Interner, +I: ::rustc_type_ir::LiftInto, +``` + +`I: LiftInto` supplies the relationships needed to lift interner-associated types, such as an +`I::Ty` into the corresponding `J::Ty`. + +`LiftInto` is generated in `rustc_type_ir::interner` by `declare_lift_into!`. The declaration lists +the `Interner` associated types that can be transferred between interners, including `Ty`, `Const`, +`RegionAssumptions`, `GenericArgs`, and the various ID types. + +The `declare_lift_into` macro generates a trait shaped like this: + +```rust +pub trait LiftInto: + Interner< + Ty: Lift, + Const: Lift, + DefId: Lift, + // ...the remaining declared associated types... + > +where + J: Interner, +{ +} +``` + +It also generates a blanket implementation for every source interner whose associated types +satisfy those bounds. + +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. + +To calculate the associated `Lifted` type, the macro transforms type paths syntactically: + +- A path beginning with `I` is rewritten to begin with `J`. +- A standalone ordinary generic type parameter such as `T` becomes + `>::Lifted`. +- The transformation recursively examines nested type arguments. +- Other paths are left unchanged. + +For example: + +```text +I::Ty -> J::Ty +T -> >::Lifted +Binder -> Binder>::Lifted> +Option -> Option +``` + +Each normal field is then converted by calling: + +```rust +field.lift_to_interner(interner) +``` + +Ordinary generic parameters encountered in lifted field types receive `Lift` bounds. +Interner-associated-type requirements are supplied by `I: LiftInto`. + +### How to use it + +```rust +use std::marker::PhantomData; + +use rustc_type_ir::Interner; +use rustc_type_ir_macros::Lift_Generic; + +#[derive(Lift_Generic)] +struct Example { + ty: I::Ty, + value: T, + marker: PhantomData, + #[lift(identity)] + flags: u32, +} +``` + +Use `#[lift(identity)]` for an interner-independent field that should be moved into the lifted value +without conversion. No `Lift` bound is added for that field. + +The unchanged field type must still fit the corresponding field in the generated `Lifted` type. In +practice, `identity` is intended for types that do not mention `I` or ordinary generic parameters +whose lifted type could differ. + +`PhantomData` is handled separately and constructs a new `PhantomData` instead of calling +`lift_to_interner` on the old marker. This is only recognised as `PhantomData` not +`std::marker::PhantomData` so it will need to be included in the file which it +usually is. + +There are some limitations with this macro: + +- The source interner must be named `I` for its paths to be recognized. +- The destination interner introduced by the macro is named `J`. +- Qualified-self paths are not rewritten by the type-parameter transformer. +- Only ordinary generic type parameters declared directly by the item are replaced with their + `Lifted` associated type. +- A field not marked `identity` must support the generated `lift_to_interner` call and produce the + field type expected by the transformed containing type. + +### What its expansion does + +The example expands approximately to: + +```rust +impl ::rustc_type_ir::lift::Lift for Example +where + I: Interner, + J: Interner, + I: ::rustc_type_ir::LiftInto, + T: ::rustc_type_ir::lift::Lift, +{ + type Lifted = Example< + J, + >::Lifted, + >; + + fn lift_to_interner(self, interner: J) -> Self::Lifted { + match self { + Example { + ty, + value, + marker: _, + flags, + } => Example { + ty: ty.lift_to_interner(interner), + value: value.lift_to_interner(interner), + marker: PhantomData, + flags, + }, + } + } +} +``` + +For enums, every variant is reconstructed as the corresponding variant of the lifted enum. + +## `GenericTypeVisitable` +[generictypevisitable]: #generictypevisitable + +Derives `rustc_type_ir::GenericTypeVisitable` for a struct or enum, but not a union. + +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. + +When `rustc_type_ir_macros` is built without its `nightly` feature, the macro: + +1. Introduces a visitor type parameter named `__V` in the generated implementation. +2. Adds `GenericTypeVisitable<__V>` bounds for every field type. +3. Pattern-matches the struct or enum. +4. Calls `GenericTypeVisitable::generic_visit_with` on every field in declaration order. +5. Returns `()` after all fields have been visited. + +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. + +### How to use it + +```rust +use rustc_type_ir_macros::GenericTypeVisitable; + +#[derive(GenericTypeVisitable)] +enum Example { + One(T), + Two { left: T, right: T }, +} +``` + +Every field type must implement `GenericTypeVisitable` for the visitor being used. + +### What its expansion does + +The expansion is approximately equivalent to: + +```rust +impl ::rustc_type_ir::GenericTypeVisitable<__V> for Example +where + T: ::rustc_type_ir::GenericTypeVisitable<__V>, +{ + fn generic_visit_with(&self, __visitor: &mut __V) { + match *self { + Example::One(ref value) => { + ::rustc_type_ir::GenericTypeVisitable::<__V>::generic_visit_with( + value, + __visitor, + ); + } + Example::Two { + ref left, + ref right, + } => { + ::rustc_type_ir::GenericTypeVisitable::<__V>::generic_visit_with( + left, + __visitor, + ); + ::rustc_type_ir::GenericTypeVisitable::<__V>::generic_visit_with( + right, + __visitor, + ); + } + } + } +} +``` + +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