diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:43 +0000 |
commit | 3e3e70d529d8c7d7c4d7bc4fefc9f109393b9245 (patch) | |
tree | daf049b282ab10e8c3d03e409b3cd84ff3f7690c /compiler/rustc_type_ir/src | |
parent | Adding debian version 1.68.2+dfsg1-1. (diff) | |
download | rustc-3e3e70d529d8c7d7c4d7bc4fefc9f109393b9245.tar.xz rustc-3e3e70d529d8c7d7c4d7bc4fefc9f109393b9245.zip |
Merging upstream version 1.69.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_type_ir/src')
-rw-r--r-- | compiler/rustc_type_ir/src/fold.rs | 239 | ||||
-rw-r--r-- | compiler/rustc_type_ir/src/lib.rs | 142 | ||||
-rw-r--r-- | compiler/rustc_type_ir/src/macros.rs | 176 | ||||
-rw-r--r-- | compiler/rustc_type_ir/src/structural_impls.rs | 175 | ||||
-rw-r--r-- | compiler/rustc_type_ir/src/sty.rs | 156 | ||||
-rw-r--r-- | compiler/rustc_type_ir/src/visit.rs | 115 |
6 files changed, 892 insertions, 111 deletions
diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs new file mode 100644 index 000000000..ee4ef57c3 --- /dev/null +++ b/compiler/rustc_type_ir/src/fold.rs @@ -0,0 +1,239 @@ +//! A folding traversal mechanism for complex data structures that contain type +//! information. +//! +//! This is a modifying traversal. It consumes the data structure, producing a +//! (possibly) modified version of it. Both fallible and infallible versions are +//! available. The name is potentially confusing, because this traversal is more +//! like `Iterator::map` than `Iterator::fold`. +//! +//! This traversal has limited flexibility. Only a small number of "types of +//! interest" within the complex data structures can receive custom +//! modification. These are the ones containing the most important type-related +//! information, such as `Ty`, `Predicate`, `Region`, and `Const`. +//! +//! There are three groups of traits involved in each traversal. +//! - `TypeFoldable`. This is implemented once for many types, including: +//! - Types of interest, for which the methods delegate to the folder. +//! - All other types, including generic containers like `Vec` and `Option`. +//! It defines a "skeleton" of how they should be folded. +//! - `TypeSuperFoldable`. This is implemented only for each type of interest, +//! and defines the folding "skeleton" for these types. +//! - `TypeFolder`/`FallibleTypeFolder. One of these is implemented for each +//! folder. This defines how types of interest are folded. +//! +//! This means each fold is a mixture of (a) generic folding operations, and (b) +//! custom fold operations that are specific to the folder. +//! - The `TypeFoldable` impls handle most of the traversal, and call into +//! `TypeFolder`/`FallibleTypeFolder` when they encounter a type of interest. +//! - A `TypeFolder`/`FallibleTypeFolder` may call into another `TypeFoldable` +//! impl, because some of the types of interest are recursive and can contain +//! other types of interest. +//! - A `TypeFolder`/`FallibleTypeFolder` may also call into a `TypeSuperFoldable` +//! impl, because each folder might provide custom handling only for some types +//! of interest, or only for some variants of each type of interest, and then +//! use default traversal for the remaining cases. +//! +//! For example, if you have `struct S(Ty, U)` where `S: TypeFoldable` and `U: +//! TypeFoldable`, and an instance `s = S(ty, u)`, it would be folded like so: +//! ```text +//! s.fold_with(folder) calls +//! - ty.fold_with(folder) calls +//! - folder.fold_ty(ty) may call +//! - ty.super_fold_with(folder) +//! - u.fold_with(folder) +//! ``` +use crate::{visit::TypeVisitable, Interner}; + +/// This trait is implemented for every type that can be folded, +/// providing the skeleton of the traversal. +/// +/// To implement this conveniently, use the derive macro located in +/// `rustc_macros`. +pub trait TypeFoldable<I: Interner>: TypeVisitable<I> { + /// The entry point for folding. To fold a value `t` with a folder `f` + /// call: `t.try_fold_with(f)`. + /// + /// For most types, this just traverses the value, calling `try_fold_with` + /// on each field/element. + /// + /// For types of interest (such as `Ty`), the implementation of method + /// calls a folder method specifically for that type (such as + /// `F::try_fold_ty`). This is where control transfers from `TypeFoldable` + /// to `TypeFolder`. + fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error>; + + /// A convenient alternative to `try_fold_with` for use with infallible + /// folders. Do not override this method, to ensure coherence with + /// `try_fold_with`. + fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self { + self.try_fold_with(folder).into_ok() + } +} + +// This trait is implemented for types of interest. +pub trait TypeSuperFoldable<I: Interner>: TypeFoldable<I> { + /// Provides a default fold for a type of interest. This should only be + /// called within `TypeFolder` methods, when a non-custom traversal is + /// desired for the value of the type of interest passed to that method. + /// For example, in `MyFolder::try_fold_ty(ty)`, it is valid to call + /// `ty.try_super_fold_with(self)`, but any other folding should be done + /// with `xyz.try_fold_with(self)`. + fn try_super_fold_with<F: FallibleTypeFolder<I>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error>; + + /// A convenient alternative to `try_super_fold_with` for use with + /// infallible folders. Do not override this method, to ensure coherence + /// with `try_super_fold_with`. + fn super_fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self { + self.try_super_fold_with(folder).into_ok() + } +} + +/// This trait is implemented for every infallible folding traversal. There is +/// a fold method defined for every type of interest. Each such method has a +/// default that does an "identity" fold. Implementations of these methods +/// often fall back to a `super_fold_with` method if the primary argument +/// doesn't satisfy a particular condition. +/// +/// A blanket implementation of [`FallibleTypeFolder`] will defer to +/// the infallible methods of this trait to ensure that the two APIs +/// are coherent. +pub trait TypeFolder<I: Interner>: FallibleTypeFolder<I, Error = !> { + fn interner(&self) -> I; + + fn fold_binder<T>(&mut self, t: I::Binder<T>) -> I::Binder<T> + where + T: TypeFoldable<I>, + I::Binder<T>: TypeSuperFoldable<I>, + { + t.super_fold_with(self) + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty + where + I::Ty: TypeSuperFoldable<I>, + { + t.super_fold_with(self) + } + + fn fold_region(&mut self, r: I::Region) -> I::Region + where + I::Region: TypeSuperFoldable<I>, + { + r.super_fold_with(self) + } + + fn fold_const(&mut self, c: I::Const) -> I::Const + where + I::Const: TypeSuperFoldable<I>, + { + c.super_fold_with(self) + } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate + where + I::Predicate: TypeSuperFoldable<I>, + { + p.super_fold_with(self) + } +} + +/// This trait is implemented for every folding traversal. There is a fold +/// method defined for every type of interest. Each such method has a default +/// that does an "identity" fold. +/// +/// A blanket implementation of this trait (that defers to the relevant +/// method of [`TypeFolder`]) is provided for all infallible folders in +/// order to ensure the two APIs are coherent. +pub trait FallibleTypeFolder<I: Interner>: Sized { + type Error; + + fn interner(&self) -> I; + + fn try_fold_binder<T>(&mut self, t: I::Binder<T>) -> Result<I::Binder<T>, Self::Error> + where + T: TypeFoldable<I>, + I::Binder<T>: TypeSuperFoldable<I>, + { + t.try_super_fold_with(self) + } + + fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Self::Error> + where + I::Ty: TypeSuperFoldable<I>, + { + t.try_super_fold_with(self) + } + + fn try_fold_region(&mut self, r: I::Region) -> Result<I::Region, Self::Error> + where + I::Region: TypeSuperFoldable<I>, + { + r.try_super_fold_with(self) + } + + fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Self::Error> + where + I::Const: TypeSuperFoldable<I>, + { + c.try_super_fold_with(self) + } + + fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> + where + I::Predicate: TypeSuperFoldable<I>, + { + p.try_super_fold_with(self) + } +} + +// This blanket implementation of the fallible trait for infallible folders +// delegates to infallible methods to ensure coherence. +impl<I: Interner, F> FallibleTypeFolder<I> for F +where + F: TypeFolder<I>, +{ + type Error = !; + + fn interner(&self) -> I { + TypeFolder::interner(self) + } + + fn try_fold_binder<T>(&mut self, t: I::Binder<T>) -> Result<I::Binder<T>, !> + where + T: TypeFoldable<I>, + I::Binder<T>: TypeSuperFoldable<I>, + { + Ok(self.fold_binder(t)) + } + + fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, !> + where + I::Ty: TypeSuperFoldable<I>, + { + Ok(self.fold_ty(t)) + } + + fn try_fold_region(&mut self, r: I::Region) -> Result<I::Region, !> + where + I::Region: TypeSuperFoldable<I>, + { + Ok(self.fold_region(r)) + } + + fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, !> + where + I::Const: TypeSuperFoldable<I>, + { + Ok(self.fold_const(c)) + } + + fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, !> + where + I::Predicate: TypeSuperFoldable<I>, + { + Ok(self.fold_predicate(p)) + } +} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 44004cb0b..5a991e03d 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -1,6 +1,9 @@ +#![feature(associated_type_defaults)] #![feature(fmt_helpers_for_derive)] #![feature(min_specialization)] +#![feature(never_type)] #![feature(rustc_attrs)] +#![feature(unwrap_infallible)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] @@ -18,8 +21,14 @@ use std::hash::Hash; use std::mem::discriminant; pub mod codec; +pub mod fold; pub mod sty; pub mod ty_info; +pub mod visit; + +#[macro_use] +mod macros; +mod structural_impls; pub use codec::*; pub use sty::*; @@ -28,68 +37,69 @@ pub use ty_info::*; /// Needed so we can use #[derive(HashStable_Generic)] pub trait HashStableContext {} -pub trait Interner { - type AdtDef: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type SubstsRef: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type DefId: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type Ty: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type Const: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type Region: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type TypeAndMut: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type Mutability: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type Movability: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type PolyFnSig: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type ListBinderExistentialPredicate: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type BinderListTy: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type ListTy: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type AliasTy: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type ParamTy: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type BoundTy: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type PlaceholderType: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type InferTy: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type ErrorGuaranteed: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; +pub trait Interner: Sized { + type AdtDef: Clone + Debug + Hash + Ord; + type SubstsRef: Clone + Debug + Hash + Ord; + type DefId: Clone + Debug + Hash + Ord; + type Binder<T>; + type Ty: Clone + Debug + Hash + Ord; + type Const: Clone + Debug + Hash + Ord; + type Region: Clone + Debug + Hash + Ord; + type Predicate; + type TypeAndMut: Clone + Debug + Hash + Ord; + type Mutability: Clone + Debug + Hash + Ord; + type Movability: Clone + Debug + Hash + Ord; + type PolyFnSig: Clone + Debug + Hash + Ord; + type ListBinderExistentialPredicate: Clone + Debug + Hash + Ord; + type BinderListTy: Clone + Debug + Hash + Ord; + type ListTy: Clone + Debug + Hash + Ord; + type AliasTy: Clone + Debug + Hash + Ord; + type ParamTy: Clone + Debug + Hash + Ord; + type BoundTy: Clone + Debug + Hash + Ord; + type PlaceholderType: Clone + Debug + Hash + Ord; + type InferTy: Clone + Debug + Hash + Ord; + type ErrorGuaranteed: Clone + Debug + Hash + Ord; type PredicateKind: Clone + Debug + Hash + PartialEq + Eq; - type AllocId: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; + type AllocId: Clone + Debug + Hash + Ord; - type EarlyBoundRegion: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type BoundRegion: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type FreeRegion: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type RegionVid: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; - type PlaceholderRegion: Clone + Debug + Hash + PartialEq + Eq + PartialOrd + Ord; + type EarlyBoundRegion: Clone + Debug + Hash + Ord; + type BoundRegion: Clone + Debug + Hash + Ord; + type FreeRegion: Clone + Debug + Hash + Ord; + type RegionVid: Clone + Debug + Hash + Ord; + type PlaceholderRegion: Clone + Debug + Hash + Ord; } -pub trait InternAs<T: ?Sized, R> { +/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` +/// that produces `T` items. You could combine them with +/// `f(&iter.collect::<Vec<_>>())`, but this requires allocating memory for the +/// `Vec`. +/// +/// This trait allows for faster implementations, intended for cases where the +/// number of items produced by the iterator is small. There is a blanket impl +/// for `T` items, but there is also a fallible impl for `Result<T, E>` items. +pub trait CollectAndApply<T, R>: Sized { type Output; - fn intern_with<F>(self, f: F) -> Self::Output + + /// Produce a result of type `Self::Output` from `iter`. The result will + /// typically be produced by applying `f` on the elements produced by + /// `iter`, though this may not happen in some impls, e.g. if an error + /// occured during iteration. + fn collect_and_apply<I, F>(iter: I, f: F) -> Self::Output where + I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R; } -impl<I, T, R, E> InternAs<T, R> for I -where - E: InternIteratorElement<T, R>, - I: Iterator<Item = E>, -{ - type Output = E::Output; - fn intern_with<F>(self, f: F) -> Self::Output +/// The blanket impl that always collects all elements and applies `f`. +impl<T, R> CollectAndApply<T, R> for T { + type Output = R; + + /// Equivalent to `f(&iter.collect::<Vec<_>>())`. + fn collect_and_apply<I, F>(mut iter: I, f: F) -> R where + I: Iterator<Item = T>, F: FnOnce(&[T]) -> R, { - E::intern_with(self, f) - } -} - -pub trait InternIteratorElement<T, R>: Sized { - type Output; - fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output; -} - -impl<T, R> InternIteratorElement<T, R> for T { - type Output = R; - fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>( - mut iter: I, - f: F, - ) -> Self::Output { // This code is hot enough that it's worth specializing for the most // common length lists, to avoid the overhead of `SmallVec` creation. // Lengths 0, 1, and 2 typically account for ~95% of cases. If @@ -116,23 +126,17 @@ impl<T, R> InternIteratorElement<T, R> for T { } } -impl<'a, T, R> InternIteratorElement<T, R> for &'a T -where - T: Clone + 'a, -{ - type Output = R; - fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { - // This code isn't hot. - f(&iter.cloned().collect::<SmallVec<[_; 8]>>()) - } -} - -impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> { +/// A fallible impl that will fail, without calling `f`, if there are any +/// errors during collection. +impl<T, R, E> CollectAndApply<T, R> for Result<T, E> { type Output = Result<R, E>; - fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>( - mut iter: I, - f: F, - ) -> Self::Output { + + /// Equivalent to `Ok(f(&iter.collect::<Result<Vec<_>>>()?))`. + fn collect_and_apply<I, F>(mut iter: I, f: F) -> Result<R, E> + where + I: Iterator<Item = Result<T, E>>, + F: FnOnce(&[T]) -> R, + { // This code is hot enough that it's worth specializing for the most // common length lists, to avoid the overhead of `SmallVec` creation. // Lengths 0, 1, and 2 typically account for ~95% of cases. If @@ -220,7 +224,8 @@ bitflags! { // which is different from how types/const are freshened. | TypeFlags::HAS_TY_FRESH.bits | TypeFlags::HAS_CT_FRESH.bits - | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; + | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits + | TypeFlags::HAS_RE_ERASED.bits; /// Does this have `Projection`? const HAS_TY_PROJECTION = 1 << 10; @@ -265,6 +270,9 @@ bitflags! { /// Does this value have `InferConst::Fresh`? const HAS_CT_FRESH = 1 << 21; + + /// Does this have `Generator` or `GeneratorWitness`? + const HAS_TY_GENERATOR = 1 << 22; } } diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs new file mode 100644 index 000000000..6c1810397 --- /dev/null +++ b/compiler/rustc_type_ir/src/macros.rs @@ -0,0 +1,176 @@ +/// Used for types that are `Copy` and which **do not care arena +/// allocated data** (i.e., don't need to be folded). +macro_rules! TrivialTypeTraversalImpls { + ($($ty:ty,)+) => { + $( + impl<I: $crate::Interner> $crate::fold::TypeFoldable<I> for $ty { + fn try_fold_with<F: $crate::fold::FallibleTypeFolder<I>>( + self, + _: &mut F, + ) -> ::std::result::Result<Self, F::Error> { + Ok(self) + } + + #[inline] + fn fold_with<F: $crate::fold::TypeFolder<I>>( + self, + _: &mut F, + ) -> Self { + self + } + } + + impl<I: $crate::Interner> $crate::visit::TypeVisitable<I> for $ty { + #[inline] + fn visit_with<F: $crate::visit::TypeVisitor<I>>( + &self, + _: &mut F) + -> ::std::ops::ControlFlow<F::BreakTy> + { + ::std::ops::ControlFlow::Continue(()) + } + } + )+ + }; +} + +macro_rules! EnumTypeTraversalImpl { + (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path { + $($variants:tt)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::fold::TypeFoldable<$tcx> for $s + $(where $($wc)*)* + { + fn try_fold_with<V: $crate::fold::FallibleTypeFolder<$tcx>>( + self, + folder: &mut V, + ) -> ::std::result::Result<Self, V::Error> { + EnumTypeTraversalImpl!(@FoldVariants(self, folder) input($($variants)*) output()) + } + } + }; + + (impl<$($p:tt),*> TypeVisitable<$tcx:tt> for $s:path { + $($variants:tt)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::visit::TypeVisitable<$tcx> for $s + $(where $($wc)*)* + { + fn visit_with<V: $crate::visit::TypeVisitor<$tcx>>( + &self, + visitor: &mut V, + ) -> ::std::ops::ControlFlow<V::BreakTy> { + EnumTypeTraversalImpl!(@VisitVariants(self, visitor) input($($variants)*) output()) + } + } + }; + + (@FoldVariants($this:expr, $folder:expr) input() output($($output:tt)*)) => { + Ok(match $this { + $($output)* + }) + }; + + (@FoldVariants($this:expr, $folder:expr) + input( ($variant:path) ( $($variant_arg:ident),* ) , $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeTraversalImpl!( + @FoldVariants($this, $folder) + input($($input)*) + output( + $variant ( $($variant_arg),* ) => { + $variant ( + $($crate::fold::TypeFoldable::try_fold_with($variant_arg, $folder)?),* + ) + } + $($output)* + ) + ) + }; + + (@FoldVariants($this:expr, $folder:expr) + input( ($variant:path) { $($variant_arg:ident),* $(,)? } , $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeTraversalImpl!( + @FoldVariants($this, $folder) + input($($input)*) + output( + $variant { $($variant_arg),* } => { + $variant { + $($variant_arg: $crate::fold::TypeFoldable::fold_with( + $variant_arg, $folder + )?),* } + } + $($output)* + ) + ) + }; + + (@FoldVariants($this:expr, $folder:expr) + input( ($variant:path), $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeTraversalImpl!( + @FoldVariants($this, $folder) + input($($input)*) + output( + $variant => { $variant } + $($output)* + ) + ) + }; + + (@VisitVariants($this:expr, $visitor:expr) input() output($($output:tt)*)) => { + match $this { + $($output)* + } + }; + + (@VisitVariants($this:expr, $visitor:expr) + input( ($variant:path) ( $($variant_arg:ident),* ) , $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeTraversalImpl!( + @VisitVariants($this, $visitor) + input($($input)*) + output( + $variant ( $($variant_arg),* ) => { + $($crate::visit::TypeVisitable::visit_with( + $variant_arg, $visitor + )?;)* + ::std::ops::ControlFlow::Continue(()) + } + $($output)* + ) + ) + }; + + (@VisitVariants($this:expr, $visitor:expr) + input( ($variant:path) { $($variant_arg:ident),* $(,)? } , $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeTraversalImpl!( + @VisitVariants($this, $visitor) + input($($input)*) + output( + $variant { $($variant_arg),* } => { + $($crate::visit::TypeVisitable::visit_with( + $variant_arg, $visitor + )?;)* + ::std::ops::ControlFlow::Continue(()) + } + $($output)* + ) + ) + }; + + (@VisitVariants($this:expr, $visitor:expr) + input( ($variant:path), $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeTraversalImpl!( + @VisitVariants($this, $visitor) + input($($input)*) + output( + $variant => { ::std::ops::ControlFlow::Continue(()) } + $($output)* + ) + ) + }; +} diff --git a/compiler/rustc_type_ir/src/structural_impls.rs b/compiler/rustc_type_ir/src/structural_impls.rs new file mode 100644 index 000000000..3ebe24104 --- /dev/null +++ b/compiler/rustc_type_ir/src/structural_impls.rs @@ -0,0 +1,175 @@ +//! This module contains implementations of the `TypeFoldable` and `TypeVisitable` +//! traits for various types in the Rust compiler. Most are written by hand, though +//! we've recently added some macros and proc-macros to help with the tedium. + +use crate::fold::{FallibleTypeFolder, TypeFoldable}; +use crate::visit::{TypeVisitable, TypeVisitor}; +use crate::Interner; +use rustc_data_structures::functor::IdFunctor; +use rustc_index::vec::{Idx, IndexVec}; + +use std::ops::ControlFlow; +use std::rc::Rc; +use std::sync::Arc; + +/////////////////////////////////////////////////////////////////////////// +// Atomic structs +// +// For things that don't carry any arena-allocated data (and are +// copy...), just add them to this list. + +TrivialTypeTraversalImpls! { + (), + bool, + usize, + u16, + u32, + u64, + String, + crate::DebruijnIndex, +} + +/////////////////////////////////////////////////////////////////////////// +// Traversal implementations. + +impl<I: Interner, T: TypeFoldable<I>, U: TypeFoldable<I>> TypeFoldable<I> for (T, U) { + fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<(T, U), F::Error> { + Ok((self.0.try_fold_with(folder)?, self.1.try_fold_with(folder)?)) + } +} + +impl<I: Interner, T: TypeVisitable<I>, U: TypeVisitable<I>> TypeVisitable<I> for (T, U) { + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.0.visit_with(visitor)?; + self.1.visit_with(visitor) + } +} + +impl<I: Interner, A: TypeFoldable<I>, B: TypeFoldable<I>, C: TypeFoldable<I>> TypeFoldable<I> + for (A, B, C) +{ + fn try_fold_with<F: FallibleTypeFolder<I>>( + self, + folder: &mut F, + ) -> Result<(A, B, C), F::Error> { + Ok(( + self.0.try_fold_with(folder)?, + self.1.try_fold_with(folder)?, + self.2.try_fold_with(folder)?, + )) + } +} + +impl<I: Interner, A: TypeVisitable<I>, B: TypeVisitable<I>, C: TypeVisitable<I>> TypeVisitable<I> + for (A, B, C) +{ + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.0.visit_with(visitor)?; + self.1.visit_with(visitor)?; + self.2.visit_with(visitor) + } +} + +EnumTypeTraversalImpl! { + impl<I, T> TypeFoldable<I> for Option<T> { + (Some)(a), + (None), + } where I: Interner, T: TypeFoldable<I> +} +EnumTypeTraversalImpl! { + impl<I, T> TypeVisitable<I> for Option<T> { + (Some)(a), + (None), + } where I: Interner, T: TypeVisitable<I> +} + +EnumTypeTraversalImpl! { + impl<I, T, E> TypeFoldable<I> for Result<T, E> { + (Ok)(a), + (Err)(a), + } where I: Interner, T: TypeFoldable<I>, E: TypeFoldable<I>, +} +EnumTypeTraversalImpl! { + impl<I, T, E> TypeVisitable<I> for Result<T, E> { + (Ok)(a), + (Err)(a), + } where I: Interner, T: TypeVisitable<I>, E: TypeVisitable<I>, +} + +impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Rc<T> { + fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> { + self.try_map_id(|value| value.try_fold_with(folder)) + } +} + +impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Rc<T> { + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + (**self).visit_with(visitor) + } +} + +impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Arc<T> { + fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> { + self.try_map_id(|value| value.try_fold_with(folder)) + } +} + +impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Arc<T> { + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + (**self).visit_with(visitor) + } +} + +impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<T> { + fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> { + self.try_map_id(|value| value.try_fold_with(folder)) + } +} + +impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<T> { + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + (**self).visit_with(visitor) + } +} + +impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Vec<T> { + fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> { + self.try_map_id(|t| t.try_fold_with(folder)) + } +} + +impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Vec<T> { + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +} + +impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for &[T] { + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +} + +impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<[T]> { + fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> { + self.try_map_id(|t| t.try_fold_with(folder)) + } +} + +impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<[T]> { + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +} + +impl<I: Interner, T: TypeFoldable<I>, Ix: Idx> TypeFoldable<I> for IndexVec<Ix, T> { + fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> { + self.try_map_id(|x| x.try_fold_with(folder)) + } +} + +impl<I: Interner, T: TypeVisitable<I>, Ix: Idx> TypeVisitable<I> for IndexVec<Ix, T> { + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +} diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index 5f29588ae..ebe2b76ae 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -26,11 +26,9 @@ pub enum DynKind { Dyn, /// A sized `dyn* Trait` object /// - /// These objects are represented as a `(data, vtable)` pair where `data` is a ptr-sized value - /// (often a pointer to the real object, but not necessarily) and `vtable` is a pointer to - /// the vtable for `dyn* Trait`. The representation is essentially the same as `&dyn Trait` - /// or similar, but the drop function included in the vtable is responsible for freeing the - /// underlying storage if needed. This allows a `dyn*` object to be treated agnostically with + /// These objects are represented as a `(data, vtable)` pair where `data` is a value of some + /// ptr-sized and ptr-aligned dynamically determined type `T` and `vtable` is a pointer to the + /// vtable of `impl T for Trait`. This allows a `dyn*` object to be treated agnostically with /// respect to whether it points to a `Box<T>`, `Rc<T>`, etc. DynStar, } @@ -160,6 +158,32 @@ pub enum TyKind<I: Interner> { /// ``` GeneratorWitness(I::BinderListTy), + /// A type representing the types stored inside a generator. + /// This should only appear as part of the `GeneratorSubsts`. + /// + /// Unlike upvars, the witness can reference lifetimes from + /// inside of the generator itself. To deal with them in + /// the type of the generator, we convert them to higher ranked + /// lifetimes bound by the witness itself. + /// + /// This variant is only using when `drop_tracking_mir` is set. + /// This contains the `DefId` and the `SubstRef` of the generator. + /// The actual witness types are computed on MIR by the `mir_generator_witnesses` query. + /// + /// Looking at the following example, the witness for this generator + /// may end up as something like `for<'a> [Vec<i32>, &'a Vec<i32>]`: + /// + /// ```ignore UNSOLVED (ask @compiler-errors, should this error? can we just swap the yields?) + /// #![feature(generators)] + /// |a| { + /// let x = &vec![3]; + /// yield a; + /// yield x[0]; + /// } + /// # ; + /// ``` + GeneratorWitnessMIR(I::DefId, I::SubstsRef), + /// The never type `!`. Never, @@ -241,6 +265,7 @@ const fn tykind_discriminant<I: Interner>(value: &TyKind<I>) -> usize { Placeholder(_) => 23, Infer(_) => 24, Error(_) => 25, + GeneratorWitnessMIR(_, _) => 26, } } @@ -266,6 +291,7 @@ impl<I: Interner> Clone for TyKind<I> { Closure(d, s) => Closure(d.clone(), s.clone()), Generator(d, s, m) => Generator(d.clone(), s.clone(), m.clone()), GeneratorWitness(g) => GeneratorWitness(g.clone()), + GeneratorWitnessMIR(d, s) => GeneratorWitnessMIR(d.clone(), s.clone()), Never => Never, Tuple(t) => Tuple(t.clone()), Alias(k, p) => Alias(*k, p.clone()), @@ -282,43 +308,51 @@ impl<I: Interner> Clone for TyKind<I> { impl<I: Interner> PartialEq for TyKind<I> { #[inline] fn eq(&self, other: &TyKind<I>) -> bool { - tykind_discriminant(self) == tykind_discriminant(other) - && match (self, other) { - (Int(a_i), Int(b_i)) => a_i == b_i, - (Uint(a_u), Uint(b_u)) => a_u == b_u, - (Float(a_f), Float(b_f)) => a_f == b_f, - (Adt(a_d, a_s), Adt(b_d, b_s)) => a_d == b_d && a_s == b_s, - (Foreign(a_d), Foreign(b_d)) => a_d == b_d, - (Array(a_t, a_c), Array(b_t, b_c)) => a_t == b_t && a_c == b_c, - (Slice(a_t), Slice(b_t)) => a_t == b_t, - (RawPtr(a_t), RawPtr(b_t)) => a_t == b_t, - (Ref(a_r, a_t, a_m), Ref(b_r, b_t, b_m)) => a_r == b_r && a_t == b_t && a_m == b_m, - (FnDef(a_d, a_s), FnDef(b_d, b_s)) => a_d == b_d && a_s == b_s, - (FnPtr(a_s), FnPtr(b_s)) => a_s == b_s, - (Dynamic(a_p, a_r, a_repr), Dynamic(b_p, b_r, b_repr)) => { - a_p == b_p && a_r == b_r && a_repr == b_repr - } - (Closure(a_d, a_s), Closure(b_d, b_s)) => a_d == b_d && a_s == b_s, - (Generator(a_d, a_s, a_m), Generator(b_d, b_s, b_m)) => { - a_d == b_d && a_s == b_s && a_m == b_m - } - (GeneratorWitness(a_g), GeneratorWitness(b_g)) => a_g == b_g, - (Tuple(a_t), Tuple(b_t)) => a_t == b_t, - (Alias(a_i, a_p), Alias(b_i, b_p)) => a_i == b_i && a_p == b_p, - (Param(a_p), Param(b_p)) => a_p == b_p, - (Bound(a_d, a_b), Bound(b_d, b_b)) => a_d == b_d && a_b == b_b, - (Placeholder(a_p), Placeholder(b_p)) => a_p == b_p, - (Infer(a_t), Infer(b_t)) => a_t == b_t, - (Error(a_e), Error(b_e)) => a_e == b_e, - (Bool, Bool) | (Char, Char) | (Str, Str) | (Never, Never) => true, - _ => { - debug_assert!( - false, - "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}" - ); - true - } + // You might expect this `match` to be preceded with this: + // + // tykind_discriminant(self) == tykind_discriminant(other) && + // + // but the data patterns in practice are such that a comparison + // succeeds 99%+ of the time, and it's faster to omit it. + match (self, other) { + (Int(a_i), Int(b_i)) => a_i == b_i, + (Uint(a_u), Uint(b_u)) => a_u == b_u, + (Float(a_f), Float(b_f)) => a_f == b_f, + (Adt(a_d, a_s), Adt(b_d, b_s)) => a_d == b_d && a_s == b_s, + (Foreign(a_d), Foreign(b_d)) => a_d == b_d, + (Array(a_t, a_c), Array(b_t, b_c)) => a_t == b_t && a_c == b_c, + (Slice(a_t), Slice(b_t)) => a_t == b_t, + (RawPtr(a_t), RawPtr(b_t)) => a_t == b_t, + (Ref(a_r, a_t, a_m), Ref(b_r, b_t, b_m)) => a_r == b_r && a_t == b_t && a_m == b_m, + (FnDef(a_d, a_s), FnDef(b_d, b_s)) => a_d == b_d && a_s == b_s, + (FnPtr(a_s), FnPtr(b_s)) => a_s == b_s, + (Dynamic(a_p, a_r, a_repr), Dynamic(b_p, b_r, b_repr)) => { + a_p == b_p && a_r == b_r && a_repr == b_repr + } + (Closure(a_d, a_s), Closure(b_d, b_s)) => a_d == b_d && a_s == b_s, + (Generator(a_d, a_s, a_m), Generator(b_d, b_s, b_m)) => { + a_d == b_d && a_s == b_s && a_m == b_m + } + (GeneratorWitness(a_g), GeneratorWitness(b_g)) => a_g == b_g, + (GeneratorWitnessMIR(a_d, a_s), GeneratorWitnessMIR(b_d, b_s)) => { + a_d == b_d && a_s == b_s } + (Tuple(a_t), Tuple(b_t)) => a_t == b_t, + (Alias(a_i, a_p), Alias(b_i, b_p)) => a_i == b_i && a_p == b_p, + (Param(a_p), Param(b_p)) => a_p == b_p, + (Bound(a_d, a_b), Bound(b_d, b_b)) => a_d == b_d && a_b == b_b, + (Placeholder(a_p), Placeholder(b_p)) => a_p == b_p, + (Infer(a_t), Infer(b_t)) => a_t == b_t, + (Error(a_e), Error(b_e)) => a_e == b_e, + (Bool, Bool) | (Char, Char) | (Str, Str) | (Never, Never) => true, + _ => { + debug_assert!( + tykind_discriminant(self) != tykind_discriminant(other), + "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}" + ); + false + } + } } } @@ -360,6 +394,13 @@ impl<I: Interner> Ord for TyKind<I> { a_d.cmp(b_d).then_with(|| a_s.cmp(b_s).then_with(|| a_m.cmp(b_m))) } (GeneratorWitness(a_g), GeneratorWitness(b_g)) => a_g.cmp(b_g), + ( + GeneratorWitnessMIR(a_d, a_s), + GeneratorWitnessMIR(b_d, b_s), + ) => match Ord::cmp(a_d, b_d) { + Ordering::Equal => Ord::cmp(a_s, b_s), + cmp => cmp, + }, (Tuple(a_t), Tuple(b_t)) => a_t.cmp(b_t), (Alias(a_i, a_p), Alias(b_i, b_p)) => a_i.cmp(b_i).then_with(|| a_p.cmp(b_p)), (Param(a_p), Param(b_p)) => a_p.cmp(b_p), @@ -369,7 +410,7 @@ impl<I: Interner> Ord for TyKind<I> { (Error(a_e), Error(b_e)) => a_e.cmp(b_e), (Bool, Bool) | (Char, Char) | (Str, Str) | (Never, Never) => Ordering::Equal, _ => { - debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}"); + debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}"); Ordering::Equal } } @@ -421,6 +462,10 @@ impl<I: Interner> hash::Hash for TyKind<I> { m.hash(state) } GeneratorWitness(g) => g.hash(state), + GeneratorWitnessMIR(d, s) => { + d.hash(state); + s.hash(state); + } Tuple(t) => t.hash(state), Alias(i, p) => { i.hash(state); @@ -461,6 +506,7 @@ impl<I: Interner> fmt::Debug for TyKind<I> { Closure(d, s) => f.debug_tuple_field2_finish("Closure", d, s), Generator(d, s, m) => f.debug_tuple_field3_finish("Generator", d, s, m), GeneratorWitness(g) => f.debug_tuple_field1_finish("GeneratorWitness", g), + GeneratorWitnessMIR(d, s) => f.debug_tuple_field2_finish("GeneratorWitnessMIR", d, s), Never => f.write_str("Never"), Tuple(t) => f.debug_tuple_field1_finish("Tuple", t), Alias(i, a) => f.debug_tuple_field2_finish("Alias", i, a), @@ -559,6 +605,10 @@ where GeneratorWitness(b) => e.emit_enum_variant(disc, |e| { b.encode(e); }), + GeneratorWitnessMIR(def_id, substs) => e.emit_enum_variant(disc, |e| { + def_id.encode(e); + substs.encode(e); + }), Never => e.emit_enum_variant(disc, |_| {}), Tuple(substs) => e.emit_enum_variant(disc, |e| { substs.encode(e); @@ -641,6 +691,7 @@ where 23 => Placeholder(Decodable::decode(d)), 24 => Infer(Decodable::decode(d)), 25 => Error(Decodable::decode(d)), + 26 => GeneratorWitnessMIR(Decodable::decode(d), Decodable::decode(d)), _ => panic!( "{}", format!( @@ -742,6 +793,10 @@ where GeneratorWitness(b) => { b.hash_stable(__hcx, __hasher); } + GeneratorWitnessMIR(def_id, substs) => { + def_id.hash_stable(__hcx, __hasher); + substs.hash_stable(__hcx, __hasher); + } Never => {} Tuple(substs) => { substs.hash_stable(__hcx, __hasher); @@ -903,6 +958,9 @@ pub enum RegionKind<I: Interner> { /// Erased region, used by trait selection, in MIR and during codegen. ReErased, + + /// A region that resulted from some other error. Used exclusively for diagnostics. + ReError(I::ErrorGuaranteed), } // This is manually implemented for `RegionKind` because `std::mem::discriminant` @@ -917,6 +975,7 @@ const fn regionkind_discriminant<I: Interner>(value: &RegionKind<I>) -> usize { ReVar(_) => 4, RePlaceholder(_) => 5, ReErased => 6, + ReError(_) => 7, } } @@ -928,6 +987,7 @@ where I::FreeRegion: Copy, I::RegionVid: Copy, I::PlaceholderRegion: Copy, + I::ErrorGuaranteed: Copy, { } @@ -942,6 +1002,7 @@ impl<I: Interner> Clone for RegionKind<I> { ReVar(r) => ReVar(r.clone()), RePlaceholder(r) => RePlaceholder(r.clone()), ReErased => ReErased, + ReError(r) => ReError(r.clone()), } } } @@ -959,10 +1020,11 @@ impl<I: Interner> PartialEq for RegionKind<I> { (ReVar(a_r), ReVar(b_r)) => a_r == b_r, (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r == b_r, (ReErased, ReErased) => true, + (ReError(_), ReError(_)) => true, _ => { debug_assert!( false, - "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}" + "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}" ); true } @@ -1020,6 +1082,7 @@ impl<I: Interner> hash::Hash for RegionKind<I> { ReVar(r) => r.hash(state), RePlaceholder(r) => r.hash(state), ReErased => (), + ReError(_) => (), } } } @@ -1043,6 +1106,8 @@ impl<I: Interner> fmt::Debug for RegionKind<I> { RePlaceholder(placeholder) => write!(f, "RePlaceholder({placeholder:?})"), ReErased => f.write_str("ReErased"), + + ReError(_) => f.write_str("ReError"), } } } @@ -1077,6 +1142,7 @@ where a.encode(e); }), ReErased => e.emit_enum_variant(disc, |_| {}), + ReError(_) => e.emit_enum_variant(disc, |_| {}), } } } @@ -1089,6 +1155,7 @@ where I::FreeRegion: Decodable<D>, I::RegionVid: Decodable<D>, I::PlaceholderRegion: Decodable<D>, + I::ErrorGuaranteed: Decodable<D>, { fn decode(d: &mut D) -> Self { match Decoder::read_usize(d) { @@ -1099,6 +1166,7 @@ where 4 => ReVar(Decodable::decode(d)), 5 => RePlaceholder(Decodable::decode(d)), 6 => ReErased, + 7 => ReError(Decodable::decode(d)), _ => panic!( "{}", format!( @@ -1127,7 +1195,7 @@ where ) { std::mem::discriminant(self).hash_stable(hcx, hasher); match self { - ReErased | ReStatic => { + ReErased | ReStatic | ReError(_) => { // No variant fields to hash for these ... } ReLateBound(d, r) => { diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs new file mode 100644 index 000000000..62239fd20 --- /dev/null +++ b/compiler/rustc_type_ir/src/visit.rs @@ -0,0 +1,115 @@ +//! A visiting traversal mechanism for complex data structures that contain type +//! information. +//! +//! This is a read-only traversal of the data structure. +//! +//! This traversal has limited flexibility. Only a small number of "types of +//! interest" within the complex data structures can receive custom +//! visitation. These are the ones containing the most important type-related +//! information, such as `Ty`, `Predicate`, `Region`, and `Const`. +//! +//! There are three groups of traits involved in each traversal. +//! - `TypeVisitable`. This is implemented once for many types, including: +//! - Types of interest, for which the methods delegate to the visitor. +//! - All other types, including generic containers like `Vec` and `Option`. +//! It defines a "skeleton" of how they should be visited. +//! - `TypeSuperVisitable`. This is implemented only for each type of interest, +//! and defines the visiting "skeleton" for these types. +//! - `TypeVisitor`. This is implemented for each visitor. This defines how +//! types of interest are visited. +//! +//! This means each visit is a mixture of (a) generic visiting operations, and (b) +//! custom visit operations that are specific to the visitor. +//! - The `TypeVisitable` impls handle most of the traversal, and call into +//! `TypeVisitor` when they encounter a type of interest. +//! - A `TypeVisitor` may call into another `TypeVisitable` impl, because some of +//! the types of interest are recursive and can contain other types of interest. +//! - A `TypeVisitor` may also call into a `TypeSuperVisitable` impl, because each +//! visitor might provide custom handling only for some types of interest, or +//! only for some variants of each type of interest, and then use default +//! traversal for the remaining cases. +//! +//! For example, if you have `struct S(Ty, U)` where `S: TypeVisitable` and `U: +//! TypeVisitable`, and an instance `s = S(ty, u)`, it would be visited like so: +//! ```text +//! s.visit_with(visitor) calls +//! - ty.visit_with(visitor) calls +//! - visitor.visit_ty(ty) may call +//! - ty.super_visit_with(visitor) +//! - u.visit_with(visitor) +//! ``` +use crate::Interner; + +use std::fmt; +use std::ops::ControlFlow; + +/// This trait is implemented for every type that can be visited, +/// providing the skeleton of the traversal. +/// +/// To implement this conveniently, use the derive macro located in +/// `rustc_macros`. +pub trait TypeVisitable<I: Interner>: fmt::Debug + Clone { + /// The entry point for visiting. To visit a value `t` with a visitor `v` + /// call: `t.visit_with(v)`. + /// + /// For most types, this just traverses the value, calling `visit_with` on + /// each field/element. + /// + /// For types of interest (such as `Ty`), the implementation of this method + /// that calls a visitor method specifically for that type (such as + /// `V::visit_ty`). This is where control transfers from `TypeFoldable` to + /// `TypeVisitor`. + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>; +} + +pub trait TypeSuperVisitable<I: Interner>: TypeVisitable<I> { + /// Provides a default visit for a type of interest. This should only be + /// called within `TypeVisitor` methods, when a non-custom traversal is + /// desired for the value of the type of interest passed to that method. + /// For example, in `MyVisitor::visit_ty(ty)`, it is valid to call + /// `ty.super_visit_with(self)`, but any other visiting should be done + /// with `xyz.visit_with(self)`. + fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>; +} + +/// This trait is implemented for every visiting traversal. There is a visit +/// method defined for every type of interest. Each such method has a default +/// that recurses into the type's fields in a non-custom fashion. +pub trait TypeVisitor<I: Interner>: Sized { + type BreakTy = !; + + fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> ControlFlow<Self::BreakTy> + where + I::Binder<T>: TypeSuperVisitable<I>, + { + t.super_visit_with(self) + } + + fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> + where + I::Ty: TypeSuperVisitable<I>, + { + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: I::Region) -> ControlFlow<Self::BreakTy> + where + I::Region: TypeSuperVisitable<I>, + { + r.super_visit_with(self) + } + + fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy> + where + I::Const: TypeSuperVisitable<I>, + { + c.super_visit_with(self) + } + + fn visit_predicate(&mut self, p: I::Predicate) -> ControlFlow<Self::BreakTy> + where + I::Predicate: TypeSuperVisitable<I>, + { + p.super_visit_with(self) + } +} |