@@ -35,11 +35,26 @@ use super::promote_consts::{self, Candidate, TempState};
35
35
/// What kind of item we are in.
36
36
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
37
37
enum Mode {
38
- Const ,
38
+ /// A `static` item.
39
39
Static ,
40
+ /// A `static mut` item.
40
41
StaticMut ,
42
+ /// A `const fn` item.
41
43
ConstFn ,
42
- Fn
44
+ /// A `const` item or an anonymous constant (e.g. in array lengths).
45
+ Const ,
46
+ /// Other type of `fn`.
47
+ NonConstFn ,
48
+ }
49
+
50
+ impl Mode {
51
+ /// Determine whether we are running in "const context". "const context" refers
52
+ /// to code type-checked according to the rules of the "const type system":
53
+ /// the bodies of const/static items and `const fn`.
54
+ #[ inline]
55
+ fn requires_const_checking ( self ) -> bool {
56
+ self != Mode :: NonConstFn
57
+ }
43
58
}
44
59
45
60
impl fmt:: Display for Mode {
@@ -48,7 +63,7 @@ impl fmt::Display for Mode {
48
63
Mode :: Const => write ! ( f, "constant" ) ,
49
64
Mode :: Static | Mode :: StaticMut => write ! ( f, "static" ) ,
50
65
Mode :: ConstFn => write ! ( f, "constant function" ) ,
51
- Mode :: Fn => write ! ( f, "function" )
66
+ Mode :: NonConstFn => write ! ( f, "function" )
52
67
}
53
68
}
54
69
}
@@ -135,10 +150,10 @@ enum ValueSource<'a, 'tcx> {
135
150
} ,
136
151
}
137
152
138
- /// A "qualif" is a way to lookg for something "bad" in the MIR that would prevent
139
- /// proper const evaluation. So `return true` means "I found something bad, no reason
140
- /// to go on searching". `false` is only returned if we definitely cannot find anything
141
- /// bad anywhere.
153
+ /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
154
+ /// code for promotion or prevent it from evaluating at compile time. So `return true` means
155
+ /// "I found something bad, no reason to go on searching". `false` is only returned if we
156
+ /// definitely cannot find anything bad anywhere.
142
157
///
143
158
/// The default implementations proceed structurally.
144
159
trait Qualif {
@@ -291,9 +306,11 @@ trait Qualif {
291
306
}
292
307
}
293
308
294
- /// Constant containing interior mutability (UnsafeCell).
309
+ /// Constant containing interior mutability (` UnsafeCell<T>` ).
295
310
/// This must be ruled out to make sure that evaluating the constant at compile-time
296
- /// and run-time would produce the same result.
311
+ /// and run-time would produce the same result. In particular, promotion of temporaries
312
+ /// must not change program behavior; if the promoted could be written to, that would
313
+ /// be a problem.
297
314
struct HasMutInterior ;
298
315
299
316
impl Qualif for HasMutInterior {
@@ -322,10 +339,10 @@ impl Qualif for HasMutInterior {
322
339
_ => return true ,
323
340
}
324
341
} else if let ty:: Array ( _, len) = ty. sty {
325
- // FIXME(eddyb) the `cx.mode == Mode::Fn ` condition
342
+ // FIXME(eddyb) the `cx.mode == Mode::NonConstFn ` condition
326
343
// seems unnecessary, given that this is merely a ZST.
327
344
match len. assert_usize ( cx. tcx ) {
328
- Some ( 0 ) if cx. mode == Mode :: Fn => { } ,
345
+ Some ( 0 ) if cx. mode == Mode :: NonConstFn => { } ,
329
346
_ => return true ,
330
347
}
331
348
} else {
@@ -351,9 +368,10 @@ impl Qualif for HasMutInterior {
351
368
}
352
369
}
353
370
354
- /// Constant containing an ADT that implements Drop.
355
- /// This must be ruled out because we cannot run `Drop` during compile-time
356
- /// as that might not be a `const fn`.
371
+ /// Constant containing an ADT that implements `Drop`.
372
+ /// This must be ruled out (a) because we cannot run `Drop` during compile-time
373
+ /// as that might not be a `const fn`, and (b) because implicit promotion would
374
+ /// remove side-effects that occur as part of dropping that value.
357
375
struct NeedsDrop ;
358
376
359
377
impl Qualif for NeedsDrop {
@@ -376,11 +394,12 @@ impl Qualif for NeedsDrop {
376
394
}
377
395
}
378
396
379
- /// Not promotable at all - non-`const fn` calls, asm!,
397
+ /// Not promotable at all - non-`const fn` calls, ` asm!` ,
380
398
/// pointer comparisons, ptr-to-int casts, etc.
381
399
/// Inside a const context all constness rules apply, so promotion simply has to follow the regular
382
400
/// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
383
- /// and `NeedsDrop` respectively).
401
+ /// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
402
+ /// visitor enforces by emitting errors when working in const context.
384
403
struct IsNotPromotable ;
385
404
386
405
impl Qualif for IsNotPromotable {
@@ -411,9 +430,10 @@ impl Qualif for IsNotPromotable {
411
430
ProjectionElem :: Index ( _) => { }
412
431
413
432
ProjectionElem :: Field ( ..) => {
414
- if cx. mode == Mode :: Fn {
433
+ if cx. mode == Mode :: NonConstFn {
415
434
let base_ty = proj. base . ty ( cx. mir , cx. tcx ) . ty ;
416
435
if let Some ( def) = base_ty. ty_adt_def ( ) {
436
+ // No promotion of union field accesses.
417
437
if def. is_union ( ) {
418
438
return true ;
419
439
}
@@ -427,7 +447,7 @@ impl Qualif for IsNotPromotable {
427
447
428
448
fn in_rvalue ( cx : & ConstCx < ' _ , ' tcx > , rvalue : & Rvalue < ' tcx > ) -> bool {
429
449
match * rvalue {
430
- Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if cx. mode == Mode :: Fn => {
450
+ Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if cx. mode == Mode :: NonConstFn => {
431
451
let operand_ty = operand. ty ( cx. mir , cx. tcx ) ;
432
452
let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
433
453
let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
@@ -441,7 +461,7 @@ impl Qualif for IsNotPromotable {
441
461
}
442
462
}
443
463
444
- Rvalue :: BinaryOp ( op, ref lhs, _) if cx. mode == Mode :: Fn => {
464
+ Rvalue :: BinaryOp ( op, ref lhs, _) if cx. mode == Mode :: NonConstFn => {
445
465
if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs. ty ( cx. mir , cx. tcx ) . sty {
446
466
assert ! ( op == BinOp :: Eq || op == BinOp :: Ne ||
447
467
op == BinOp :: Le || op == BinOp :: Lt ||
@@ -524,9 +544,9 @@ impl Qualif for IsNotPromotable {
524
544
525
545
/// Refers to temporaries which cannot be promoted *implicitly*.
526
546
/// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
527
- /// Implicit promotion has almost the same rules, except that it does not happen if `const fn`
528
- /// calls are involved. The call may be perfectly alright at runtime, but fail at compile time
529
- /// e.g. due to addresses being compared inside the function.
547
+ /// Implicit promotion has almost the same rules, except that disallows `const fn` except for
548
+ /// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run- time operation
549
+ /// into a failing compile-time operation e.g. due to addresses being compared inside the function.
530
550
struct IsNotImplicitlyPromotable ;
531
551
532
552
impl Qualif for IsNotImplicitlyPromotable {
@@ -538,7 +558,7 @@ impl Qualif for IsNotImplicitlyPromotable {
538
558
args : & [ Operand < ' tcx > ] ,
539
559
_return_ty : Ty < ' tcx > ,
540
560
) -> bool {
541
- if cx. mode == Mode :: Fn {
561
+ if cx. mode == Mode :: NonConstFn {
542
562
if let ty:: FnDef ( def_id, _) = callee. ty ( cx. mir , cx. tcx ) . sty {
543
563
// Never promote runtime `const fn` calls of
544
564
// functions without `#[rustc_promotable]`.
@@ -602,8 +622,8 @@ impl ConstCx<'_, 'tcx> {
602
622
/// Checks MIR for const-correctness, using `ConstCx`
603
623
/// for value qualifications, and accumulates writes of
604
624
/// rvalue/call results to locals, in `local_qualif`.
605
- /// For functions (constant or not), it also records
606
- /// candidates for promotion in `promotion_candidates` .
625
+ /// It also records candidates for promotion in `promotion_candidates`,
626
+ /// both in functions and const/static items .
607
627
struct Checker < ' a , ' tcx > {
608
628
cx : ConstCx < ' a , ' tcx > ,
609
629
@@ -687,7 +707,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
687
707
// slightly pointless (even with feature-gating).
688
708
fn not_const ( & mut self ) {
689
709
unleash_miri ! ( self ) ;
690
- if self . mode != Mode :: Fn {
710
+ if self . mode . requires_const_checking ( ) {
691
711
let mut err = struct_span_err ! (
692
712
self . tcx. sess,
693
713
self . span,
@@ -722,7 +742,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
722
742
qualifs[ HasMutInterior ] = false ;
723
743
qualifs[ IsNotPromotable ] = true ;
724
744
725
- if self . mode != Mode :: Fn {
745
+ if self . mode . requires_const_checking ( ) {
726
746
if let BorrowKind :: Mut { .. } = kind {
727
747
let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0017 ,
728
748
"references in {}s may only refer \
@@ -752,7 +772,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
752
772
753
773
// We might have a candidate for promotion.
754
774
let candidate = Candidate :: Ref ( location) ;
755
- // We can only promote interior borrows of promotable temps .
775
+ // Start by traversing to the "base", with non-deref projections removed .
756
776
let mut place = place;
757
777
while let Place :: Projection ( ref proj) = * place {
758
778
if proj. elem == ProjectionElem :: Deref {
@@ -761,6 +781,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
761
781
place = & proj. base ;
762
782
}
763
783
debug ! ( "qualify_consts: promotion candidate: place={:?}" , place) ;
784
+ // We can only promote interior borrows of promotable temps (non-temps
785
+ // don't get promoted anyway).
786
+ // (If we bailed out of the loop due to a `Deref` above, we will definitely
787
+ // not enter the conditional here.)
764
788
if let Place :: Base ( PlaceBase :: Local ( local) ) = * place {
765
789
if self . mir . local_kind ( local) == LocalKind :: Temp {
766
790
debug ! ( "qualify_consts: promotion candidate: local={:?}" , local) ;
@@ -771,10 +795,11 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
771
795
// `HasMutInterior`, from a type that does, e.g.:
772
796
// `let _: &'static _ = &(Cell::new(1), 2).1;`
773
797
let mut local_qualifs = self . qualifs_in_local ( local) ;
774
- local_qualifs [ HasMutInterior ] = false ;
775
- // Make sure there is no reason to prevent promotion.
798
+ // Any qualifications, except HasMutInterior (see above), disqualify
799
+ // from promotion.
776
800
// This is, in particular, the "implicit promotion" version of
777
801
// the check making sure that we don't run drop glue during const-eval.
802
+ local_qualifs[ HasMutInterior ] = false ;
778
803
if !local_qualifs. 0 . iter ( ) . any ( |& qualif| qualif) {
779
804
debug ! ( "qualify_consts: promotion candidate: {:?}" , candidate) ;
780
805
self . promotion_candidates . push ( candidate) ;
@@ -821,7 +846,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
821
846
debug ! ( "store to {:?} {:?}" , kind, index) ;
822
847
823
848
// Only handle promotable temps in non-const functions.
824
- if self . mode == Mode :: Fn {
849
+ if self . mode == Mode :: NonConstFn {
825
850
if kind != LocalKind :: Temp ||
826
851
!self . temp_promotion_state [ index] . is_promotable ( ) {
827
852
return ;
@@ -956,7 +981,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
956
981
. get_attrs ( * def_id)
957
982
. iter ( )
958
983
. any ( |attr| attr. check_name ( sym:: thread_local) ) {
959
- if self . mode != Mode :: Fn {
984
+ if self . mode . requires_const_checking ( ) {
960
985
span_err ! ( self . tcx. sess, self . span, E0625 ,
961
986
"thread-local statics cannot be \
962
987
accessed at compile-time") ;
@@ -980,7 +1005,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
980
1005
}
981
1006
unleash_miri ! ( self ) ;
982
1007
983
- if self . mode != Mode :: Fn {
1008
+ if self . mode . requires_const_checking ( ) {
984
1009
let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0013 ,
985
1010
"{}s cannot refer to statics, use \
986
1011
a constant instead", self . mode) ;
@@ -1018,7 +1043,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1018
1043
}
1019
1044
let base_ty = proj. base . ty ( self . mir , self . tcx ) . ty ;
1020
1045
match self . mode {
1021
- Mode :: Fn => { } ,
1046
+ Mode :: NonConstFn => { } ,
1022
1047
_ => {
1023
1048
if let ty:: RawPtr ( _) = base_ty. sty {
1024
1049
if !self . tcx . features ( ) . const_raw_ptr_deref {
@@ -1054,7 +1079,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1054
1079
}
1055
1080
} ,
1056
1081
1057
- | Mode :: Fn
1082
+ | Mode :: NonConstFn
1058
1083
| Mode :: Static
1059
1084
| Mode :: StaticMut
1060
1085
| Mode :: Const
@@ -1144,7 +1169,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1144
1169
let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
1145
1170
match ( cast_in, cast_out) {
1146
1171
( CastTy :: Ptr ( _) , CastTy :: Int ( _) ) |
1147
- ( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: Fn => {
1172
+ ( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: NonConstFn => {
1148
1173
unleash_miri ! ( self ) ;
1149
1174
if !self . tcx . features ( ) . const_raw_ptr_to_usize_cast {
1150
1175
// in const fn and constants require the feature gate
@@ -1171,7 +1196,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1171
1196
op == BinOp :: Offset ) ;
1172
1197
1173
1198
unleash_miri ! ( self ) ;
1174
- if self . mode != Mode :: Fn && !self . tcx . features ( ) . const_compare_raw_pointers {
1199
+ if self . mode . requires_const_checking ( ) &&
1200
+ !self . tcx . features ( ) . const_compare_raw_pointers
1201
+ {
1175
1202
// require the feature gate inside constants and const fn
1176
1203
// FIXME: make it unsafe to use these operations
1177
1204
emit_feature_err (
@@ -1187,7 +1214,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1187
1214
1188
1215
Rvalue :: NullaryOp ( NullOp :: Box , _) => {
1189
1216
unleash_miri ! ( self ) ;
1190
- if self . mode != Mode :: Fn {
1217
+ if self . mode . requires_const_checking ( ) {
1191
1218
let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0010 ,
1192
1219
"allocations are not allowed in {}s" , self . mode) ;
1193
1220
err. span_label ( self . span , format ! ( "allocation not allowed in {}s" , self . mode) ) ;
@@ -1232,8 +1259,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1232
1259
// special intrinsic that can be called diretly without an intrinsic
1233
1260
// feature gate needs a language feature gate
1234
1261
"transmute" => {
1235
- // never promote transmute calls
1236
- if self . mode != Mode :: Fn {
1262
+ if self . mode . requires_const_checking ( ) {
1237
1263
// const eval transmute calls only with the feature gate
1238
1264
if !self . tcx . features ( ) . const_transmute {
1239
1265
emit_feature_err (
@@ -1256,7 +1282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1256
1282
}
1257
1283
_ => {
1258
1284
// In normal functions no calls are feature-gated.
1259
- if self . mode != Mode :: Fn {
1285
+ if self . mode . requires_const_checking ( ) {
1260
1286
let unleash_miri = self
1261
1287
. tcx
1262
1288
. sess
@@ -1315,7 +1341,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1315
1341
}
1316
1342
}
1317
1343
ty:: FnPtr ( _) => {
1318
- if self . mode != Mode :: Fn {
1344
+ if self . mode . requires_const_checking ( ) {
1319
1345
let mut err = self . tcx . sess . struct_span_err (
1320
1346
self . span ,
1321
1347
& format ! ( "function pointers are not allowed in const fn" ) ) ;
@@ -1374,7 +1400,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1374
1400
self . super_terminator_kind ( kind, location) ;
1375
1401
1376
1402
// Deny *any* live drops anywhere other than functions.
1377
- if self . mode != Mode :: Fn {
1403
+ if self . mode . requires_const_checking ( ) {
1378
1404
unleash_miri ! ( self ) ;
1379
1405
// HACK(eddyb): emulate a bit of dataflow analysis,
1380
1406
// conservatively, that drop elaboration will do.
@@ -1485,12 +1511,12 @@ impl MirPass for QualifyAndPromoteConstants {
1485
1511
let id = tcx. hir ( ) . as_local_hir_id ( def_id) . unwrap ( ) ;
1486
1512
let mut const_promoted_temps = None ;
1487
1513
let mode = match tcx. hir ( ) . body_owner_kind_by_hir_id ( id) {
1488
- hir:: BodyOwnerKind :: Closure => Mode :: Fn ,
1514
+ hir:: BodyOwnerKind :: Closure => Mode :: NonConstFn ,
1489
1515
hir:: BodyOwnerKind :: Fn => {
1490
1516
if tcx. is_const_fn ( def_id) {
1491
1517
Mode :: ConstFn
1492
1518
} else {
1493
- Mode :: Fn
1519
+ Mode :: NonConstFn
1494
1520
}
1495
1521
}
1496
1522
hir:: BodyOwnerKind :: Const => {
@@ -1502,7 +1528,7 @@ impl MirPass for QualifyAndPromoteConstants {
1502
1528
} ;
1503
1529
1504
1530
debug ! ( "run_pass: mode={:?}" , mode) ;
1505
- if mode == Mode :: Fn || mode == Mode :: ConstFn {
1531
+ if mode == Mode :: NonConstFn || mode == Mode :: ConstFn {
1506
1532
// This is ugly because Checker holds onto mir,
1507
1533
// which can't be mutated until its scope ends.
1508
1534
let ( temps, candidates) = {
0 commit comments