1+ use indexmap:: IndexSet ;
12use quote:: { ToTokens , quote} ;
23use syn:: visit_mut:: VisitMut ;
34use syn:: { Attribute , parse_quote} ;
@@ -17,11 +18,24 @@ decl_derive!(
1718 [ GenericTypeVisitable ] => customizable_type_visitable_derive
1819) ;
1920
20- struct LiftedTy {
21+ struct TransformedTy {
2122 ty : syn:: Type ,
22- generic_parameter_bounds : Vec < syn:: Ident > ,
23+ generic_parameter_bounds : IndexSet < syn:: Ident > ,
2324}
2425
26+ enum TypeParameterPath {
27+ Interner ,
28+ GenericParameter ( syn:: Ident ) ,
29+ }
30+
31+ enum TypeParameterTransform {
32+ Continue ,
33+ Stop ,
34+ }
35+
36+ type TypeParameterVisitor =
37+ fn ( TypeParameterPath , & mut syn:: TypePath , & mut IndexSet < syn:: Ident > ) -> TypeParameterTransform ;
38+
2539fn has_ignore_attr ( attrs : & [ Attribute ] , name : & ' static str , meta : & ' static str ) -> bool {
2640 let mut ignored = false ;
2741 attrs. iter ( ) . for_each ( |attr| {
@@ -91,6 +105,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
91105
92106 s. add_where_predicate ( parse_quote ! { I : Interner } ) ;
93107 s. add_bounds ( synstructure:: AddBounds :: Fields ) ;
108+ let generic_parameters =
109+ s. ast ( ) . generics . type_params ( ) . map ( |ty| ty. ident . clone ( ) ) . collect :: < Vec < _ > > ( ) ;
110+ let mut generic_parameter_bounds = IndexSet :: new ( ) ;
94111 s. bind_with ( |_| synstructure:: BindStyle :: Move ) ;
95112 let body_try_fold = s. each_variant ( |vi| {
96113 let bindings = vi. bindings ( ) ;
@@ -101,6 +118,12 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
101118 if has_ignore_attr ( & bind. ast ( ) . attrs , "type_foldable" , "identity" ) {
102119 bind. to_token_stream ( )
103120 } else {
121+ for param in
122+ type_foldable_generic_parameters ( bind. ast ( ) . ty . clone ( ) , & generic_parameters)
123+ {
124+ generic_parameter_bounds. insert ( param) ;
125+ }
126+
104127 quote ! {
105128 :: rustc_type_ir:: TypeFoldable :: try_fold_with( #bind, __folder) ?
106129 }
@@ -129,6 +152,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
129152 // to generate code for them.
130153 s. filter ( |bi| !has_ignore_attr ( & bi. ast ( ) . attrs , "type_foldable" , "identity" ) ) ;
131154 s. add_bounds ( synstructure:: AddBounds :: Fields ) ;
155+ for param in generic_parameter_bounds {
156+ s. add_where_predicate ( parse_quote ! { #param: :: rustc_type_ir:: TypeFoldable <I > } ) ;
157+ }
132158 s. bound_impl (
133159 quote ! ( :: rustc_type_ir:: TypeFoldable <I >) ,
134160 quote ! {
@@ -149,6 +175,19 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
149175 )
150176}
151177
178+ fn type_foldable_generic_parameters (
179+ ty : syn:: Type ,
180+ generic_parameters : & [ syn:: Ident ] ,
181+ ) -> IndexSet < syn:: Ident > {
182+ transform_type_parameters ( ty, generic_parameters, |path, _, generic_parameter_bounds| {
183+ if let TypeParameterPath :: GenericParameter ( param) = path {
184+ generic_parameter_bounds. insert ( param) ;
185+ }
186+ TypeParameterTransform :: Continue
187+ } )
188+ . generic_parameter_bounds
189+ }
190+
152191/// `Lift_Generic` is specialised for structs/enums parameterised by an interner
153192/// `I: Interner`. It derives `Lift<J>` by rewriting interner associated types
154193/// from `I::Assoc` to `J::Assoc`. The required associated type lift bounds are
@@ -251,40 +290,72 @@ fn is_type_phantom(ty: &syn::Type) -> bool {
251290 get_first_path_segment ( ty) . is_some_and ( |segment| segment. ident == "PhantomData" )
252291}
253292
254- fn lift ( mut ty : syn:: Type , generic_parameters : & [ syn:: Ident ] ) -> LiftedTy {
255- struct ItoJ < ' a > {
293+ fn lift ( ty : syn:: Type , generic_parameters : & [ syn:: Ident ] ) -> TransformedTy {
294+ transform_type_parameters ( ty, generic_parameters, |path, ty, generic_parameter_bounds| {
295+ match path {
296+ TypeParameterPath :: Interner => {
297+ * ty. path . segments . first_mut ( ) . unwrap ( ) = parse_quote ! { J } ;
298+ TypeParameterTransform :: Continue
299+ }
300+ TypeParameterPath :: GenericParameter ( param) => {
301+ generic_parameter_bounds. insert ( param. clone ( ) ) ;
302+ * ty = parse_quote ! { <#param as :: rustc_type_ir:: lift:: Lift <J >>:: Lifted } ;
303+ TypeParameterTransform :: Stop
304+ }
305+ }
306+ } )
307+ }
308+
309+ fn transform_type_parameters (
310+ mut ty : syn:: Type ,
311+ generic_parameters : & [ syn:: Ident ] ,
312+ visit : TypeParameterVisitor ,
313+ ) -> TransformedTy {
314+ struct TypeParameterTransformer < ' a > {
256315 generic_parameters : & ' a [ syn:: Ident ] ,
257- generic_parameter_bounds : Vec < syn:: Ident > ,
316+ generic_parameter_bounds : IndexSet < syn:: Ident > ,
317+ visit : TypeParameterVisitor ,
258318 }
259319
260- impl VisitMut for ItoJ < ' _ > {
320+ impl VisitMut for TypeParameterTransformer < ' _ > {
261321 fn visit_type_path_mut ( & mut self , i : & mut syn:: TypePath ) {
262- if i. qself . is_none ( ) {
322+ let path = if i. qself . is_none ( ) {
263323 let segments_len = i. path . segments . len ( ) ;
264- if let Some ( first) = i. path . segments . first_mut ( ) {
265- // Turn paths from `I` into `J`
324+ i. path . segments . first ( ) . and_then ( |first| {
266325 if first. ident == "I" {
267- * first = parse_quote ! { J } ;
326+ Some ( TypeParameterPath :: Interner )
268327 } else if segments_len == 1
269328 && matches ! ( first. arguments, syn:: PathArguments :: None )
270- && self . generic_parameters . iter ( ) . any ( |param| first. ident == * param )
329+ && self . generic_parameters . contains ( & first. ident )
271330 {
272- let ident = first. ident . clone ( ) ;
273- if !self . generic_parameter_bounds . iter ( ) . any ( |param| * param == ident) {
274- self . generic_parameter_bounds . push ( ident. clone ( ) ) ;
275- }
276-
277- * i = parse_quote ! { <#ident as :: rustc_type_ir:: lift:: Lift <J >>:: Lifted } ;
278- return ;
331+ Some ( TypeParameterPath :: GenericParameter ( first. ident . clone ( ) ) )
332+ } else {
333+ None
279334 }
335+ } )
336+ } else {
337+ None
338+ } ;
339+
340+ if let Some ( path) = path {
341+ if let TypeParameterTransform :: Stop =
342+ ( self . visit ) ( path, i, & mut self . generic_parameter_bounds )
343+ {
344+ return ;
280345 }
281346 }
347+
282348 syn:: visit_mut:: visit_type_path_mut ( self , i) ;
283349 }
284350 }
285- let mut visitor = ItoJ { generic_parameters, generic_parameter_bounds : Vec :: new ( ) } ;
351+
352+ let mut visitor = TypeParameterTransformer {
353+ generic_parameters,
354+ generic_parameter_bounds : IndexSet :: new ( ) ,
355+ visit,
356+ } ;
286357 visitor. visit_type_mut ( & mut ty) ;
287- LiftedTy { ty, generic_parameter_bounds : visitor. generic_parameter_bounds }
358+ TransformedTy { ty, generic_parameter_bounds : visitor. generic_parameter_bounds }
288359}
289360
290361#[ cfg( not( feature = "nightly" ) ) ]
0 commit comments