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` is a cache that associates the `NodeId`s of local
31+ // variant definitions with the discriminant expression that applies to
32+ // each one. If the variant uses the default values (starting from `0`),
33+ // then `None` is stored.
34+ discriminant_map : RefCell < NodeMap < Option < & ' ast ast:: Expr > > > ,
2735}
2836
29- impl < ' v , ' a , ' ast > Visitor < ' v > for CheckCrateVisitor < ' a , ' ast > {
30- fn visit_item ( & mut self , it : & ast:: Item ) {
37+ impl < ' a , ' ast : ' a > Visitor < ' ast > for CheckCrateVisitor < ' a , ' ast > {
38+ fn visit_item ( & mut self , it : & ' ast ast:: Item ) {
3139 match it. node {
32- ast:: ItemStatic ( _ , _ , ref expr ) |
33- ast:: ItemConst ( _ , ref expr ) => {
40+ ast:: ItemStatic ( .. ) |
41+ ast:: ItemConst ( .. ) => {
3442 let mut recursion_visitor =
3543 CheckItemRecursionVisitor :: new ( self , & it. span ) ;
3644 recursion_visitor. visit_item ( it) ;
37- visit:: walk_expr ( self , & * expr)
3845 } ,
39- _ => visit:: walk_item ( self , it)
46+ ast:: ItemEnum ( ref enum_def, ref generics) => {
47+ // We could process the whole enum, but handling the variants
48+ // with discriminant expressions one by one gives more specific,
49+ // less redundant output.
50+ for variant in & enum_def. variants {
51+ if let Some ( _) = variant. node . disr_expr {
52+ let mut recursion_visitor =
53+ CheckItemRecursionVisitor :: new ( self , & variant. span ) ;
54+ recursion_visitor. populate_enum_discriminants ( enum_def) ;
55+ recursion_visitor. visit_variant ( variant, generics) ;
56+ }
57+ }
58+ }
59+ _ => { }
4060 }
61+ visit:: walk_item ( self , it)
4162 }
4263
43- fn visit_trait_item ( & mut self , ti : & ast:: TraitItem ) {
64+ fn visit_trait_item ( & mut self , ti : & ' ast ast:: TraitItem ) {
4465 match ti. node {
4566 ast:: ConstTraitItem ( _, ref default) => {
46- if let Some ( ref expr ) = * default {
67+ if let Some ( _ ) = * default {
4768 let mut recursion_visitor =
4869 CheckItemRecursionVisitor :: new ( self , & ti. span ) ;
4970 recursion_visitor. visit_trait_item ( ti) ;
50- visit:: walk_expr ( self , & * expr)
5171 }
5272 }
53- _ => visit :: walk_trait_item ( self , ti )
73+ _ => { }
5474 }
75+ visit:: walk_trait_item ( self , ti)
5576 }
5677
57- fn visit_impl_item ( & mut self , ii : & ast:: ImplItem ) {
78+ fn visit_impl_item ( & mut self , ii : & ' ast ast:: ImplItem ) {
5879 match ii. node {
59- ast:: ConstImplItem ( _ , ref expr ) => {
80+ ast:: ConstImplItem ( .. ) => {
6081 let mut recursion_visitor =
6182 CheckItemRecursionVisitor :: new ( self , & ii. span ) ;
6283 recursion_visitor. visit_impl_item ( ii) ;
63- visit:: walk_expr ( self , & * expr)
6484 }
65- _ => visit :: walk_impl_item ( self , ii )
85+ _ => { }
6686 }
87+ visit:: walk_impl_item ( self , ii)
6788 }
6889}
6990
7091pub fn check_crate < ' ast > ( sess : & Session ,
71- krate : & ast:: Crate ,
92+ krate : & ' ast ast:: Crate ,
7293 def_map : & DefMap ,
7394 ast_map : & ast_map:: Map < ' ast > ) {
7495 let mut visitor = CheckCrateVisitor {
7596 sess : sess,
7697 def_map : def_map,
77- ast_map : ast_map
98+ ast_map : ast_map,
99+ discriminant_map : RefCell :: new ( NodeMap ( ) ) ,
78100 } ;
79101 visit:: walk_crate ( & mut visitor, krate) ;
80102 sess. abort_if_errors ( ) ;
@@ -85,53 +107,120 @@ struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
85107 sess : & ' a Session ,
86108 ast_map : & ' a ast_map:: Map < ' ast > ,
87109 def_map : & ' a DefMap ,
88- idstack : Vec < ast:: NodeId >
110+ discriminant_map : & ' a RefCell < NodeMap < Option < & ' ast ast:: Expr > > > ,
111+ idstack : Vec < ast:: NodeId > ,
89112}
90113
91114impl < ' a , ' ast : ' a > CheckItemRecursionVisitor < ' a , ' ast > {
92- fn new ( v : & CheckCrateVisitor < ' a , ' ast > , span : & ' a Span )
115+ fn new ( v : & ' a CheckCrateVisitor < ' a , ' ast > , span : & ' a Span )
93116 -> CheckItemRecursionVisitor < ' a , ' ast > {
94117 CheckItemRecursionVisitor {
95118 root_span : span,
96119 sess : v. sess ,
97120 ast_map : v. ast_map ,
98121 def_map : v. def_map ,
99- idstack : Vec :: new ( )
122+ discriminant_map : & v. discriminant_map ,
123+ idstack : Vec :: new ( ) ,
100124 }
101125 }
102126 fn with_item_id_pushed < F > ( & mut self , id : ast:: NodeId , f : F )
103127 where F : Fn ( & mut Self ) {
104- if self . idstack . iter ( ) . any ( |x| x == & ( id ) ) {
128+ if self . idstack . iter ( ) . any ( |x| * x == id ) {
105129 span_err ! ( self . sess, * self . root_span, E0265 , "recursive constant" ) ;
106130 return ;
107131 }
108132 self . idstack . push ( id) ;
109133 f ( self ) ;
110134 self . idstack . pop ( ) ;
111135 }
136+ // If a variant has an expression specifying its discriminant, then it needs
137+ // to be checked just like a static or constant. However, if there are more
138+ // variants with no explicitly specified discriminant, those variants will
139+ // increment the same expression to get their values.
140+ //
141+ // So for every variant, we need to track whether there is an expression
142+ // somewhere in the enum definition that controls its discriminant. We do
143+ // this by starting from the end and searching backward.
144+ fn populate_enum_discriminants ( & self , enum_definition : & ' ast ast:: EnumDef ) {
145+ // Get the map, and return if we already processed this enum or if it
146+ // has no variants.
147+ let mut discriminant_map = self . discriminant_map . borrow_mut ( ) ;
148+ match enum_definition. variants . first ( ) {
149+ None => { return ; }
150+ Some ( variant) if discriminant_map. contains_key ( & variant. node . id ) => {
151+ return ;
152+ }
153+ _ => { }
154+ }
155+
156+ // Go through all the variants.
157+ let mut variant_stack: Vec < ast:: NodeId > = Vec :: new ( ) ;
158+ for variant in enum_definition. variants . iter ( ) . rev ( ) {
159+ variant_stack. push ( variant. node . id ) ;
160+ // When we find an expression, every variant currently on the stack
161+ // is affected by that expression.
162+ if let Some ( ref expr) = variant. node . disr_expr {
163+ for id in & variant_stack {
164+ discriminant_map. insert ( * id, Some ( expr) ) ;
165+ }
166+ variant_stack. clear ( )
167+ }
168+ }
169+ // If we are at the top, that always starts at 0, so any variant on the
170+ // stack has a default value and does not need to be checked.
171+ for id in & variant_stack {
172+ discriminant_map. insert ( * id, None ) ;
173+ }
174+ }
112175}
113176
114- impl < ' a , ' ast , ' v > Visitor < ' v > for CheckItemRecursionVisitor < ' a , ' ast > {
115- fn visit_item ( & mut self , it : & ast:: Item ) {
177+ impl < ' a , ' ast : ' a > Visitor < ' ast > for CheckItemRecursionVisitor < ' a , ' ast > {
178+ fn visit_item ( & mut self , it : & ' ast ast:: Item ) {
116179 self . with_item_id_pushed ( it. id , |v| visit:: walk_item ( v, it) ) ;
117180 }
118181
119- fn visit_trait_item ( & mut self , ti : & ast:: TraitItem ) {
182+ fn visit_enum_def ( & mut self , enum_definition : & ' ast ast:: EnumDef ,
183+ generics : & ' ast ast:: Generics ) {
184+ self . populate_enum_discriminants ( enum_definition) ;
185+ visit:: walk_enum_def ( self , enum_definition, generics) ;
186+ }
187+
188+ fn visit_variant ( & mut self , variant : & ' ast ast:: Variant ,
189+ _: & ' ast ast:: Generics ) {
190+ let variant_id = variant. node . id ;
191+ let maybe_expr;
192+ if let Some ( get_expr) = self . discriminant_map . borrow ( ) . get ( & variant_id) {
193+ // This is necessary because we need to let the `discriminant_map`
194+ // borrow fall out of scope, so that we can reborrow farther down.
195+ maybe_expr = ( * get_expr) . clone ( ) ;
196+ } else {
197+ self . sess . span_bug ( variant. span ,
198+ "`check_static_recursion` attempted to visit \
199+ variant with unknown discriminant")
200+ }
201+ // If `maybe_expr` is `None`, that's because no discriminant is
202+ // specified that affects this variant. Thus, no risk of recursion.
203+ if let Some ( expr) = maybe_expr {
204+ self . with_item_id_pushed ( expr. id , |v| visit:: walk_expr ( v, expr) ) ;
205+ }
206+ }
207+
208+ fn visit_trait_item ( & mut self , ti : & ' ast ast:: TraitItem ) {
120209 self . with_item_id_pushed ( ti. id , |v| visit:: walk_trait_item ( v, ti) ) ;
121210 }
122211
123- fn visit_impl_item ( & mut self , ii : & ast:: ImplItem ) {
212+ fn visit_impl_item ( & mut self , ii : & ' ast ast:: ImplItem ) {
124213 self . with_item_id_pushed ( ii. id , |v| visit:: walk_impl_item ( v, ii) ) ;
125214 }
126215
127- fn visit_expr ( & mut self , e : & ast:: Expr ) {
216+ fn visit_expr ( & mut self , e : & ' ast ast:: Expr ) {
128217 match e. node {
129218 ast:: ExprPath ( ..) => {
130219 match self . def_map . borrow ( ) . get ( & e. id ) . map ( |d| d. base_def ) {
131220 Some ( DefStatic ( def_id, _) ) |
132221 Some ( DefAssociatedConst ( def_id, _) ) |
133- Some ( DefConst ( def_id) ) if
134- ast_util:: is_local ( def_id) => {
222+ Some ( DefConst ( def_id) )
223+ if ast_util:: is_local ( def_id) => {
135224 match self . ast_map . get ( def_id. node ) {
136225 ast_map:: NodeItem ( item) =>
137226 self . visit_item ( item) ,
@@ -141,11 +230,28 @@ impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
141230 self . visit_impl_item ( item) ,
142231 ast_map:: NodeForeignItem ( _) => { } ,
143232 _ => {
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- } ,
233+ self . sess . span_bug (
234+ e. span ,
235+ & format ! ( "expected item, found {}" ,
236+ self . ast_map. node_to_string( def_id. node) ) ) ;
237+ }
238+ }
239+ }
240+ // For variants, we only want to check expressions that
241+ // affect the specific variant used, but we need to check
242+ // the whole enum definition to see what expression that
243+ // might be (if any).
244+ Some ( DefVariant ( enum_id, variant_id, false ) )
245+ if ast_util:: is_local ( enum_id) => {
246+ if let ast:: ItemEnum ( ref enum_def, ref generics) =
247+ self . ast_map . expect_item ( enum_id. local_id ( ) ) . node {
248+ self . populate_enum_discriminants ( enum_def) ;
249+ let variant = self . ast_map . expect_variant ( variant_id. local_id ( ) ) ;
250+ self . visit_variant ( variant, generics) ;
251+ } else {
252+ self . sess . span_bug ( e. span ,
253+ "`check_static_recursion` found \
254+ non-enum in DefVariant") ;
149255 }
150256 }
151257 _ => ( )
0 commit comments