@@ -24,6 +24,7 @@ use middle::region;
2424use middle:: subst;
2525use middle:: ty;
2626use std:: fmt;
27+ use std:: mem:: replace;
2728use syntax:: ast;
2829use syntax:: codemap:: Span ;
2930use syntax:: parse:: token:: special_idents;
@@ -70,6 +71,9 @@ struct LifetimeContext<'a> {
7071
7172 // I'm sorry.
7273 trait_ref_hack : bool ,
74+
75+ // List of labels in the function/method currently under analysis.
76+ labels_in_fn : Vec < ( ast:: Ident , Span ) > ,
7377}
7478
7579enum ScopeChain < ' a > {
@@ -97,13 +101,18 @@ pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegio
97101 scope : & ROOT_SCOPE ,
98102 def_map : def_map,
99103 trait_ref_hack : false ,
104+ labels_in_fn : vec ! [ ] ,
100105 } , krate) ;
101106 sess. abort_if_errors ( ) ;
102107 named_region_map
103108}
104109
105110impl < ' a , ' v > Visitor < ' v > for LifetimeContext < ' a > {
106111 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+
107116 // Items always introduce a new root scope
108117 self . with ( RootScope , |_, this| {
109118 match item. node {
@@ -137,23 +146,26 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
137146 }
138147 }
139148 } ) ;
149+
150+ // Done traversing the item; restore saved set of labels.
151+ replace ( & mut self . labels_in_fn , saved) ;
140152 }
141153
142154 fn visit_fn ( & mut self , fk : visit:: FnKind < ' v > , fd : & ' v ast:: FnDecl ,
143155 b : & ' v ast:: Block , s : Span , _: ast:: NodeId ) {
144156 match fk {
145157 visit:: FkItemFn ( _, generics, _, _) => {
146158 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)
148160 } )
149161 }
150162 visit:: FkMethod ( _, sig) => {
151163 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)
153165 } )
154166 }
155167 visit:: FkFnBlock ( ..) => {
156- visit :: walk_fn ( self , fk, fd, b, s)
168+ self . walk_fn ( fk, fd, b, s)
157169 }
158170 }
159171 }
@@ -190,13 +202,19 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
190202 }
191203
192204 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+
193209 if let ast:: MethodTraitItem ( ref sig, None ) = trait_item. node {
194210 self . visit_early_late (
195211 subst:: FnSpace , & sig. generics ,
196212 |this| visit:: walk_trait_item ( this, trait_item) )
197213 } else {
198214 visit:: walk_trait_item ( self , trait_item) ;
199215 }
216+
217+ replace ( & mut self . labels_in_fn , saved) ;
200218 }
201219
202220 fn visit_block ( & mut self , b : & ast:: Block ) {
@@ -286,7 +304,159 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
286304 }
287305}
288306
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+
289429impl < ' 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+
290460 fn with < F > ( & mut self , wrap_scope : ScopeChain , f : F ) where
291461 F : FnOnce ( Scope , & mut LifetimeContext ) ,
292462 {
@@ -297,6 +467,7 @@ impl<'a> LifetimeContext<'a> {
297467 scope : & wrap_scope,
298468 def_map : self . def_map ,
299469 trait_ref_hack : self . trait_ref_hack ,
470+ labels_in_fn : self . labels_in_fn . clone ( ) ,
300471 } ;
301472 debug ! ( "entering scope {:?}" , this. scope) ;
302473 f ( self . scope , & mut this) ;
@@ -494,6 +665,17 @@ impl<'a> LifetimeContext<'a> {
494665 mut old_scope : Scope ,
495666 lifetime : & ast:: Lifetime )
496667 {
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+
497679 loop {
498680 match * old_scope {
499681 BlockScope ( _, s) => {
@@ -507,15 +689,11 @@ impl<'a> LifetimeContext<'a> {
507689 EarlyScope ( _, lifetimes, s) |
508690 LateScope ( lifetimes, s) => {
509691 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) ) ;
519697 return ;
520698 }
521699
0 commit comments