@@ -5,6 +5,8 @@ use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}};
55use rustc_target:: spec:: abi;
66use std:: borrow:: Cow ;
77use syntax_pos:: Span ;
8+ use syntax:: symbol:: { sym, Symbol } ;
9+ use syntax:: attr;
810
911type McfResult = Result < ( ) , ( Span , Cow < ' static , str > ) > ;
1012
@@ -67,9 +69,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -
6769 ) ?;
6870
6971 for bb in body. basic_blocks ( ) {
70- check_terminator ( tcx, body, bb. terminator ( ) ) ?;
72+ check_terminator ( tcx, body, def_id , bb. terminator ( ) ) ?;
7173 for stmt in & bb. statements {
72- check_statement ( tcx, body, stmt) ?;
74+ check_statement ( tcx, body, def_id , stmt) ?;
7375 }
7476 }
7577 Ok ( ( ) )
@@ -121,16 +123,17 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> Mc
121123
122124fn check_rvalue (
123125 tcx : TyCtxt < ' tcx > ,
124- body : & ' a Body < ' tcx > ,
126+ body : & Body < ' tcx > ,
127+ def_id : DefId ,
125128 rvalue : & Rvalue < ' tcx > ,
126129 span : Span ,
127130) -> McfResult {
128131 match rvalue {
129132 Rvalue :: Repeat ( operand, _) | Rvalue :: Use ( operand) => {
130- check_operand ( operand, span)
133+ check_operand ( tcx , operand, span, def_id , body )
131134 }
132135 Rvalue :: Len ( place) | Rvalue :: Discriminant ( place) | Rvalue :: Ref ( _, _, place) => {
133- check_place ( place, span)
136+ check_place ( tcx , place, span, def_id , body )
134137 }
135138 Rvalue :: Cast ( CastKind :: Misc , operand, cast_ty) => {
136139 use rustc:: ty:: cast:: CastTy ;
@@ -144,11 +147,11 @@ fn check_rvalue(
144147 ( CastTy :: RPtr ( _) , CastTy :: Float ) => bug ! ( ) ,
145148 ( CastTy :: RPtr ( _) , CastTy :: Int ( _) ) => bug ! ( ) ,
146149 ( CastTy :: Ptr ( _) , CastTy :: RPtr ( _) ) => bug ! ( ) ,
147- _ => check_operand ( operand, span) ,
150+ _ => check_operand ( tcx , operand, span, def_id , body ) ,
148151 }
149152 }
150153 Rvalue :: Cast ( CastKind :: Pointer ( PointerCast :: MutToConstPointer ) , operand, _) => {
151- check_operand ( operand, span)
154+ check_operand ( tcx , operand, span, def_id , body )
152155 }
153156 Rvalue :: Cast ( CastKind :: Pointer ( PointerCast :: UnsafeFnPointer ) , _, _) |
154157 Rvalue :: Cast ( CastKind :: Pointer ( PointerCast :: ClosureFnPointer ( _) ) , _, _) |
@@ -162,8 +165,8 @@ fn check_rvalue(
162165 ) ) ,
163166 // binops are fine on integers
164167 Rvalue :: BinaryOp ( _, lhs, rhs) | Rvalue :: CheckedBinaryOp ( _, lhs, rhs) => {
165- check_operand ( lhs, span) ?;
166- check_operand ( rhs, span) ?;
168+ check_operand ( tcx , lhs, span, def_id , body ) ?;
169+ check_operand ( tcx , rhs, span, def_id , body ) ?;
167170 let ty = lhs. ty ( body, tcx) ;
168171 if ty. is_integral ( ) || ty. is_bool ( ) || ty. is_char ( ) {
169172 Ok ( ( ) )
@@ -182,7 +185,7 @@ fn check_rvalue(
182185 Rvalue :: UnaryOp ( _, operand) => {
183186 let ty = operand. ty ( body, tcx) ;
184187 if ty. is_integral ( ) || ty. is_bool ( ) {
185- check_operand ( operand, span)
188+ check_operand ( tcx , operand, span, def_id , body )
186189 } else {
187190 Err ( (
188191 span,
@@ -192,7 +195,7 @@ fn check_rvalue(
192195 }
193196 Rvalue :: Aggregate ( _, operands) => {
194197 for operand in operands {
195- check_operand ( operand, span) ?;
198+ check_operand ( tcx , operand, span, def_id , body ) ?;
196199 }
197200 Ok ( ( ) )
198201 }
@@ -201,21 +204,22 @@ fn check_rvalue(
201204
202205fn check_statement (
203206 tcx : TyCtxt < ' tcx > ,
204- body : & ' a Body < ' tcx > ,
207+ body : & Body < ' tcx > ,
208+ def_id : DefId ,
205209 statement : & Statement < ' tcx > ,
206210) -> McfResult {
207211 let span = statement. source_info . span ;
208212 match & statement. kind {
209213 StatementKind :: Assign ( box( place, rval) ) => {
210- check_place ( place, span) ?;
211- check_rvalue ( tcx, body, rval, span)
214+ check_place ( tcx , place, span, def_id , body ) ?;
215+ check_rvalue ( tcx, body, def_id , rval, span)
212216 }
213217
214218 StatementKind :: FakeRead ( FakeReadCause :: ForMatchedPlace , _) => {
215219 Err ( ( span, "loops and conditional expressions are not stable in const fn" . into ( ) ) )
216220 }
217221
218- StatementKind :: FakeRead ( _, place) => check_place ( place, span) ,
222+ StatementKind :: FakeRead ( _, place) => check_place ( tcx , place, span, def_id , body ) ,
219223
220224 // just an assignment
221225 StatementKind :: SetDiscriminant { .. } => Ok ( ( ) ) ,
@@ -234,30 +238,48 @@ fn check_statement(
234238}
235239
236240fn check_operand (
241+ tcx : TyCtxt < ' tcx > ,
237242 operand : & Operand < ' tcx > ,
238243 span : Span ,
244+ def_id : DefId ,
245+ body : & Body < ' tcx >
239246) -> McfResult {
240247 match operand {
241248 Operand :: Move ( place) | Operand :: Copy ( place) => {
242- check_place ( place, span)
249+ check_place ( tcx , place, span, def_id , body )
243250 }
244251 Operand :: Constant ( _) => Ok ( ( ) ) ,
245252 }
246253}
247254
248255fn check_place (
256+ tcx : TyCtxt < ' tcx > ,
249257 place : & Place < ' tcx > ,
250258 span : Span ,
259+ def_id : DefId ,
260+ body : & Body < ' tcx >
251261) -> McfResult {
252- for elem in place. projection . iter ( ) {
262+ let mut cursor = & * place. projection ;
263+ while let [ proj_base @ .., elem] = cursor {
264+ cursor = proj_base;
253265 match elem {
254266 ProjectionElem :: Downcast ( ..) => {
255267 return Err ( ( span, "`match` or `if let` in `const fn` is unstable" . into ( ) ) ) ;
256268 }
269+ ProjectionElem :: Field ( ..) => {
270+ let base_ty = Place :: ty_from ( & place. base , & proj_base, body, tcx) . ty ;
271+ if let Some ( def) = base_ty. ty_adt_def ( ) {
272+ // No union field accesses in `const fn`
273+ if def. is_union ( ) {
274+ if !feature_allowed ( tcx, def_id, sym:: const_fn_union) {
275+ return Err ( ( span, "accessing union fields is unstable" . into ( ) ) ) ;
276+ }
277+ }
278+ }
279+ }
257280 ProjectionElem :: ConstantIndex { .. }
258281 | ProjectionElem :: Subslice { .. }
259282 | ProjectionElem :: Deref
260- | ProjectionElem :: Field ( ..)
261283 | ProjectionElem :: Index ( _) => { }
262284 }
263285 }
@@ -271,9 +293,20 @@ fn check_place(
271293 }
272294}
273295
296+ /// Returns whether `allow_internal_unstable(..., <feature_gate>, ...)` is present.
297+ fn feature_allowed (
298+ tcx : TyCtxt < ' tcx > ,
299+ def_id : DefId ,
300+ feature_gate : Symbol ,
301+ ) -> bool {
302+ attr:: allow_internal_unstable ( & tcx. get_attrs ( def_id) , & tcx. sess . diagnostic ( ) )
303+ . map_or ( false , |mut features| features. any ( |name| name == feature_gate) )
304+ }
305+
274306fn check_terminator (
275307 tcx : TyCtxt < ' tcx > ,
276308 body : & ' a Body < ' tcx > ,
309+ def_id : DefId ,
277310 terminator : & Terminator < ' tcx > ,
278311) -> McfResult {
279312 let span = terminator. source_info . span ;
@@ -283,11 +316,11 @@ fn check_terminator(
283316 | TerminatorKind :: Resume => Ok ( ( ) ) ,
284317
285318 TerminatorKind :: Drop { location, .. } => {
286- check_place ( location, span)
319+ check_place ( tcx , location, span, def_id , body )
287320 }
288321 TerminatorKind :: DropAndReplace { location, value, .. } => {
289- check_place ( location, span) ?;
290- check_operand ( value, span)
322+ check_place ( tcx , location, span, def_id , body ) ?;
323+ check_operand ( tcx , value, span, def_id , body )
291324 } ,
292325
293326 TerminatorKind :: FalseEdges { .. } | TerminatorKind :: SwitchInt { .. } => Err ( (
@@ -339,10 +372,10 @@ fn check_terminator(
339372 ) ) ,
340373 }
341374
342- check_operand ( func, span) ?;
375+ check_operand ( tcx , func, span, def_id , body ) ?;
343376
344377 for arg in args {
345- check_operand ( arg, span) ?;
378+ check_operand ( tcx , arg, span, def_id , body ) ?;
346379 }
347380 Ok ( ( ) )
348381 } else {
@@ -356,7 +389,7 @@ fn check_terminator(
356389 msg : _,
357390 target : _,
358391 cleanup : _,
359- } => check_operand ( cond, span) ,
392+ } => check_operand ( tcx , cond, span, def_id , body ) ,
360393
361394 TerminatorKind :: FalseUnwind { .. } => {
362395 Err ( ( span, "loops are not allowed in const fn" . into ( ) ) )
0 commit comments