diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 193dbd75fbd57..8ea26167f7667 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -23,8 +23,7 @@ mod newtype; mod query; mod serialize; mod symbols; -mod type_foldable; -mod type_visitable; +mod traversable; #[proc_macro] pub fn current_rustc_version(input: TokenStream) -> TokenStream { @@ -75,25 +74,62 @@ decl_derive!([TyEncodable] => serialize::type_encodable_derive); decl_derive!([MetadataDecodable] => serialize::meta_decodable_derive); decl_derive!([MetadataEncodable] => serialize::meta_encodable_derive); decl_derive!( - [TypeFoldable, attributes(type_foldable)] => + [TypeFoldable, attributes(type_foldable, inline_traversals)] => /// Derives `TypeFoldable` for the annotated `struct` or `enum` (`union` is not supported). /// - /// The fold will produce a value of the same struct or enum variant as the input, with - /// each field respectively folded using the `TypeFoldable` implementation for its type. - /// However, if a field of a struct or an enum variant is annotated with - /// `#[type_foldable(identity)]` then that field will retain its incumbent value (and its - /// type is not required to implement `TypeFoldable`). - type_foldable::type_foldable_derive + /// Folds will produce a value of the same struct or enum variant as the input, with each field + /// respectively folded (in definition order) using the `TypeFoldable` implementation for its + /// type. However, if a field of a struct or of an enum variant is annotated with + /// `#[type_foldable(identity)]` then that field will retain its incumbent value (and its type + /// is not required to implement `TypeFoldable`). However use of this attribute is dangerous + /// and should be used with extreme caution: should the type of the annotated field contain + /// (now or in the future) a type that is of interest to a folder, it will not get folded (which + /// may result in unexpected, hard-to-track bugs that could result in unsoundness). + /// + /// If the annotated item has a `'tcx` lifetime parameter, then that will be used as the + /// lifetime for the type context/interner; otherwise the lifetime of the type context/interner + /// will be unrelated to the annotated type. It therefore matters how any lifetime parameters of + /// the annotated type are named. For example, deriving `TypeFoldable` for both `Foo<'a>` and + /// `Bar<'tcx>` will respectively produce: + /// + /// `impl<'a, 'tcx> TypeFoldable> for Foo<'a>` + /// + /// and + /// + /// `impl<'tcx> TypeFoldable> for Bar<'tcx>` + /// + /// The annotated item may be decorated with an `#[inline_traversals]` attribute to cause the + /// generated folding method to be marked `#[inline]`. + traversable::traversable_derive:: ); decl_derive!( - [TypeVisitable, attributes(type_visitable)] => + [TypeVisitable, attributes(type_visitable, inline_traversals)] => /// Derives `TypeVisitable` for the annotated `struct` or `enum` (`union` is not supported). /// - /// Each field of the struct or enum variant will be visited in definition order, using the - /// `TypeVisitable` implementation for its type. However, if a field of a struct or an enum - /// variant is annotated with `#[type_visitable(ignore)]` then that field will not be - /// visited (and its type is not required to implement `TypeVisitable`). - type_visitable::type_visitable_derive + /// Each field of the struct or enum variant will be visited (in definition order) using the + /// `TypeVisitable` implementation for its type. However, if a field of a struct or of an enum + /// variant is annotated with `#[type_visitable(ignore)]` then that field will not be visited + /// (and its type is not required to implement `TypeVisitable`). However use of this attribute + /// is dangerous and should be used with extreme caution: should the type of the annotated + /// field (now or in the future) a type that is of interest to a visitor, it will not get + /// visited (which may result in unexpected, hard-to-track bugs that could result in + /// unsoundness). + /// + /// If the annotated item has a `'tcx` lifetime parameter, then that will be used as the + /// lifetime for the type context/interner; otherwise the lifetime of the type context/interner + /// will be unrelated to the annotated type. It therefore matters how any lifetime parameters of + /// the annotated type are named. For example, deriving `TypeVisitable` for both `Foo<'a>` and + /// `Bar<'tcx>` will respectively produce: + /// + /// `impl<'a, 'tcx> TypeVisitable> for Foo<'a>` + /// + /// and + /// + /// `impl<'tcx> TypeVisitable> for Bar<'tcx>` + /// + /// The annotated item may be decorated with an `#[inline_traversals]` attribute to cause the + /// generated folding method to be marked `#[inline]`. + traversable::traversable_derive:: ); decl_derive!([Lift, attributes(lift)] => lift::lift_derive); decl_derive!( diff --git a/compiler/rustc_macros/src/traversable.rs b/compiler/rustc_macros/src/traversable.rs new file mode 100644 index 0000000000000..04f448a277572 --- /dev/null +++ b/compiler/rustc_macros/src/traversable.rs @@ -0,0 +1,135 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::parse_quote; + +pub struct Foldable; +pub struct Visitable; + +/// An abstraction over traversable traits. +pub trait Traversable { + /// The trait that this `Traversable` represents. + fn traversable() -> TokenStream; + + /// The `match` arms for a traversal of this type. + fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream; + + /// The body of an implementation given the match `arms`. + fn impl_body(arms: impl ToTokens, attrs: impl ToTokens) -> TokenStream; +} + +impl Traversable for Foldable { + fn traversable() -> TokenStream { + quote! { ::rustc_middle::ty::fold::TypeFoldable<::rustc_middle::ty::TyCtxt<'tcx>> } + } + fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream { + structure.each_variant(|vi| { + let bindings = vi.bindings(); + vi.construct(|_, index| { + let bind = &bindings[index]; + + let mut fixed = false; + + // retain value of fields with #[type_foldable(identity)] + bind.ast().attrs.iter().for_each(|x| { + if !x.path().is_ident("type_foldable") { + return; + } + let _ = x.parse_nested_meta(|nested| { + if nested.path.is_ident("identity") { + fixed = true; + } + Ok(()) + }); + }); + + if fixed { + bind.to_token_stream() + } else { + quote! { + ::rustc_middle::ty::fold::TypeFoldable::try_fold_with(#bind, __folder)? + } + } + }) + }) + } + fn impl_body(arms: impl ToTokens, attrs: impl ToTokens) -> TokenStream { + quote! { + #attrs + fn try_fold_with<__F: ::rustc_middle::ty::fold::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>( + self, + __folder: &mut __F + ) -> ::core::result::Result { + ::core::result::Result::Ok(match self { #arms }) + } + } + } +} + +impl Traversable for Visitable { + fn traversable() -> TokenStream { + quote! { ::rustc_middle::ty::visit::TypeVisitable<::rustc_middle::ty::TyCtxt<'tcx>> } + } + fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream { + // ignore fields with #[type_visitable(ignore)] + structure.filter(|bi| { + let mut ignored = false; + + bi.ast().attrs.iter().for_each(|attr| { + if !attr.path().is_ident("type_visitable") { + return; + } + let _ = attr.parse_nested_meta(|nested| { + if nested.path.is_ident("ignore") { + ignored = true; + } + Ok(()) + }); + }); + + !ignored + }); + + structure.each(|bind| { + quote! { + ::rustc_middle::ty::visit::TypeVisitable::visit_with(#bind, __visitor)?; + } + }) + } + fn impl_body(arms: impl ToTokens, attrs: impl ToTokens) -> TokenStream { + quote! { + #attrs + fn visit_with<__V: ::rustc_middle::ty::visit::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>( + &self, + __visitor: &mut __V + ) -> ::std::ops::ControlFlow<__V::BreakTy> { + match self { #arms } + ::std::ops::ControlFlow::Continue(()) + } + } + } +} + +pub fn traversable_derive( + mut structure: synstructure::Structure<'_>, +) -> TokenStream { + if let syn::Data::Union(_) = structure.ast().data { + panic!("cannot derive on union") + } + + structure.add_bounds(synstructure::AddBounds::Generics); + structure.bind_with(|_| synstructure::BindStyle::Move); + + if !structure.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { + structure.add_impl_generic(parse_quote! { 'tcx }); + } + + let arms = T::arms(&mut structure); + let attrs = structure + .ast() + .attrs + .iter() + .any(|attr| attr.path().is_ident("inline_traversals")) + .then_some(quote! { #[inline] }); + + structure.bound_impl(T::traversable(), T::impl_body(arms, attrs)) +} diff --git a/compiler/rustc_macros/src/type_foldable.rs b/compiler/rustc_macros/src/type_foldable.rs deleted file mode 100644 index 5ee4d8793135c..0000000000000 --- a/compiler/rustc_macros/src/type_foldable.rs +++ /dev/null @@ -1,56 +0,0 @@ -use quote::{quote, ToTokens}; -use syn::parse_quote; - -pub fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { - if let syn::Data::Union(_) = s.ast().data { - panic!("cannot derive on union") - } - - if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { - s.add_impl_generic(parse_quote! { 'tcx }); - } - - s.add_bounds(synstructure::AddBounds::Generics); - s.bind_with(|_| synstructure::BindStyle::Move); - let body_fold = s.each_variant(|vi| { - let bindings = vi.bindings(); - vi.construct(|_, index| { - let bind = &bindings[index]; - - let mut fixed = false; - - // retain value of fields with #[type_foldable(identity)] - bind.ast().attrs.iter().for_each(|x| { - if !x.path().is_ident("type_foldable") { - return; - } - let _ = x.parse_nested_meta(|nested| { - if nested.path.is_ident("identity") { - fixed = true; - } - Ok(()) - }); - }); - - if fixed { - bind.to_token_stream() - } else { - quote! { - ::rustc_middle::ty::fold::TypeFoldable::try_fold_with(#bind, __folder)? - } - } - }) - }); - - s.bound_impl( - quote!(::rustc_middle::ty::fold::TypeFoldable<::rustc_middle::ty::TyCtxt<'tcx>>), - quote! { - fn try_fold_with<__F: ::rustc_middle::ty::fold::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>( - self, - __folder: &mut __F - ) -> Result { - Ok(match self { #body_fold }) - } - }, - ) -} diff --git a/compiler/rustc_macros/src/type_visitable.rs b/compiler/rustc_macros/src/type_visitable.rs deleted file mode 100644 index dcd505a105e57..0000000000000 --- a/compiler/rustc_macros/src/type_visitable.rs +++ /dev/null @@ -1,52 +0,0 @@ -use quote::quote; -use syn::parse_quote; - -pub fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { - if let syn::Data::Union(_) = s.ast().data { - panic!("cannot derive on union") - } - - // ignore fields with #[type_visitable(ignore)] - s.filter(|bi| { - let mut ignored = false; - - bi.ast().attrs.iter().for_each(|attr| { - if !attr.path().is_ident("type_visitable") { - return; - } - let _ = attr.parse_nested_meta(|nested| { - if nested.path.is_ident("ignore") { - ignored = true; - } - Ok(()) - }); - }); - - !ignored - }); - - if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { - s.add_impl_generic(parse_quote! { 'tcx }); - } - - s.add_bounds(synstructure::AddBounds::Generics); - let body_visit = s.each(|bind| { - quote! { - ::rustc_middle::ty::visit::TypeVisitable::visit_with(#bind, __visitor)?; - } - }); - s.bind_with(|_| synstructure::BindStyle::Move); - - s.bound_impl( - quote!(::rustc_middle::ty::visit::TypeVisitable<::rustc_middle::ty::TyCtxt<'tcx>>), - quote! { - fn visit_with<__V: ::rustc_middle::ty::visit::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>( - &self, - __visitor: &mut __V - ) -> ::std::ops::ControlFlow<__V::BreakTy> { - match *self { #body_visit } - ::std::ops::ControlFlow::Continue(()) - } - }, - ) -} diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index d609965f36fe6..06c63ceb876a8 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -401,14 +401,16 @@ pub enum ClosureOutlivesSubject<'tcx> { /// This abstraction is necessary because the type may include `ReVar` regions, /// which is what we use internally within NLL code, and they can't be used in /// a query response. -/// -/// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this -/// type is not recognized as a binder for late-bound region. #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct ClosureOutlivesSubjectTy<'tcx> { inner: Ty<'tcx>, } +/// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this +/// type is not recognized as a binder for late-bound region. +impl<'tcx, I> !ty::TypeFoldable for ClosureOutlivesSubjectTy<'tcx> {} +impl<'tcx, I> !ty::TypeVisitable for ClosureOutlivesSubjectTy<'tcx> {} + impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { /// All regions of `ty` must be of kind `ReVar` and must represent /// universal regions *external* to the closure. diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 27a1e64a78bd5..f2b2090ba4204 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -1,13 +1,10 @@ -use std::ops::ControlFlow; - use rustc_data_structures::intern::Interned; use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints}; use crate::traits::query::NoSolution; use crate::traits::{Canonical, DefiningAnchor}; use crate::ty::{ - self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, - TypeVisitor, + self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitor, }; use rustc_span::def_id::DefId; @@ -110,7 +107,7 @@ pub struct QueryInput<'tcx, T> { } /// Additional constraints returned on success. -#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)] pub struct PredefinedOpaquesData<'tcx> { pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, } @@ -167,21 +164,7 @@ impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { self, folder: &mut F, ) -> Result { - Ok(FallibleTypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData { - region_constraints: self.region_constraints.clone().try_fold_with(folder)?, - opaque_types: self - .opaque_types - .iter() - .map(|opaque| opaque.try_fold_with(folder)) - .collect::>()?, - })) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - TypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData { - region_constraints: self.region_constraints.clone().fold_with(folder), - opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), - }) + Ok(folder.interner().mk_external_constraints((*self).clone().try_fold_with(folder)?)) } } @@ -190,9 +173,7 @@ impl<'tcx> TypeVisitable> for ExternalConstraints<'tcx> { &self, visitor: &mut V, ) -> std::ops::ControlFlow { - self.region_constraints.visit_with(visitor)?; - self.opaque_types.visit_with(visitor)?; - ControlFlow::Continue(()) + (**self).visit_with(visitor) } } @@ -206,21 +187,7 @@ impl<'tcx> TypeFoldable> for PredefinedOpaques<'tcx> { self, folder: &mut F, ) -> Result { - Ok(FallibleTypeFolder::interner(folder).mk_predefined_opaques_in_body( - PredefinedOpaquesData { - opaque_types: self - .opaque_types - .iter() - .map(|opaque| opaque.try_fold_with(folder)) - .collect::>()?, - }, - )) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - TypeFolder::interner(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData { - opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), - }) + Ok(folder.interner().mk_predefined_opaques_in_body((*self).clone().try_fold_with(folder)?)) } } @@ -229,7 +196,7 @@ impl<'tcx> TypeVisitable> for PredefinedOpaques<'tcx> { &self, visitor: &mut V, ) -> std::ops::ControlFlow { - self.opaque_types.visit_with(visitor) + (**self).visit_with(visitor) } } diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index c9607b2245a71..235675d55f79c 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -47,6 +47,8 @@ const REGION_TAG: usize = 0b01; const CONST_TAG: usize = 0b10; #[derive(Debug, TyEncodable, TyDecodable, PartialEq, Eq, PartialOrd, Ord, HashStable)] +#[derive(Clone, TypeFoldable, TypeVisitable)] +#[inline_traversals] pub enum GenericArgKind<'tcx> { Lifetime(ty::Region<'tcx>), Type(Ty<'tcx>), @@ -210,21 +212,14 @@ impl<'tcx> TypeFoldable> for GenericArg<'tcx> { self, folder: &mut F, ) -> Result { - match self.unpack() { - GenericArgKind::Lifetime(lt) => lt.try_fold_with(folder).map(Into::into), - GenericArgKind::Type(ty) => ty.try_fold_with(folder).map(Into::into), - GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), - } + self.unpack().try_fold_with(folder).map(GenericArgKind::pack) } } impl<'tcx> TypeVisitable> for GenericArg<'tcx> { + #[inline] fn visit_with>>(&self, visitor: &mut V) -> ControlFlow { - match self.unpack() { - GenericArgKind::Lifetime(lt) => lt.visit_with(visitor), - GenericArgKind::Type(ty) => ty.visit_with(visitor), - GenericArgKind::Const(ct) => ct.visit_with(visitor), - } + self.unpack().visit_with(visitor) } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 6af68bc5dbabb..9dc70a0df0dd9 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -829,15 +829,6 @@ impl<'tcx> TypeVisitable> for InferConst { } } -impl<'tcx> TypeSuperVisitable> for ty::UnevaluatedConst<'tcx> { - fn super_visit_with>>( - &self, - visitor: &mut V, - ) -> ControlFlow { - self.args.visit_with(visitor) - } -} - impl<'tcx> TypeVisitable> for TyAndLayout<'tcx, Ty<'tcx>> { fn visit_with>>(&self, visitor: &mut V) -> ControlFlow { visitor.visit_ty(self.ty)