@@ -24,6 +24,7 @@ use middle::region;
24
24
use middle:: subst;
25
25
use middle:: ty;
26
26
use std:: fmt;
27
+ use std:: mem:: replace;
27
28
use syntax:: ast;
28
29
use syntax:: codemap:: Span ;
29
30
use syntax:: parse:: token:: special_idents;
@@ -70,6 +71,9 @@ struct LifetimeContext<'a> {
70
71
71
72
// I'm sorry.
72
73
trait_ref_hack : bool ,
74
+
75
+ // List of labels in the function/method currently under analysis.
76
+ labels_in_fn : Vec < ( ast:: Ident , Span ) > ,
73
77
}
74
78
75
79
enum ScopeChain < ' a > {
@@ -97,13 +101,18 @@ pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegio
97
101
scope : & ROOT_SCOPE ,
98
102
def_map : def_map,
99
103
trait_ref_hack : false ,
104
+ labels_in_fn : vec ! [ ] ,
100
105
} , krate) ;
101
106
sess. abort_if_errors ( ) ;
102
107
named_region_map
103
108
}
104
109
105
110
impl < ' a , ' v > Visitor < ' v > for LifetimeContext < ' a > {
106
111
fn visit_item ( & mut self , item : & ast:: Item ) {
112
+ // Items save/restore the set of labels. This way innner items
113
+ // can freely reuse names, be they loop labels or lifetimes.
114
+ let saved = replace ( & mut self . labels_in_fn , vec ! [ ] ) ;
115
+
107
116
// Items always introduce a new root scope
108
117
self . with ( RootScope , |_, this| {
109
118
match item. node {
@@ -137,23 +146,26 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
137
146
}
138
147
}
139
148
} ) ;
149
+
150
+ // Done traversing the item; restore saved set of labels.
151
+ replace ( & mut self . labels_in_fn , saved) ;
140
152
}
141
153
142
154
fn visit_fn ( & mut self , fk : visit:: FnKind < ' v > , fd : & ' v ast:: FnDecl ,
143
155
b : & ' v ast:: Block , s : Span , _: ast:: NodeId ) {
144
156
match fk {
145
157
visit:: FkItemFn ( _, generics, _, _) => {
146
158
self . visit_early_late ( subst:: FnSpace , generics, |this| {
147
- visit :: walk_fn ( this , fk, fd, b, s)
159
+ this . walk_fn ( fk, fd, b, s)
148
160
} )
149
161
}
150
162
visit:: FkMethod ( _, sig) => {
151
163
self . visit_early_late ( subst:: FnSpace , & sig. generics , |this| {
152
- visit :: walk_fn ( this , fk, fd, b, s)
164
+ this . walk_fn ( fk, fd, b, s)
153
165
} )
154
166
}
155
167
visit:: FkFnBlock ( ..) => {
156
- visit :: walk_fn ( self , fk, fd, b, s)
168
+ self . walk_fn ( fk, fd, b, s)
157
169
}
158
170
}
159
171
}
@@ -190,13 +202,19 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
190
202
}
191
203
192
204
fn visit_trait_item ( & mut self , trait_item : & ast:: TraitItem ) {
205
+ // We reset the labels on every trait item, so that different
206
+ // methods in an impl can reuse label names.
207
+ let saved = replace ( & mut self . labels_in_fn , vec ! [ ] ) ;
208
+
193
209
if let ast:: MethodTraitItem ( ref sig, None ) = trait_item. node {
194
210
self . visit_early_late (
195
211
subst:: FnSpace , & sig. generics ,
196
212
|this| visit:: walk_trait_item ( this, trait_item) )
197
213
} else {
198
214
visit:: walk_trait_item ( self , trait_item) ;
199
215
}
216
+
217
+ replace ( & mut self . labels_in_fn , saved) ;
200
218
}
201
219
202
220
fn visit_block ( & mut self , b : & ast:: Block ) {
@@ -286,7 +304,159 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
286
304
}
287
305
}
288
306
307
+ enum ShadowKind { Label , Lifetime }
308
+ struct Original { kind : ShadowKind , span : Span }
309
+ struct Shadower { kind : ShadowKind , span : Span }
310
+
311
+ fn original_label ( span : Span ) -> Original {
312
+ Original { kind : ShadowKind :: Label , span : span }
313
+ }
314
+ fn shadower_label ( span : Span ) -> Shadower {
315
+ Shadower { kind : ShadowKind :: Label , span : span }
316
+ }
317
+ fn original_lifetime ( l : & ast:: Lifetime ) -> Original {
318
+ Original { kind : ShadowKind :: Lifetime , span : l. span }
319
+ }
320
+ fn shadower_lifetime ( l : & ast:: Lifetime ) -> Shadower {
321
+ Shadower { kind : ShadowKind :: Lifetime , span : l. span }
322
+ }
323
+
324
+ impl ShadowKind {
325
+ fn desc ( & self ) -> & ' static str {
326
+ match * self {
327
+ ShadowKind :: Label => "label" ,
328
+ ShadowKind :: Lifetime => "lifetime" ,
329
+ }
330
+ }
331
+ }
332
+
333
+ fn signal_shadowing_error (
334
+ sess : & Session , name : ast:: Name , orig : Original , shadower : Shadower ) {
335
+ sess. span_err ( shadower. span ,
336
+ & format ! ( "{} name `{}` shadows a \
337
+ {} name that is already in scope",
338
+ shadower. kind. desc( ) , name, orig. kind. desc( ) ) ) ;
339
+ sess. span_note ( orig. span ,
340
+ & format ! ( "shadowed {} `{}` declared here" ,
341
+ orig. kind. desc( ) , name) ) ;
342
+ }
343
+
344
+ // Adds all labels in `b` to `ctxt.labels_in_fn`, signalling an error
345
+ // if one of the label shadows a lifetime or another label.
346
+ fn extract_labels < ' v , ' a > ( ctxt : & mut LifetimeContext < ' a > , b : & ' v ast:: Block ) {
347
+
348
+ struct GatherLabels < ' a > {
349
+ sess : & ' a Session ,
350
+ scope : Scope < ' a > ,
351
+ labels_in_fn : & ' a mut Vec < ( ast:: Ident , Span ) > ,
352
+ }
353
+
354
+ let mut gather = GatherLabels {
355
+ sess : ctxt. sess ,
356
+ scope : ctxt. scope ,
357
+ labels_in_fn : & mut ctxt. labels_in_fn ,
358
+ } ;
359
+ gather. visit_block ( b) ;
360
+ return ;
361
+
362
+ impl < ' v , ' a > Visitor < ' v > for GatherLabels < ' a > {
363
+ fn visit_expr ( & mut self , ex : & ' v ast:: Expr ) {
364
+ if let Some ( label) = expression_label ( ex) {
365
+ for & ( prior, prior_span) in & self . labels_in_fn [ ..] {
366
+ // FIXME (#24278): non-hygienic comparision
367
+ if label. name == prior. name {
368
+ signal_shadowing_error ( self . sess ,
369
+ label. name ,
370
+ original_label ( prior_span) ,
371
+ shadower_label ( ex. span ) ) ;
372
+ }
373
+ }
374
+
375
+ check_if_label_shadows_lifetime ( self . sess ,
376
+ self . scope ,
377
+ label,
378
+ ex. span ) ;
379
+
380
+ self . labels_in_fn . push ( ( label, ex. span ) ) ;
381
+ }
382
+ visit:: walk_expr ( self , ex)
383
+ }
384
+
385
+ fn visit_item ( & mut self , _: & ast:: Item ) {
386
+ // do not recurse into items defined in the block
387
+ }
388
+ }
389
+
390
+ fn expression_label ( ex : & ast:: Expr ) -> Option < ast:: Ident > {
391
+ match ex. node {
392
+ ast:: ExprWhile ( _, _, Some ( label) ) |
393
+ ast:: ExprWhileLet ( _, _, _, Some ( label) ) |
394
+ ast:: ExprForLoop ( _, _, _, Some ( label) ) |
395
+ ast:: ExprLoop ( _, Some ( label) ) => Some ( label) ,
396
+ _ => None ,
397
+ }
398
+ }
399
+
400
+ fn check_if_label_shadows_lifetime < ' a > ( sess : & ' a Session ,
401
+ mut scope : Scope < ' a > ,
402
+ label : ast:: Ident ,
403
+ label_span : Span ) {
404
+ loop {
405
+ match * scope {
406
+ BlockScope ( _, s) => { scope = s; }
407
+ RootScope => { return ; }
408
+
409
+ EarlyScope ( _, lifetimes, s) |
410
+ LateScope ( lifetimes, s) => {
411
+ for lifetime_def in lifetimes {
412
+ // FIXME (#24278): non-hygienic comparision
413
+ if label. name == lifetime_def. lifetime . name {
414
+ signal_shadowing_error (
415
+ sess,
416
+ label. name ,
417
+ original_lifetime ( & lifetime_def. lifetime ) ,
418
+ shadower_label ( label_span) ) ;
419
+ return ;
420
+ }
421
+ }
422
+ scope = s;
423
+ }
424
+ }
425
+ }
426
+ }
427
+ }
428
+
289
429
impl < ' a > LifetimeContext < ' a > {
430
+ // This is just like visit::walk_fn, except that it extracts the
431
+ // labels of the function body and swaps them in before visiting
432
+ // the function body itself.
433
+ fn walk_fn < ' b > ( & mut self ,
434
+ fk : visit:: FnKind ,
435
+ fd : & ast:: FnDecl ,
436
+ fb : & ' b ast:: Block ,
437
+ _span : Span ) {
438
+ match fk {
439
+ visit:: FkItemFn ( _, generics, _, _) => {
440
+ visit:: walk_fn_decl ( self , fd) ;
441
+ self . visit_generics ( generics) ;
442
+ }
443
+ visit:: FkMethod ( _, sig) => {
444
+ visit:: walk_fn_decl ( self , fd) ;
445
+ self . visit_generics ( & sig. generics ) ;
446
+ self . visit_explicit_self ( & sig. explicit_self ) ;
447
+ }
448
+ visit:: FkFnBlock ( ..) => {
449
+ visit:: walk_fn_decl ( self , fd) ;
450
+ }
451
+ }
452
+
453
+ // After inpsecting the decl, add all labels from the body to
454
+ // `self.labels_in_fn`.
455
+ extract_labels ( self , fb) ;
456
+
457
+ self . visit_block ( fb) ;
458
+ }
459
+
290
460
fn with < F > ( & mut self , wrap_scope : ScopeChain , f : F ) where
291
461
F : FnOnce ( Scope , & mut LifetimeContext ) ,
292
462
{
@@ -297,6 +467,7 @@ impl<'a> LifetimeContext<'a> {
297
467
scope : & wrap_scope,
298
468
def_map : self . def_map ,
299
469
trait_ref_hack : self . trait_ref_hack ,
470
+ labels_in_fn : self . labels_in_fn . clone ( ) ,
300
471
} ;
301
472
debug ! ( "entering scope {:?}" , this. scope) ;
302
473
f ( self . scope , & mut this) ;
@@ -494,6 +665,17 @@ impl<'a> LifetimeContext<'a> {
494
665
mut old_scope : Scope ,
495
666
lifetime : & ast:: Lifetime )
496
667
{
668
+ for & ( label, label_span) in & self . labels_in_fn {
669
+ // FIXME (#24278): non-hygienic comparision
670
+ if lifetime. name == label. name {
671
+ signal_shadowing_error ( self . sess ,
672
+ lifetime. name ,
673
+ original_label ( label_span) ,
674
+ shadower_lifetime ( & lifetime) ) ;
675
+ return ;
676
+ }
677
+ }
678
+
497
679
loop {
498
680
match * old_scope {
499
681
BlockScope ( _, s) => {
@@ -507,15 +689,11 @@ impl<'a> LifetimeContext<'a> {
507
689
EarlyScope ( _, lifetimes, s) |
508
690
LateScope ( lifetimes, s) => {
509
691
if let Some ( ( _, lifetime_def) ) = search_lifetimes ( lifetimes, lifetime) {
510
- self . sess . span_err (
511
- lifetime. span ,
512
- & format ! ( "lifetime name `{}` shadows another \
513
- lifetime name that is already in scope",
514
- token:: get_name( lifetime. name) ) ) ;
515
- self . sess . span_note (
516
- lifetime_def. span ,
517
- & format ! ( "shadowed lifetime `{}` declared here" ,
518
- token:: get_name( lifetime. name) ) ) ;
692
+ signal_shadowing_error (
693
+ self . sess ,
694
+ lifetime. name ,
695
+ original_lifetime ( & lifetime_def) ,
696
+ shadower_lifetime ( & lifetime) ) ;
519
697
return ;
520
698
}
521
699
0 commit comments