@@ -138,6 +138,61 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
138
138
) ;
139
139
assert ! ( old_parent. is_none( ) , "parent `LocalDefId` is reset for an invocation" ) ;
140
140
}
141
+
142
+ /// Determines whether the const argument `AnonConst` is a simple macro call, optionally
143
+ /// surrounded with braces.
144
+ ///
145
+ /// If this const argument *is* a trivial macro call then the id for the macro call is
146
+ /// returned along with the information required to build the anon const's def if
147
+ /// the macro call expands to a non-trivial expression.
148
+ fn is_const_arg_trivial_macro_expansion (
149
+ & self ,
150
+ anon_const : & ' a AnonConst ,
151
+ ) -> Option < ( PendingAnonConstInfo , NodeId ) > {
152
+ let ( block_was_stripped, expr) = anon_const. value . maybe_unwrap_block ( ) ;
153
+ match expr {
154
+ Expr { kind : ExprKind :: MacCall ( ..) , id, .. } => Some ( (
155
+ PendingAnonConstInfo {
156
+ id : anon_const. id ,
157
+ span : anon_const. value . span ,
158
+ block_was_stripped,
159
+ } ,
160
+ * id,
161
+ ) ) ,
162
+ _ => None ,
163
+ }
164
+ }
165
+
166
+ /// Determines whether the expression `const_arg_sub_expr` is a simple macro call, sometimes
167
+ /// surrounded with braces if a set of braces has not already been entered. This is required
168
+ /// as `{ N }` is treated as equivalent to a bare parameter `N` whereas `{{ N }}` is treated as
169
+ /// a real block expression and is lowered to an anonymous constant which is not allowed to use
170
+ /// generic parameters.
171
+ ///
172
+ /// If this expression is a trivial macro call then the id for the macro call is
173
+ /// returned along with the information required to build the anon const's def if
174
+ /// the macro call expands to a non-trivial expression.
175
+ fn is_const_arg_sub_expr_trivial_macro_expansion (
176
+ & self ,
177
+ const_arg_sub_expr : & ' a Expr ,
178
+ ) -> Option < ( PendingAnonConstInfo , NodeId ) > {
179
+ let pending_anon = self . pending_anon_const_info . unwrap_or_else ( ||
180
+ panic ! ( "Checking expr is trivial macro call without having entered anon const: `{const_arg_sub_expr:?}`" ) ,
181
+ ) ;
182
+
183
+ let ( block_was_stripped, expr) = if pending_anon. block_was_stripped {
184
+ ( true , const_arg_sub_expr)
185
+ } else {
186
+ const_arg_sub_expr. maybe_unwrap_block ( )
187
+ } ;
188
+
189
+ match expr {
190
+ Expr { kind : ExprKind :: MacCall ( ..) , id, .. } => {
191
+ Some ( ( PendingAnonConstInfo { block_was_stripped, ..pending_anon } , * id) )
192
+ }
193
+ _ => None ,
194
+ }
195
+ }
141
196
}
142
197
143
198
impl < ' a , ' ra , ' tcx > visit:: Visitor < ' a > for DefCollector < ' a , ' ra , ' tcx > {
@@ -354,12 +409,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
354
409
// items will be messed up, but that's ok because there can't be any if we're just looking
355
410
// for bare idents.
356
411
357
- if matches ! ( constant . value . maybe_unwrap_block ( ) . kind , ExprKind :: MacCall ( .. ) ) {
358
- // See self.pending_anon_const_info for explanation
359
- self . pending_anon_const_info =
360
- Some ( PendingAnonConstInfo { id : constant . id , span : constant . value . span } ) ;
361
- return visit :: walk_anon_const ( self , constant ) ;
362
- } else if constant. value . is_potential_trivial_const_arg ( ) {
412
+ if let Some ( ( pending_anon , macro_invoc ) ) =
413
+ self . is_const_arg_trivial_macro_expansion ( constant )
414
+ {
415
+ self . pending_anon_const_info = Some ( pending_anon ) ;
416
+ return self . visit_macro_invoc ( macro_invoc ) ;
417
+ } else if constant. value . is_potential_trivial_const_arg ( true ) {
363
418
return visit:: walk_anon_const ( self , constant) ;
364
419
}
365
420
@@ -368,23 +423,36 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
368
423
}
369
424
370
425
fn visit_expr ( & mut self , expr : & ' a Expr ) {
371
- if matches ! ( expr. kind, ExprKind :: MacCall ( ..) ) {
372
- return self . visit_macro_invoc ( expr. id ) ;
426
+ // If we're visiting the expression of a const argument that was a macro call then
427
+ // check if it is *still* unknown whether it is a trivial const arg or not. If so
428
+ // recurse into the macro call and delay creating the anon const def until expansion.
429
+ if self . pending_anon_const_info . is_some ( )
430
+ && let Some ( ( pending_anon, macro_invoc) ) =
431
+ self . is_const_arg_sub_expr_trivial_macro_expansion ( expr)
432
+ {
433
+ self . pending_anon_const_info = Some ( pending_anon) ;
434
+ return self . visit_macro_invoc ( macro_invoc) ;
373
435
}
374
436
375
- let grandparent_def = if let Some ( pending_anon) = self . pending_anon_const_info . take ( ) {
376
- // See self.pending_anon_const_info for explanation
377
- if !expr. is_potential_trivial_const_arg ( ) {
437
+ // See self.pending_anon_const_info for explanation
438
+ let parent_def = self
439
+ . pending_anon_const_info
440
+ . take ( )
441
+ // If we already stripped away a set of braces then do not do it again when determining
442
+ // if the macro expanded to a trivial const arg. This arises in cases such as:
443
+ // `Foo<{ bar!() }>` where `bar!()` expands to `{ N }`. This should not be considered a
444
+ // trivial const argument even though `{ N }` by itself *is*.
445
+ . filter ( |pending_anon| {
446
+ !expr. is_potential_trivial_const_arg ( !pending_anon. block_was_stripped )
447
+ } )
448
+ . map ( |pending_anon| {
378
449
self . create_def ( pending_anon. id , kw:: Empty , DefKind :: AnonConst , pending_anon. span )
379
- } else {
380
- self . parent_def
381
- }
382
- } else {
383
- self . parent_def
384
- } ;
450
+ } )
451
+ . unwrap_or ( self . parent_def ) ;
385
452
386
- self . with_parent ( grandparent_def , |this| {
453
+ self . with_parent ( parent_def , |this| {
387
454
let parent_def = match expr. kind {
455
+ ExprKind :: MacCall ( ..) => return this. visit_macro_invoc ( expr. id ) ,
388
456
ExprKind :: Closure ( ..) | ExprKind :: Gen ( ..) => {
389
457
this. create_def ( expr. id , kw:: Empty , DefKind :: Closure , expr. span )
390
458
}
0 commit comments