Skip to content

TypeFoldable/TypeVisitable tidy up #117896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 51 additions & 15 deletions compiler/rustc_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// is not required to implement `TypeFoldable`). However use of this attribute is dangerous
/// is not required to implement `TypeFoldable`). 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<TyCtxt<'tcx>> for Foo<'a>`
///
/// and
///
/// `impl<'tcx> TypeFoldable<TyCtxt<'tcx>> 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::<traversable::Foldable>
);
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// (and its type is not required to implement `TypeVisitable`). However use of this attribute
/// (and its type is not required to implement `TypeVisitable`). 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<TyCtxt<'tcx>> for Foo<'a>`
///
/// and
///
/// `impl<'tcx> TypeVisitable<TyCtxt<'tcx>> 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::<traversable::Visitable>
);
decl_derive!([Lift, attributes(lift)] => lift::lift_derive);
decl_derive!(
Expand Down
135 changes: 135 additions & 0 deletions compiler/rustc_macros/src/traversable.rs
Original file line number Diff line number Diff line change
@@ -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<Self, __F::Error> {
::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<T: Traversable>(
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))
}
56 changes: 0 additions & 56 deletions compiler/rustc_macros/src/type_foldable.rs

This file was deleted.

52 changes: 0 additions & 52 deletions compiler/rustc_macros/src/type_visitable.rs

This file was deleted.

8 changes: 5 additions & 3 deletions compiler/rustc_middle/src/mir/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<I> for ClosureOutlivesSubjectTy<'tcx> {}
impl<'tcx, I> !ty::TypeVisitable<I> 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.
Expand Down
Loading