Skip to content

Commit b57fd59

Browse files
committed
Auto merge of #117896 - eggyal:traversables-1-tidy-up, r=<try>
TypeFoldable/TypeVisitable tidy up I'm breaking #117726 into more manageable, logical chunks. This is the first of those, and just makes some minor/tidying refactors in preparation for the chunks to follow. r? types
2 parents 38eecca + 35e3019 commit b57fd59

File tree

8 files changed

+202
-184
lines changed

8 files changed

+202
-184
lines changed

compiler/rustc_macros/src/lib.rs

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ mod lift;
2222
mod query;
2323
mod serialize;
2424
mod symbols;
25-
mod type_foldable;
26-
mod type_visitable;
25+
mod traversable;
2726

2827
// Reads the rust version (e.g. "1.75.0") from the CFG_RELEASE env var and
2928
// produces a `RustcVersion` literal containing that version (e.g.
@@ -63,25 +62,62 @@ decl_derive!([TyEncodable] => serialize::type_encodable_derive);
6362
decl_derive!([MetadataDecodable] => serialize::meta_decodable_derive);
6463
decl_derive!([MetadataEncodable] => serialize::meta_encodable_derive);
6564
decl_derive!(
66-
[TypeFoldable, attributes(type_foldable)] =>
65+
[TypeFoldable, attributes(type_foldable, inline_traversals)] =>
6766
/// Derives `TypeFoldable` for the annotated `struct` or `enum` (`union` is not supported).
6867
///
69-
/// The fold will produce a value of the same struct or enum variant as the input, with
70-
/// each field respectively folded using the `TypeFoldable` implementation for its type.
71-
/// However, if a field of a struct or an enum variant is annotated with
72-
/// `#[type_foldable(identity)]` then that field will retain its incumbent value (and its
73-
/// type is not required to implement `TypeFoldable`).
74-
type_foldable::type_foldable_derive
68+
/// Folds will produce a value of the same struct or enum variant as the input, with each field
69+
/// respectively folded (in definition order) using the `TypeFoldable` implementation for its
70+
/// type. However, if a field of a struct or of an enum variant is annotated with
71+
/// `#[type_foldable(identity)]` then that field will retain its incumbent value (and its type
72+
/// is not required to implement `TypeFoldable`). However use of this attribute is dangerous
73+
/// and should be used with extreme caution: should the type of the annotated field contain
74+
/// (now or in the future) a type that is of interest to a folder, it will not get folded (which
75+
/// may result in unexpected, hard-to-track bugs that could result in unsoundness).
76+
///
77+
/// If the annotated item has a `'tcx` lifetime parameter, then that will be used as the
78+
/// lifetime for the type context/interner; otherwise the lifetime of the type context/interner
79+
/// will be unrelated to the annotated type. It therefore matters how any lifetime parameters of
80+
/// the annotated type are named. For example, deriving `TypeFoldable` for both `Foo<'a>` and
81+
/// `Bar<'tcx>` will respectively produce:
82+
///
83+
/// `impl<'a, 'tcx> TypeFoldable<TyCtxt<'tcx>> for Foo<'a>`
84+
///
85+
/// and
86+
///
87+
/// `impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Bar<'tcx>`
88+
///
89+
/// The annotated item may be decorated with an `#[inline_traversals]` attribute to cause the
90+
/// generated folding method to be marked `#[inline]`.
91+
traversable::traversable_derive::<traversable::Foldable>
7592
);
7693
decl_derive!(
77-
[TypeVisitable, attributes(type_visitable)] =>
94+
[TypeVisitable, attributes(type_visitable, inline_traversals)] =>
7895
/// Derives `TypeVisitable` for the annotated `struct` or `enum` (`union` is not supported).
7996
///
80-
/// Each field of the struct or enum variant will be visited in definition order, using the
81-
/// `TypeVisitable` implementation for its type. However, if a field of a struct or an enum
82-
/// variant is annotated with `#[type_visitable(ignore)]` then that field will not be
83-
/// visited (and its type is not required to implement `TypeVisitable`).
84-
type_visitable::type_visitable_derive
97+
/// Each field of the struct or enum variant will be visited (in definition order) using the
98+
/// `TypeVisitable` implementation for its type. However, if a field of a struct or of an enum
99+
/// variant is annotated with `#[type_visitable(ignore)]` then that field will not be visited
100+
/// (and its type is not required to implement `TypeVisitable`). However use of this attribute
101+
/// is dangerous and should be used with extreme caution: should the type of the annotated
102+
/// field (now or in the future) a type that is of interest to a visitor, it will not get
103+
/// visited (which may result in unexpected, hard-to-track bugs that could result in
104+
/// unsoundness).
105+
///
106+
/// If the annotated item has a `'tcx` lifetime parameter, then that will be used as the
107+
/// lifetime for the type context/interner; otherwise the lifetime of the type context/interner
108+
/// will be unrelated to the annotated type. It therefore matters how any lifetime parameters of
109+
/// the annotated type are named. For example, deriving `TypeVisitable` for both `Foo<'a>` and
110+
/// `Bar<'tcx>` will respectively produce:
111+
///
112+
/// `impl<'a, 'tcx> TypeVisitable<TyCtxt<'tcx>> for Foo<'a>`
113+
///
114+
/// and
115+
///
116+
/// `impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for Bar<'tcx>`
117+
///
118+
/// The annotated item may be decorated with an `#[inline_traversals]` attribute to cause the
119+
/// generated folding method to be marked `#[inline]`.
120+
traversable::traversable_derive::<traversable::Visitable>
85121
);
86122
decl_derive!([Lift, attributes(lift)] => lift::lift_derive);
87123
decl_derive!(
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use proc_macro2::TokenStream;
2+
use quote::{quote, ToTokens};
3+
use syn::parse_quote;
4+
5+
pub struct Foldable;
6+
pub struct Visitable;
7+
8+
/// An abstraction over traversable traits.
9+
pub trait Traversable {
10+
/// The trait that this `Traversable` represents.
11+
fn traversable() -> TokenStream;
12+
13+
/// The `match` arms for a traversal of this type.
14+
fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream;
15+
16+
/// The body of an implementation given the match `arms`.
17+
fn impl_body(arms: impl ToTokens, attrs: impl ToTokens) -> TokenStream;
18+
}
19+
20+
impl Traversable for Foldable {
21+
fn traversable() -> TokenStream {
22+
quote! { ::rustc_middle::ty::fold::TypeFoldable<::rustc_middle::ty::TyCtxt<'tcx>> }
23+
}
24+
fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream {
25+
structure.each_variant(|vi| {
26+
let bindings = vi.bindings();
27+
vi.construct(|_, index| {
28+
let bind = &bindings[index];
29+
30+
let mut fixed = false;
31+
32+
// retain value of fields with #[type_foldable(identity)]
33+
bind.ast().attrs.iter().for_each(|x| {
34+
if !x.path().is_ident("type_foldable") {
35+
return;
36+
}
37+
let _ = x.parse_nested_meta(|nested| {
38+
if nested.path.is_ident("identity") {
39+
fixed = true;
40+
}
41+
Ok(())
42+
});
43+
});
44+
45+
if fixed {
46+
bind.to_token_stream()
47+
} else {
48+
quote! {
49+
::rustc_middle::ty::fold::TypeFoldable::try_fold_with(#bind, __folder)?
50+
}
51+
}
52+
})
53+
})
54+
}
55+
fn impl_body(arms: impl ToTokens, attrs: impl ToTokens) -> TokenStream {
56+
quote! {
57+
#attrs
58+
fn try_fold_with<__F: ::rustc_middle::ty::fold::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>(
59+
self,
60+
__folder: &mut __F
61+
) -> ::core::result::Result<Self, __F::Error> {
62+
::core::result::Result::Ok(match self { #arms })
63+
}
64+
}
65+
}
66+
}
67+
68+
impl Traversable for Visitable {
69+
fn traversable() -> TokenStream {
70+
quote! { ::rustc_middle::ty::visit::TypeVisitable<::rustc_middle::ty::TyCtxt<'tcx>> }
71+
}
72+
fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream {
73+
// ignore fields with #[type_visitable(ignore)]
74+
structure.filter(|bi| {
75+
let mut ignored = false;
76+
77+
bi.ast().attrs.iter().for_each(|attr| {
78+
if !attr.path().is_ident("type_visitable") {
79+
return;
80+
}
81+
let _ = attr.parse_nested_meta(|nested| {
82+
if nested.path.is_ident("ignore") {
83+
ignored = true;
84+
}
85+
Ok(())
86+
});
87+
});
88+
89+
!ignored
90+
});
91+
92+
structure.each(|bind| {
93+
quote! {
94+
::rustc_middle::ty::visit::TypeVisitable::visit_with(#bind, __visitor)?;
95+
}
96+
})
97+
}
98+
fn impl_body(arms: impl ToTokens, attrs: impl ToTokens) -> TokenStream {
99+
quote! {
100+
#attrs
101+
fn visit_with<__V: ::rustc_middle::ty::visit::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>(
102+
&self,
103+
__visitor: &mut __V
104+
) -> ::std::ops::ControlFlow<__V::BreakTy> {
105+
match self { #arms }
106+
::std::ops::ControlFlow::Continue(())
107+
}
108+
}
109+
}
110+
}
111+
112+
pub fn traversable_derive<T: Traversable>(
113+
mut structure: synstructure::Structure<'_>,
114+
) -> TokenStream {
115+
if let syn::Data::Union(_) = structure.ast().data {
116+
panic!("cannot derive on union")
117+
}
118+
119+
structure.add_bounds(synstructure::AddBounds::Generics);
120+
structure.bind_with(|_| synstructure::BindStyle::Move);
121+
122+
if !structure.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
123+
structure.add_impl_generic(parse_quote! { 'tcx });
124+
}
125+
126+
let arms = T::arms(&mut structure);
127+
let attrs = structure
128+
.ast()
129+
.attrs
130+
.iter()
131+
.any(|attr| attr.path().is_ident("inline_traversals"))
132+
.then_some(quote! { #[inline] });
133+
134+
structure.bound_impl(T::traversable(), T::impl_body(arms, attrs))
135+
}

compiler/rustc_macros/src/type_foldable.rs

Lines changed: 0 additions & 56 deletions
This file was deleted.

compiler/rustc_macros/src/type_visitable.rs

Lines changed: 0 additions & 52 deletions
This file was deleted.

compiler/rustc_middle/src/mir/query.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,14 +406,16 @@ pub enum ClosureOutlivesSubject<'tcx> {
406406
/// This abstraction is necessary because the type may include `ReVar` regions,
407407
/// which is what we use internally within NLL code, and they can't be used in
408408
/// a query response.
409-
///
410-
/// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this
411-
/// type is not recognized as a binder for late-bound region.
412409
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
413410
pub struct ClosureOutlivesSubjectTy<'tcx> {
414411
inner: Ty<'tcx>,
415412
}
416413

414+
/// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this
415+
/// type is not recognized as a binder for late-bound region.
416+
impl<'tcx, I> !ty::TypeFoldable<I> for ClosureOutlivesSubjectTy<'tcx> {}
417+
impl<'tcx, I> !ty::TypeVisitable<I> for ClosureOutlivesSubjectTy<'tcx> {}
418+
417419
impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
418420
/// All regions of `ty` must be of kind `ReVar` and must represent
419421
/// universal regions *external* to the closure.

0 commit comments

Comments
 (0)