1313
1414use ast_map;
1515use session:: Session ;
16- use middle:: def:: { DefStatic , DefConst , DefAssociatedConst , DefMap } ;
16+ use middle:: def:: { DefStatic , DefConst , DefAssociatedConst , DefVariant , DefMap } ;
17+ use util:: nodemap:: NodeMap ;
1718
1819use syntax:: { ast, ast_util} ;
1920use syntax:: codemap:: Span ;
2021use syntax:: visit:: Visitor ;
2122use syntax:: visit;
2223
24+ use std:: cell:: RefCell ;
25+
2326struct CheckCrateVisitor < ' a , ' ast : ' a > {
2427 sess : & ' a Session ,
2528 def_map : & ' a DefMap ,
26- ast_map : & ' a ast_map:: Map < ' ast >
29+ ast_map : & ' a ast_map:: Map < ' ast > ,
30+ discriminant_map : RefCell < NodeMap < Option < & ' ast ast:: Expr > > > ,
2731}
2832
29- impl < ' v , ' a , ' ast > Visitor < ' v > for CheckCrateVisitor < ' a , ' ast > {
30- fn visit_item ( & mut self , it : & ast:: Item ) {
33+ impl < ' a , ' ast : ' a > Visitor < ' ast > for CheckCrateVisitor < ' a , ' ast > {
34+ fn visit_item ( & mut self , it : & ' ast ast:: Item ) {
3135 match it. node {
32- ast:: ItemStatic ( _ , _ , ref expr ) |
33- ast:: ItemConst ( _ , ref expr ) => {
36+ ast:: ItemStatic ( .. ) |
37+ ast:: ItemConst ( .. ) => {
3438 let mut recursion_visitor =
3539 CheckItemRecursionVisitor :: new ( self , & it. span ) ;
3640 recursion_visitor. visit_item ( it) ;
37- visit:: walk_expr ( self , & * expr)
3841 } ,
39- _ => visit:: walk_item ( self , it)
42+ ast:: ItemEnum ( ref enum_def, ref generics) => {
43+ // We could process the whole enum, but handling the variants
44+ // with discriminant expressions one by one gives more specific,
45+ // less redundant output.
46+ for variant in & enum_def. variants {
47+ if let Some ( _) = variant. node . disr_expr {
48+ let mut recursion_visitor =
49+ CheckItemRecursionVisitor :: new ( self , & variant. span ) ;
50+ recursion_visitor. populate_enum_discriminants ( enum_def) ;
51+ recursion_visitor. visit_variant ( variant, generics) ;
52+ }
53+ }
54+ }
55+ _ => { }
4056 }
57+ visit:: walk_item ( self , it)
4158 }
4259
43- fn visit_trait_item ( & mut self , ti : & ast:: TraitItem ) {
60+ fn visit_trait_item ( & mut self , ti : & ' ast ast:: TraitItem ) {
4461 match ti. node {
4562 ast:: ConstTraitItem ( _, ref default) => {
46- if let Some ( ref expr ) = * default {
63+ if let Some ( _ ) = * default {
4764 let mut recursion_visitor =
4865 CheckItemRecursionVisitor :: new ( self , & ti. span ) ;
4966 recursion_visitor. visit_trait_item ( ti) ;
50- visit:: walk_expr ( self , & * expr)
5167 }
5268 }
53- _ => visit :: walk_trait_item ( self , ti )
69+ _ => { }
5470 }
71+ visit:: walk_trait_item ( self , ti)
5572 }
5673
57- fn visit_impl_item ( & mut self , ii : & ast:: ImplItem ) {
74+ fn visit_impl_item ( & mut self , ii : & ' ast ast:: ImplItem ) {
5875 match ii. node {
59- ast:: ConstImplItem ( _ , ref expr ) => {
76+ ast:: ConstImplItem ( .. ) => {
6077 let mut recursion_visitor =
6178 CheckItemRecursionVisitor :: new ( self , & ii. span ) ;
6279 recursion_visitor. visit_impl_item ( ii) ;
63- visit:: walk_expr ( self , & * expr)
6480 }
65- _ => visit :: walk_impl_item ( self , ii )
81+ _ => { }
6682 }
83+ visit:: walk_impl_item ( self , ii)
6784 }
6885}
6986
7087pub fn check_crate < ' ast > ( sess : & Session ,
71- krate : & ast:: Crate ,
88+ krate : & ' ast ast:: Crate ,
7289 def_map : & DefMap ,
7390 ast_map : & ast_map:: Map < ' ast > ) {
7491 let mut visitor = CheckCrateVisitor {
7592 sess : sess,
7693 def_map : def_map,
77- ast_map : ast_map
94+ ast_map : ast_map,
95+ discriminant_map : RefCell :: new ( NodeMap ( ) ) ,
7896 } ;
7997 visit:: walk_crate ( & mut visitor, krate) ;
8098 sess. abort_if_errors ( ) ;
@@ -85,53 +103,120 @@ struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
85103 sess : & ' a Session ,
86104 ast_map : & ' a ast_map:: Map < ' ast > ,
87105 def_map : & ' a DefMap ,
88- idstack : Vec < ast:: NodeId >
106+ discriminant_map : & ' a RefCell < NodeMap < Option < & ' ast ast:: Expr > > > ,
107+ idstack : Vec < ast:: NodeId > ,
89108}
90109
91110impl < ' a , ' ast : ' a > CheckItemRecursionVisitor < ' a , ' ast > {
92- fn new ( v : & CheckCrateVisitor < ' a , ' ast > , span : & ' a Span )
111+ fn new ( v : & ' a CheckCrateVisitor < ' a , ' ast > , span : & ' a Span )
93112 -> CheckItemRecursionVisitor < ' a , ' ast > {
94113 CheckItemRecursionVisitor {
95114 root_span : span,
96115 sess : v. sess ,
97116 ast_map : v. ast_map ,
98117 def_map : v. def_map ,
99- idstack : Vec :: new ( )
118+ discriminant_map : & v. discriminant_map ,
119+ idstack : Vec :: new ( ) ,
100120 }
101121 }
102122 fn with_item_id_pushed < F > ( & mut self , id : ast:: NodeId , f : F )
103123 where F : Fn ( & mut Self ) {
104- if self . idstack . iter ( ) . any ( |x| x == & ( id ) ) {
124+ if self . idstack . iter ( ) . any ( |x| * x == id ) {
105125 span_err ! ( self . sess, * self . root_span, E0265 , "recursive constant" ) ;
106126 return ;
107127 }
108128 self . idstack . push ( id) ;
109129 f ( self ) ;
110130 self . idstack . pop ( ) ;
111131 }
132+ // If a variant has an expression specifying its discriminant, then it needs
133+ // to be checked just like a static or constant. However, if there are more
134+ // variants with no explicitly specified discriminant, those variants will
135+ // increment the same expression to get their values.
136+ //
137+ // So for every variant, we need to track whether there is an expression
138+ // somewhere in the enum definition that controls its discriminant. We do
139+ // this by starting from the end and searching backward.
140+ fn populate_enum_discriminants ( & self , enum_definition : & ' ast ast:: EnumDef ) {
141+ // Get the map, and return if we already processed this enum or if it
142+ // has no variants.
143+ let mut discriminant_map = self . discriminant_map . borrow_mut ( ) ;
144+ match enum_definition. variants . first ( ) {
145+ None => { return ; }
146+ Some ( variant) if discriminant_map. contains_key ( & variant. node . id ) => {
147+ return ;
148+ }
149+ _ => { }
150+ }
151+
152+ // Go through all the variants.
153+ let mut variant_stack: Vec < ast:: NodeId > = Vec :: new ( ) ;
154+ for variant in enum_definition. variants . iter ( ) . rev ( ) {
155+ variant_stack. push ( variant. node . id ) ;
156+ // When we find an expression, every variant currently on the stack
157+ // is affected by that expression.
158+ if let Some ( ref expr) = variant. node . disr_expr {
159+ for id in & variant_stack {
160+ discriminant_map. insert ( * id, Some ( expr) ) ;
161+ }
162+ variant_stack. clear ( )
163+ }
164+ }
165+ // If we are at the top, that always starts at 0, so any variant on the
166+ // stack has a default value and does not need to be checked.
167+ for id in & variant_stack {
168+ discriminant_map. insert ( * id, None ) ;
169+ }
170+ }
112171}
113172
114- impl < ' a , ' ast , ' v > Visitor < ' v > for CheckItemRecursionVisitor < ' a , ' ast > {
115- fn visit_item ( & mut self , it : & ast:: Item ) {
173+ impl < ' a , ' ast : ' a > Visitor < ' ast > for CheckItemRecursionVisitor < ' a , ' ast > {
174+ fn visit_item ( & mut self , it : & ' ast ast:: Item ) {
116175 self . with_item_id_pushed ( it. id , |v| visit:: walk_item ( v, it) ) ;
117176 }
118177
119- fn visit_trait_item ( & mut self , ti : & ast:: TraitItem ) {
178+ fn visit_enum_def ( & mut self , enum_definition : & ' ast ast:: EnumDef ,
179+ generics : & ' ast ast:: Generics ) {
180+ self . populate_enum_discriminants ( enum_definition) ;
181+ visit:: walk_enum_def ( self , enum_definition, generics) ;
182+ }
183+
184+ fn visit_variant ( & mut self , variant : & ' ast ast:: Variant ,
185+ _: & ' ast ast:: Generics ) {
186+ let variant_id = variant. node . id ;
187+ let maybe_expr;
188+ if let Some ( get_expr) = self . discriminant_map . borrow ( ) . get ( & variant_id) {
189+ // This is necessary because we need to let the `discriminant_map`
190+ // borrow fall out of scope, so that we can reborrow farther down.
191+ maybe_expr = ( * get_expr) . clone ( ) ;
192+ } else {
193+ self . sess . span_bug ( variant. span ,
194+ "`check_static_recursion` attempted to visit \
195+ variant with unknown discriminant")
196+ }
197+ // If `maybe_expr` is `None`, that's because no discriminant is
198+ // specified that affects this variant. Thus, no risk of recursion.
199+ if let Some ( expr) = maybe_expr {
200+ self . with_item_id_pushed ( expr. id , |v| visit:: walk_expr ( v, expr) ) ;
201+ }
202+ }
203+
204+ fn visit_trait_item ( & mut self , ti : & ' ast ast:: TraitItem ) {
120205 self . with_item_id_pushed ( ti. id , |v| visit:: walk_trait_item ( v, ti) ) ;
121206 }
122207
123- fn visit_impl_item ( & mut self , ii : & ast:: ImplItem ) {
208+ fn visit_impl_item ( & mut self , ii : & ' ast ast:: ImplItem ) {
124209 self . with_item_id_pushed ( ii. id , |v| visit:: walk_impl_item ( v, ii) ) ;
125210 }
126211
127- fn visit_expr ( & mut self , e : & ast:: Expr ) {
212+ fn visit_expr ( & mut self , e : & ' ast ast:: Expr ) {
128213 match e. node {
129214 ast:: ExprPath ( ..) => {
130215 match self . def_map . borrow ( ) . get ( & e. id ) . map ( |d| d. base_def ) {
131216 Some ( DefStatic ( def_id, _) ) |
132217 Some ( DefAssociatedConst ( def_id, _) ) |
133- Some ( DefConst ( def_id) ) if
134- ast_util:: is_local ( def_id) => {
218+ Some ( DefConst ( def_id) )
219+ if ast_util:: is_local ( def_id) => {
135220 match self . ast_map . get ( def_id. node ) {
136221 ast_map:: NodeItem ( item) =>
137222 self . visit_item ( item) ,
@@ -141,11 +226,28 @@ impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
141226 self . visit_impl_item ( item) ,
142227 ast_map:: NodeForeignItem ( _) => { } ,
143228 _ => {
144- span_err ! ( self . sess, e. span, E0266 ,
145- "expected item, found {}" ,
146- self . ast_map. node_to_string( def_id. node) ) ;
147- return ;
148- } ,
229+ self . sess . span_bug (
230+ e. span ,
231+ & format ! ( "expected item, found {}" ,
232+ self . ast_map. node_to_string( def_id. node) ) ) ;
233+ }
234+ }
235+ }
236+ // For variants, we only want to check expressions that
237+ // affect the specific variant used, but we need to check
238+ // the whole enum definition to see what expression that
239+ // might be (if any).
240+ Some ( DefVariant ( enum_id, variant_id, false ) )
241+ if ast_util:: is_local ( enum_id) => {
242+ if let ast:: ItemEnum ( ref enum_def, ref generics) =
243+ self . ast_map . expect_item ( enum_id. local_id ( ) ) . node {
244+ self . populate_enum_discriminants ( enum_def) ;
245+ let variant = self . ast_map . expect_variant ( variant_id. local_id ( ) ) ;
246+ self . visit_variant ( variant, generics) ;
247+ } else {
248+ self . sess . span_bug ( e. span ,
249+ "`check_static_recursion` found \
250+ non-enum in DefVariant") ;
149251 }
150252 }
151253 _ => ( )
0 commit comments