@@ -30,7 +30,7 @@ use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUs
30
30
use rustc:: middle:: lang_items;
31
31
use rustc_target:: spec:: abi:: Abi ;
32
32
use 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 } ;
34
34
use syntax_pos:: { Span , DUMMY_SP } ;
35
35
36
36
use std:: fmt;
@@ -114,6 +114,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
114
114
param_env : ty:: ParamEnv < ' tcx > ,
115
115
local_qualif : IndexVec < Local , Option < Qualif > > ,
116
116
qualif : Qualif ,
117
+ const_fn_arg_vars : BitSet < Local > ,
117
118
temp_promotion_state : IndexVec < Local , TempState > ,
118
119
promotion_candidates : Vec < Candidate >
119
120
}
@@ -148,6 +149,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
148
149
param_env,
149
150
local_qualif,
150
151
qualif : Qualif :: empty ( ) ,
152
+ const_fn_arg_vars : BitSet :: new_empty ( mir. local_decls . len ( ) ) ,
151
153
temp_promotion_state : temps,
152
154
promotion_candidates : vec ! [ ]
153
155
}
@@ -176,6 +178,26 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
176
178
}
177
179
}
178
180
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
+
179
201
/// Add the given qualification to self.qualif.
180
202
fn add ( & mut self , qualif : Qualif ) {
181
203
self . qualif = self . qualif | qualif;
@@ -221,46 +243,85 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
221
243
return ;
222
244
}
223
245
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
+ }
251
276
}
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 ) ,
252
287
}
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 ;
263
289
}
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
264
325
}
265
326
266
327
/// Qualify a whole const, static initializer or const fn.
@@ -299,7 +360,48 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
299
360
TerminatorKind :: FalseEdges { .. } |
300
361
TerminatorKind :: FalseUnwind { .. } => None ,
301
362
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
+ }
303
405
} ;
304
406
305
407
match target {
@@ -366,6 +468,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
366
468
LocalKind :: ReturnPointer => {
367
469
self . not_const ( ) ;
368
470
}
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
+ }
369
479
LocalKind :: Var |
370
480
LocalKind :: Arg |
371
481
LocalKind :: Temp => {
@@ -448,6 +558,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
448
558
ProjectionElem :: Deref => {
449
559
if context. is_mutating_use ( ) {
450
560
this. not_const ( )
561
+ } else {
562
+ this. qualif = Qualif :: NOT_CONST ;
451
563
}
452
564
let base_ty = proj. base . ty ( this. mir , this. tcx ) . to_ty ( this. tcx ) ;
453
565
match this. mode {
@@ -1050,6 +1162,46 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
1050
1162
debug ! ( "visit_assign: dest={:?} rvalue={:?} location={:?}" , dest, rvalue, location) ;
1051
1163
self . visit_rvalue ( rvalue, location) ;
1052
1164
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
+
1053
1205
self . assign ( dest, location) ;
1054
1206
}
1055
1207
0 commit comments