@@ -30,7 +30,7 @@ use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUs
3030use rustc:: middle:: lang_items;
3131use rustc_target:: spec:: abi:: Abi ;
3232use syntax:: ast:: LitKind ;
33- use syntax:: feature_gate:: { UnstableFeatures , emit_feature_err, GateIssue } ;
33+ use syntax:: feature_gate:: { UnstableFeatures , feature_err , emit_feature_err, GateIssue } ;
3434use syntax_pos:: { Span , DUMMY_SP } ;
3535
3636use std:: fmt;
@@ -114,6 +114,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
114114 param_env : ty:: ParamEnv < ' tcx > ,
115115 local_qualif : IndexVec < Local , Option < Qualif > > ,
116116 qualif : Qualif ,
117+ const_fn_arg_vars : BitSet < Local > ,
117118 temp_promotion_state : IndexVec < Local , TempState > ,
118119 promotion_candidates : Vec < Candidate >
119120}
@@ -148,6 +149,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
148149 param_env,
149150 local_qualif,
150151 qualif : Qualif :: empty ( ) ,
152+ const_fn_arg_vars : BitSet :: new_empty ( mir. local_decls . len ( ) ) ,
151153 temp_promotion_state : temps,
152154 promotion_candidates : vec ! [ ]
153155 }
@@ -176,6 +178,26 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
176178 }
177179 }
178180
181+ /// Error about extra statements in a constant.
182+ fn statement_like ( & mut self ) {
183+ self . add ( Qualif :: NOT_CONST ) ;
184+ if self . mode != Mode :: Fn {
185+ let mut err = feature_err (
186+ & self . tcx . sess . parse_sess ,
187+ "const_let" ,
188+ self . span ,
189+ GateIssue :: Language ,
190+ & format ! ( "statements in {}s are unstable" , self . mode) ,
191+ ) ;
192+ if self . tcx . sess . teach ( & err. get_code ( ) . unwrap ( ) ) {
193+ err. note ( "Blocks in constants may only contain items (such as constant, function \
194+ definition, etc...) and a tail expression.") ;
195+ err. help ( "To avoid it, you have to replace the non-item object." ) ;
196+ }
197+ err. emit ( ) ;
198+ }
199+ }
200+
179201 /// Add the given qualification to self.qualif.
180202 fn add ( & mut self , qualif : Qualif ) {
181203 self . qualif = self . qualif | qualif;
@@ -221,46 +243,85 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
221243 return ;
222244 }
223245
224- let mut dest = dest;
225- let index = loop {
226- match dest {
227- Place :: Local ( index) => break * index,
228- // projections are transparent for assignments
229- // we qualify the entire destination at once, even if just a field would have
230- // stricter qualification
231- Place :: Projection ( proj) => {
232- // Catch more errors in the destination. `visit_place` also checks various
233- // projection rules like union field access and raw pointer deref
234- self . visit_place (
235- dest,
236- PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ,
237- location
238- ) ;
239- dest = & proj. base ;
240- } ,
241- Place :: Promoted ( ..) => bug ! ( "promoteds don't exist yet during promotion" ) ,
242- Place :: Static ( ..) => {
243- // Catch more errors in the destination. `visit_place` also checks that we
244- // do not try to access statics from constants or try to mutate statics
245- self . visit_place (
246- dest,
247- PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ,
248- location
249- ) ;
250- return ;
246+ if self . const_let_allowed ( ) {
247+ let mut dest = dest;
248+ let index = loop {
249+ match dest {
250+ // with `const_let` active, we treat all locals equal
251+ Place :: Local ( index) => break * index,
252+ // projections are transparent for assignments
253+ // we qualify the entire destination at once, even if just a field would have
254+ // stricter qualification
255+ Place :: Projection ( proj) => {
256+ // Catch more errors in the destination. `visit_place` also checks various
257+ // projection rules like union field access and raw pointer deref
258+ self . visit_place (
259+ dest,
260+ PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ,
261+ location
262+ ) ;
263+ dest = & proj. base ;
264+ } ,
265+ Place :: Promoted ( ..) => bug ! ( "promoteds don't exist yet during promotion" ) ,
266+ Place :: Static ( ..) => {
267+ // Catch more errors in the destination. `visit_place` also checks that we
268+ // do not try to access statics from constants or try to mutate statics
269+ self . visit_place (
270+ dest,
271+ PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ,
272+ location
273+ ) ;
274+ return ;
275+ }
251276 }
277+ } ;
278+ debug ! ( "store to var {:?}" , index) ;
279+ match & mut self . local_qualif [ index] {
280+ // this is overly restrictive, because even full assignments do not clear the qualif
281+ // While we could special case full assignments, this would be inconsistent with
282+ // aggregates where we overwrite all fields via assignments, which would not get
283+ // that feature.
284+ Some ( ref mut qualif) => * qualif = * qualif | self . qualif ,
285+ // insert new qualification
286+ qualif @ None => * qualif = Some ( self . qualif ) ,
252287 }
253- } ;
254- debug ! ( "store to var {:?}" , index) ;
255- match & mut self . local_qualif [ index] {
256- // this is overly restrictive, because even full assignments do not clear the qualif
257- // While we could special case full assignments, this would be inconsistent with
258- // aggregates where we overwrite all fields via assignments, which would not get
259- // that feature.
260- Some ( ref mut qualif) => * qualif = * qualif | self . qualif ,
261- // insert new qualification
262- qualif @ None => * qualif = Some ( self . qualif ) ,
288+ return ;
263289 }
290+
291+ match * dest {
292+ Place :: Local ( index) if self . mir . local_kind ( index) == LocalKind :: Temp ||
293+ self . mir . local_kind ( index) == LocalKind :: ReturnPointer => {
294+ debug ! ( "store to {:?} (temp or return pointer)" , index) ;
295+ store ( & mut self . local_qualif [ index] )
296+ }
297+
298+ Place :: Projection ( box Projection {
299+ base : Place :: Local ( index) ,
300+ elem : ProjectionElem :: Deref
301+ } ) if self . mir . local_kind ( index) == LocalKind :: Temp
302+ && self . mir . local_decls [ index] . ty . is_box ( )
303+ && self . local_qualif [ index] . map_or ( false , |qualif| {
304+ qualif. contains ( Qualif :: NOT_CONST )
305+ } ) => {
306+ // Part of `box expr`, we should've errored
307+ // already for the Box allocation Rvalue.
308+ }
309+
310+ // This must be an explicit assignment.
311+ _ => {
312+ // Catch more errors in the destination.
313+ self . visit_place (
314+ dest,
315+ PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ,
316+ location
317+ ) ;
318+ self . statement_like ( ) ;
319+ }
320+ }
321+ }
322+
323+ fn const_let_allowed ( & self ) -> bool {
324+ self . tcx . features ( ) . const_let
264325 }
265326
266327 /// Qualify a whole const, static initializer or const fn.
@@ -299,7 +360,48 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
299360 TerminatorKind :: FalseEdges { .. } |
300361 TerminatorKind :: FalseUnwind { .. } => None ,
301362
302- TerminatorKind :: Return => break ,
363+ TerminatorKind :: Return => {
364+ if !self . const_let_allowed ( ) {
365+ // Check for unused values. This usually means
366+ // there are extra statements in the AST.
367+ for temp in mir. temps_iter ( ) {
368+ if self . local_qualif [ temp] . is_none ( ) {
369+ continue ;
370+ }
371+
372+ let state = self . temp_promotion_state [ temp] ;
373+ if let TempState :: Defined { location, uses : 0 } = state {
374+ let data = & mir[ location. block ] ;
375+ let stmt_idx = location. statement_index ;
376+
377+ // Get the span for the initialization.
378+ let source_info = if stmt_idx < data. statements . len ( ) {
379+ data. statements [ stmt_idx] . source_info
380+ } else {
381+ data. terminator ( ) . source_info
382+ } ;
383+ self . span = source_info. span ;
384+
385+ // Treat this as a statement in the AST.
386+ self . statement_like ( ) ;
387+ }
388+ }
389+
390+ // Make sure there are no extra unassigned variables.
391+ self . qualif = Qualif :: NOT_CONST ;
392+ for index in mir. vars_iter ( ) {
393+ if !self . const_fn_arg_vars . contains ( index) {
394+ debug ! ( "unassigned variable {:?}" , index) ;
395+ self . assign ( & Place :: Local ( index) , Location {
396+ block : bb,
397+ statement_index : usize:: MAX ,
398+ } ) ;
399+ }
400+ }
401+ }
402+
403+ break ;
404+ }
303405 } ;
304406
305407 match target {
@@ -366,6 +468,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
366468 LocalKind :: ReturnPointer => {
367469 self . not_const ( ) ;
368470 }
471+ LocalKind :: Var if !self . const_let_allowed ( ) => {
472+ if self . mode != Mode :: Fn {
473+ emit_feature_err ( & self . tcx . sess . parse_sess , "const_let" ,
474+ self . span , GateIssue :: Language ,
475+ & format ! ( "let bindings in {}s are unstable" , self . mode) ) ;
476+ }
477+ self . add ( Qualif :: NOT_CONST ) ;
478+ }
369479 LocalKind :: Var |
370480 LocalKind :: Arg |
371481 LocalKind :: Temp => {
@@ -448,6 +558,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
448558 ProjectionElem :: Deref => {
449559 if context. is_mutating_use ( ) {
450560 this. not_const ( )
561+ } else {
562+ this. qualif = Qualif :: NOT_CONST ;
451563 }
452564 let base_ty = proj. base . ty ( this. mir , this. tcx ) . to_ty ( this. tcx ) ;
453565 match this. mode {
@@ -1050,6 +1162,46 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
10501162 debug ! ( "visit_assign: dest={:?} rvalue={:?} location={:?}" , dest, rvalue, location) ;
10511163 self . visit_rvalue ( rvalue, location) ;
10521164
1165+ // Check the allowed const fn argument forms.
1166+ if let ( Mode :: ConstFn , & Place :: Local ( index) ) = ( self . mode , dest) {
1167+ if self . mir . local_kind ( index) == LocalKind :: Var &&
1168+ self . const_fn_arg_vars . insert ( index) &&
1169+ !self . tcx . sess . features_untracked ( ) . const_let {
1170+ // Direct use of an argument is permitted.
1171+ match * rvalue {
1172+ Rvalue :: Use ( Operand :: Copy ( Place :: Local ( local) ) ) |
1173+ Rvalue :: Use ( Operand :: Move ( Place :: Local ( local) ) ) => {
1174+ if self . mir . local_kind ( local) == LocalKind :: Arg {
1175+ return ;
1176+ }
1177+ }
1178+ _ => { }
1179+ }
1180+ // Avoid a generic error for other uses of arguments.
1181+ if self . qualif . contains ( Qualif :: FN_ARGUMENT ) {
1182+ let decl = & self . mir . local_decls [ index] ;
1183+ let mut err = feature_err (
1184+ & self . tcx . sess . parse_sess ,
1185+ "const_let" ,
1186+ decl. source_info . span ,
1187+ GateIssue :: Language ,
1188+ "arguments of constant functions can only be immutable by-value bindings"
1189+ ) ;
1190+ if self . tcx . sess . teach ( & err. get_code ( ) . unwrap ( ) ) {
1191+ err. note ( "Constant functions are not allowed to mutate anything. Thus, \
1192+ binding to an argument with a mutable pattern is not allowed.") ;
1193+ err. note ( "Remove any mutable bindings from the argument list to fix this \
1194+ error. In case you need to mutate the argument, try lazily \
1195+ initializing a global variable instead of using a const fn, or \
1196+ refactoring the code to a functional style to avoid mutation if \
1197+ possible.") ;
1198+ }
1199+ err. emit ( ) ;
1200+ return ;
1201+ }
1202+ }
1203+ }
1204+
10531205 self . assign ( dest, location) ;
10541206 }
10551207
0 commit comments