Skip to content

Commit a5f77ae

Browse files
committed
Auto merge of rust-lang#117896 - eggyal:traversables-1-tidy-up, r=<try>
TypeFoldable/TypeVisitable tidy up I'm breaking rust-lang#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 fa14810 + dce79e4 commit a5f77ae

File tree

8 files changed

+183
-182
lines changed

8 files changed

+183
-182
lines changed

compiler/rustc_macros/src/lib.rs

+43-13
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ mod newtype;
2323
mod query;
2424
mod serialize;
2525
mod symbols;
26-
mod type_foldable;
27-
mod type_visitable;
26+
mod traversable;
2827

2928
// Reads the rust version (e.g. "1.75.0") from the CFG_RELEASE env var and
3029
// produces a `RustcVersion` literal containing that version (e.g.
@@ -81,22 +80,53 @@ decl_derive!(
8180
[TypeFoldable, attributes(type_foldable)] =>
8281
/// Derives `TypeFoldable` for the annotated `struct` or `enum` (`union` is not supported).
8382
///
84-
/// The fold will produce a value of the same struct or enum variant as the input, with
85-
/// each field respectively folded using the `TypeFoldable` implementation for its type.
86-
/// However, if a field of a struct or an enum variant is annotated with
87-
/// `#[type_foldable(identity)]` then that field will retain its incumbent value (and its
88-
/// type is not required to implement `TypeFoldable`).
89-
type_foldable::type_foldable_derive
83+
/// Folds will produce a value of the same struct or enum variant as the input, with each field
84+
/// respectively folded (in definition order) using the `TypeFoldable` implementation for its
85+
/// type. However, if a field of a struct or of an enum variant is annotated with
86+
/// `#[type_foldable(identity)]` then that field will retain its incumbent value (and its type
87+
/// is not required to implement `TypeFoldable`). However use of this attribute is dangerous
88+
/// and should be used with extreme caution: should the type of the annotated field contain
89+
/// (now or in the future) a type that is of interest to a folder, it will not get folded (which
90+
/// may result in unexpected, hard-to-track bugs that could result in unsoundness).
91+
///
92+
/// If the annotated item has a `'tcx` lifetime parameter, then that will be used as the
93+
/// lifetime for the type context/interner; otherwise the lifetime of the type context/interner
94+
/// will be unrelated to the annotated type. It therefore matters how any lifetime parameters of
95+
/// the annotated type are named. For example, deriving `TypeFoldable` for both `Foo<'a>` and
96+
/// `Bar<'tcx>` will respectively produce:
97+
///
98+
/// `impl<'a, 'tcx> TypeFoldable<TyCtxt<'tcx>> for Foo<'a>`
99+
///
100+
/// and
101+
///
102+
/// `impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Bar<'tcx>`
103+
traversable::traversable_derive::<traversable::Foldable>
90104
);
91105
decl_derive!(
92106
[TypeVisitable, attributes(type_visitable)] =>
93107
/// Derives `TypeVisitable` for the annotated `struct` or `enum` (`union` is not supported).
94108
///
95-
/// Each field of the struct or enum variant will be visited in definition order, using the
96-
/// `TypeVisitable` implementation for its type. However, if a field of a struct or an enum
97-
/// variant is annotated with `#[type_visitable(ignore)]` then that field will not be
98-
/// visited (and its type is not required to implement `TypeVisitable`).
99-
type_visitable::type_visitable_derive
109+
/// Each field of the struct or enum variant will be visited (in definition order) using the
110+
/// `TypeVisitable` implementation for its type. However, if a field of a struct or of an enum
111+
/// variant is annotated with `#[type_visitable(ignore)]` then that field will not be visited
112+
/// (and its type is not required to implement `TypeVisitable`). However use of this attribute
113+
/// is dangerous and should be used with extreme caution: should the type of the annotated
114+
/// field (now or in the future) a type that is of interest to a visitor, it will not get
115+
/// visited (which may result in unexpected, hard-to-track bugs that could result in
116+
/// unsoundness).
117+
///
118+
/// If the annotated item has a `'tcx` lifetime parameter, then that will be used as the
119+
/// lifetime for the type context/interner; otherwise the lifetime of the type context/interner
120+
/// will be unrelated to the annotated type. It therefore matters how any lifetime parameters of
121+
/// the annotated type are named. For example, deriving `TypeVisitable` for both `Foo<'a>` and
122+
/// `Bar<'tcx>` will respectively produce:
123+
///
124+
/// `impl<'a, 'tcx> TypeVisitable<TyCtxt<'tcx>> for Foo<'a>`
125+
///
126+
/// and
127+
///
128+
/// `impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for Bar<'tcx>`
129+
traversable::traversable_derive::<traversable::Visitable>
100130
);
101131
decl_derive!([Lift, attributes(lift)] => lift::lift_derive);
102132
decl_derive!(
+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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) -> 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) -> TokenStream {
56+
quote! {
57+
fn try_fold_with<__F: ::rustc_middle::ty::fold::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>(
58+
self,
59+
__folder: &mut __F
60+
) -> ::core::result::Result<Self, __F::Error> {
61+
::core::result::Result::Ok(match self { #arms })
62+
}
63+
}
64+
}
65+
}
66+
67+
impl Traversable for Visitable {
68+
fn traversable() -> TokenStream {
69+
quote! { ::rustc_middle::ty::visit::TypeVisitable<::rustc_middle::ty::TyCtxt<'tcx>> }
70+
}
71+
fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream {
72+
// ignore fields with #[type_visitable(ignore)]
73+
structure.filter(|bi| {
74+
let mut ignored = false;
75+
76+
bi.ast().attrs.iter().for_each(|attr| {
77+
if !attr.path().is_ident("type_visitable") {
78+
return;
79+
}
80+
let _ = attr.parse_nested_meta(|nested| {
81+
if nested.path.is_ident("ignore") {
82+
ignored = true;
83+
}
84+
Ok(())
85+
});
86+
});
87+
88+
!ignored
89+
});
90+
91+
structure.each(|bind| {
92+
quote! {
93+
::rustc_middle::ty::visit::TypeVisitable::visit_with(#bind, __visitor)?;
94+
}
95+
})
96+
}
97+
fn impl_body(arms: impl ToTokens) -> TokenStream {
98+
quote! {
99+
fn visit_with<__V: ::rustc_middle::ty::visit::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>(
100+
&self,
101+
__visitor: &mut __V
102+
) -> ::std::ops::ControlFlow<__V::BreakTy> {
103+
match self { #arms }
104+
::std::ops::ControlFlow::Continue(())
105+
}
106+
}
107+
}
108+
}
109+
110+
pub fn traversable_derive<T: Traversable>(
111+
mut structure: synstructure::Structure<'_>,
112+
) -> TokenStream {
113+
if let syn::Data::Union(_) = structure.ast().data {
114+
panic!("cannot derive on union")
115+
}
116+
117+
structure.add_bounds(synstructure::AddBounds::Generics);
118+
structure.bind_with(|_| synstructure::BindStyle::Move);
119+
120+
if !structure.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
121+
structure.add_impl_generic(parse_quote! { 'tcx });
122+
}
123+
124+
let arms = T::arms(&mut structure);
125+
structure.bound_impl(T::traversable(), T::impl_body(arms))
126+
}

compiler/rustc_macros/src/type_foldable.rs

-56
This file was deleted.

compiler/rustc_macros/src/type_visitable.rs

-52
This file was deleted.

compiler/rustc_middle/src/mir/query.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -401,14 +401,16 @@ pub enum ClosureOutlivesSubject<'tcx> {
401401
/// This abstraction is necessary because the type may include `ReVar` regions,
402402
/// which is what we use internally within NLL code, and they can't be used in
403403
/// a query response.
404-
///
405-
/// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this
406-
/// type is not recognized as a binder for late-bound region.
407404
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
408405
pub struct ClosureOutlivesSubjectTy<'tcx> {
409406
inner: Ty<'tcx>,
410407
}
411408

409+
/// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this
410+
/// type is not recognized as a binder for late-bound region.
411+
impl<'tcx, I> !ty::TypeFoldable<I> for ClosureOutlivesSubjectTy<'tcx> {}
412+
impl<'tcx, I> !ty::TypeVisitable<I> for ClosureOutlivesSubjectTy<'tcx> {}
413+
412414
impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
413415
/// All regions of `ty` must be of kind `ReVar` and must represent
414416
/// universal regions *external* to the closure.

0 commit comments

Comments
 (0)