@@ -35,11 +35,25 @@ 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 have to do full const-checking because syntactically, we
52
+ /// are required to be "const".
53
+ #[ inline]
54
+ fn requires_const_checking ( self ) -> bool {
55
+ self != Mode :: NonConstFn
56
+ }
43
57
}
44
58
45
59
impl fmt:: Display for Mode {
@@ -48,7 +62,7 @@ impl fmt::Display for Mode {
48
62
Mode :: Const => write ! ( f, "constant" ) ,
49
63
Mode :: Static | Mode :: StaticMut => write ! ( f, "static" ) ,
50
64
Mode :: ConstFn => write ! ( f, "constant function" ) ,
51
- Mode :: Fn => write ! ( f, "function" )
65
+ Mode :: NonConstFn => write ! ( f, "function" )
52
66
}
53
67
}
54
68
}
@@ -135,6 +149,12 @@ enum ValueSource<'a, 'tcx> {
135
149
} ,
136
150
}
137
151
152
+ /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
153
+ /// code for promotion or prevent it from evaluating at compile time. So `return true` means
154
+ /// "I found something bad, no reason to go on searching". `false` is only returned if we
155
+ /// definitely cannot find anything bad anywhere.
156
+ ///
157
+ /// The default implementations proceed structurally.
138
158
trait Qualif {
139
159
const IDX : usize ;
140
160
@@ -285,7 +305,11 @@ trait Qualif {
285
305
}
286
306
}
287
307
288
- // Constant containing interior mutability (UnsafeCell).
308
+ /// Constant containing interior mutability (`UnsafeCell<T>`).
309
+ /// This must be ruled out to make sure that evaluating the constant at compile-time
310
+ /// and at *any point* during the run-time would produce the same result. In particular,
311
+ /// promotion of temporaries must not change program behavior; if the promoted could be
312
+ /// written to, that would be a problem.
289
313
struct HasMutInterior ;
290
314
291
315
impl Qualif for HasMutInterior {
@@ -314,10 +338,10 @@ impl Qualif for HasMutInterior {
314
338
_ => return true ,
315
339
}
316
340
} else if let ty:: Array ( _, len) = ty. sty {
317
- // FIXME(eddyb) the `cx.mode == Mode::Fn ` condition
341
+ // FIXME(eddyb) the `cx.mode == Mode::NonConstFn ` condition
318
342
// seems unnecessary, given that this is merely a ZST.
319
343
match len. assert_usize ( cx. tcx ) {
320
- Some ( 0 ) if cx. mode == Mode :: Fn => { } ,
344
+ Some ( 0 ) if cx. mode == Mode :: NonConstFn => { } ,
321
345
_ => return true ,
322
346
}
323
347
} else {
@@ -343,7 +367,10 @@ impl Qualif for HasMutInterior {
343
367
}
344
368
}
345
369
346
- // Constant containing an ADT that implements Drop.
370
+ /// Constant containing an ADT that implements `Drop`.
371
+ /// This must be ruled out (a) because we cannot run `Drop` during compile-time
372
+ /// as that might not be a `const fn`, and (b) because implicit promotion would
373
+ /// remove side-effects that occur as part of dropping that value.
347
374
struct NeedsDrop ;
348
375
349
376
impl Qualif for NeedsDrop {
@@ -366,8 +393,12 @@ impl Qualif for NeedsDrop {
366
393
}
367
394
}
368
395
369
- // Not promotable at all - non-`const fn` calls, asm!,
370
- // pointer comparisons, ptr-to-int casts, etc.
396
+ /// Not promotable at all - non-`const fn` calls, `asm!`,
397
+ /// pointer comparisons, ptr-to-int casts, etc.
398
+ /// Inside a const context all constness rules apply, so promotion simply has to follow the regular
399
+ /// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
400
+ /// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
401
+ /// visitor enforces by emitting errors when working in const context.
371
402
struct IsNotPromotable ;
372
403
373
404
impl Qualif for IsNotPromotable {
@@ -398,9 +429,10 @@ impl Qualif for IsNotPromotable {
398
429
ProjectionElem :: Index ( _) => { }
399
430
400
431
ProjectionElem :: Field ( ..) => {
401
- if cx. mode == Mode :: Fn {
432
+ if cx. mode == Mode :: NonConstFn {
402
433
let base_ty = proj. base . ty ( cx. body , cx. tcx ) . ty ;
403
434
if let Some ( def) = base_ty. ty_adt_def ( ) {
435
+ // No promotion of union field accesses.
404
436
if def. is_union ( ) {
405
437
return true ;
406
438
}
@@ -414,7 +446,7 @@ impl Qualif for IsNotPromotable {
414
446
415
447
fn in_rvalue ( cx : & ConstCx < ' _ , ' tcx > , rvalue : & Rvalue < ' tcx > ) -> bool {
416
448
match * rvalue {
417
- Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if cx. mode == Mode :: Fn => {
449
+ Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if cx. mode == Mode :: NonConstFn => {
418
450
let operand_ty = operand. ty ( cx. body , cx. tcx ) ;
419
451
let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
420
452
let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
@@ -428,7 +460,7 @@ impl Qualif for IsNotPromotable {
428
460
}
429
461
}
430
462
431
- Rvalue :: BinaryOp ( op, ref lhs, _) if cx. mode == Mode :: Fn => {
463
+ Rvalue :: BinaryOp ( op, ref lhs, _) if cx. mode == Mode :: NonConstFn => {
432
464
if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs. ty ( cx. body , cx. tcx ) . sty {
433
465
assert ! ( op == BinOp :: Eq || op == BinOp :: Ne ||
434
466
op == BinOp :: Le || op == BinOp :: Lt ||
@@ -511,12 +543,9 @@ impl Qualif for IsNotPromotable {
511
543
512
544
/// Refers to temporaries which cannot be promoted *implicitly*.
513
545
/// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
514
- /// Inside a const context all constness rules
515
- /// apply, so implicit promotion simply has to follow the regular constant rules (modulo interior
516
- /// mutability or `Drop` rules which are handled `HasMutInterior` and `NeedsDrop` respectively).
517
- /// Implicit promotion inside regular functions does not happen if `const fn` calls are involved,
518
- /// as the call may be perfectly alright at runtime, but fail at compile time e.g. due to addresses
519
- /// being compared inside the function.
546
+ /// Implicit promotion has almost the same rules, except that disallows `const fn` except for
547
+ /// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run-time operation
548
+ /// into a failing compile-time operation e.g. due to addresses being compared inside the function.
520
549
struct IsNotImplicitlyPromotable ;
521
550
522
551
impl Qualif for IsNotImplicitlyPromotable {
@@ -528,7 +557,7 @@ impl Qualif for IsNotImplicitlyPromotable {
528
557
args : & [ Operand < ' tcx > ] ,
529
558
_return_ty : Ty < ' tcx > ,
530
559
) -> bool {
531
- if cx. mode == Mode :: Fn {
560
+ if cx. mode == Mode :: NonConstFn {
532
561
if let ty:: FnDef ( def_id, _) = callee. ty ( cx. body , cx. tcx ) . sty {
533
562
// Never promote runtime `const fn` calls of
534
563
// functions without `#[rustc_promotable]`.
@@ -589,6 +618,11 @@ impl ConstCx<'_, 'tcx> {
589
618
}
590
619
}
591
620
621
+ /// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
622
+ /// for value qualifications, and accumulates writes of
623
+ /// rvalue/call results to locals, in `local_qualif`.
624
+ /// It also records candidates for promotion in `promotion_candidates`,
625
+ /// both in functions and const/static items.
592
626
struct Checker < ' a , ' tcx > {
593
627
cx : ConstCx < ' a , ' tcx > ,
594
628
@@ -672,7 +706,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
672
706
// slightly pointless (even with feature-gating).
673
707
fn not_const ( & mut self ) {
674
708
unleash_miri ! ( self ) ;
675
- if self . mode != Mode :: Fn {
709
+ if self . mode . requires_const_checking ( ) {
676
710
let mut err = struct_span_err ! (
677
711
self . tcx. sess,
678
712
self . span,
@@ -707,7 +741,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
707
741
qualifs[ HasMutInterior ] = false ;
708
742
qualifs[ IsNotPromotable ] = true ;
709
743
710
- if self . mode != Mode :: Fn {
744
+ if self . mode . requires_const_checking ( ) {
711
745
if let BorrowKind :: Mut { .. } = kind {
712
746
let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0017 ,
713
747
"references in {}s may only refer \
@@ -737,7 +771,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
737
771
738
772
// We might have a candidate for promotion.
739
773
let candidate = Candidate :: Ref ( location) ;
740
- // We can only promote interior borrows of promotable temps .
774
+ // Start by traversing to the "base", with non-deref projections removed .
741
775
let mut place = place;
742
776
while let Place :: Projection ( ref proj) = * place {
743
777
if proj. elem == ProjectionElem :: Deref {
@@ -746,6 +780,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
746
780
place = & proj. base ;
747
781
}
748
782
debug ! ( "qualify_consts: promotion candidate: place={:?}" , place) ;
783
+ // We can only promote interior borrows of promotable temps (non-temps
784
+ // don't get promoted anyway).
785
+ // (If we bailed out of the loop due to a `Deref` above, we will definitely
786
+ // not enter the conditional here.)
749
787
if let Place :: Base ( PlaceBase :: Local ( local) ) = * place {
750
788
if self . body . local_kind ( local) == LocalKind :: Temp {
751
789
debug ! ( "qualify_consts: promotion candidate: local={:?}" , local) ;
@@ -756,6 +794,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
756
794
// `HasMutInterior`, from a type that does, e.g.:
757
795
// `let _: &'static _ = &(Cell::new(1), 2).1;`
758
796
let mut local_qualifs = self . qualifs_in_local ( local) ;
797
+ // Any qualifications, except HasMutInterior (see above), disqualify
798
+ // from promotion.
799
+ // This is, in particular, the "implicit promotion" version of
800
+ // the check making sure that we don't run drop glue during const-eval.
759
801
local_qualifs[ HasMutInterior ] = false ;
760
802
if !local_qualifs. 0 . iter ( ) . any ( |& qualif| qualif) {
761
803
debug ! ( "qualify_consts: promotion candidate: {:?}" , candidate) ;
@@ -803,7 +845,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
803
845
debug ! ( "store to {:?} {:?}" , kind, index) ;
804
846
805
847
// Only handle promotable temps in non-const functions.
806
- if self . mode == Mode :: Fn {
848
+ if self . mode == Mode :: NonConstFn {
807
849
if kind != LocalKind :: Temp ||
808
850
!self . temp_promotion_state [ index] . is_promotable ( ) {
809
851
return ;
@@ -920,11 +962,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
920
962
}
921
963
}
922
964
923
- /// Checks MIR for const-correctness, using `ConstCx`
924
- /// for value qualifications, and accumulates writes of
925
- /// rvalue/call results to locals, in `local_qualif`.
926
- /// For functions (constant or not), it also records
927
- /// candidates for promotion in `promotion_candidates`.
928
965
impl < ' a , ' tcx > Visitor < ' tcx > for Checker < ' a , ' tcx > {
929
966
fn visit_place_base (
930
967
& mut self ,
@@ -943,7 +980,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
943
980
. get_attrs ( * def_id)
944
981
. iter ( )
945
982
. any ( |attr| attr. check_name ( sym:: thread_local) ) {
946
- if self . mode != Mode :: Fn {
983
+ if self . mode . requires_const_checking ( ) {
947
984
span_err ! ( self . tcx. sess, self . span, E0625 ,
948
985
"thread-local statics cannot be \
949
986
accessed at compile-time") ;
@@ -967,7 +1004,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
967
1004
}
968
1005
unleash_miri ! ( self ) ;
969
1006
970
- if self . mode != Mode :: Fn {
1007
+ if self . mode . requires_const_checking ( ) {
971
1008
let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0013 ,
972
1009
"{}s cannot refer to statics, use \
973
1010
a constant instead", self . mode) ;
@@ -1005,7 +1042,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1005
1042
}
1006
1043
let base_ty = proj. base . ty ( self . body , self . tcx ) . ty ;
1007
1044
match self . mode {
1008
- Mode :: Fn => { } ,
1045
+ Mode :: NonConstFn => { } ,
1009
1046
_ => {
1010
1047
if let ty:: RawPtr ( _) = base_ty. sty {
1011
1048
if !self . tcx . features ( ) . const_raw_ptr_deref {
@@ -1041,7 +1078,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1041
1078
}
1042
1079
} ,
1043
1080
1044
- | Mode :: Fn
1081
+ | Mode :: NonConstFn
1045
1082
| Mode :: Static
1046
1083
| Mode :: StaticMut
1047
1084
| Mode :: Const
@@ -1131,7 +1168,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1131
1168
let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
1132
1169
match ( cast_in, cast_out) {
1133
1170
( CastTy :: Ptr ( _) , CastTy :: Int ( _) ) |
1134
- ( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: Fn => {
1171
+ ( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: NonConstFn => {
1135
1172
unleash_miri ! ( self ) ;
1136
1173
if !self . tcx . features ( ) . const_raw_ptr_to_usize_cast {
1137
1174
// in const fn and constants require the feature gate
@@ -1158,7 +1195,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1158
1195
op == BinOp :: Offset ) ;
1159
1196
1160
1197
unleash_miri ! ( self ) ;
1161
- if self . mode != Mode :: Fn && !self . tcx . features ( ) . const_compare_raw_pointers {
1198
+ if self . mode . requires_const_checking ( ) &&
1199
+ !self . tcx . features ( ) . const_compare_raw_pointers
1200
+ {
1162
1201
// require the feature gate inside constants and const fn
1163
1202
// FIXME: make it unsafe to use these operations
1164
1203
emit_feature_err (
@@ -1174,7 +1213,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1174
1213
1175
1214
Rvalue :: NullaryOp ( NullOp :: Box , _) => {
1176
1215
unleash_miri ! ( self ) ;
1177
- if self . mode != Mode :: Fn {
1216
+ if self . mode . requires_const_checking ( ) {
1178
1217
let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0010 ,
1179
1218
"allocations are not allowed in {}s" , self . mode) ;
1180
1219
err. span_label ( self . span , format ! ( "allocation not allowed in {}s" , self . mode) ) ;
@@ -1219,8 +1258,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1219
1258
// special intrinsic that can be called diretly without an intrinsic
1220
1259
// feature gate needs a language feature gate
1221
1260
"transmute" => {
1222
- // never promote transmute calls
1223
- if self . mode != Mode :: Fn {
1261
+ if self . mode . requires_const_checking ( ) {
1224
1262
// const eval transmute calls only with the feature gate
1225
1263
if !self . tcx . features ( ) . const_transmute {
1226
1264
emit_feature_err (
@@ -1243,7 +1281,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1243
1281
}
1244
1282
_ => {
1245
1283
// In normal functions no calls are feature-gated.
1246
- if self . mode != Mode :: Fn {
1284
+ if self . mode . requires_const_checking ( ) {
1247
1285
let unleash_miri = self
1248
1286
. tcx
1249
1287
. sess
@@ -1302,7 +1340,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1302
1340
}
1303
1341
}
1304
1342
ty:: FnPtr ( _) => {
1305
- if self . mode != Mode :: Fn {
1343
+ if self . mode . requires_const_checking ( ) {
1306
1344
let mut err = self . tcx . sess . struct_span_err (
1307
1345
self . span ,
1308
1346
& format ! ( "function pointers are not allowed in const fn" ) ) ;
@@ -1361,7 +1399,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1361
1399
self . super_terminator_kind ( kind, location) ;
1362
1400
1363
1401
// Deny *any* live drops anywhere other than functions.
1364
- if self . mode != Mode :: Fn {
1402
+ if self . mode . requires_const_checking ( ) {
1365
1403
unleash_miri ! ( self ) ;
1366
1404
// HACK(eddyb): emulate a bit of dataflow analysis,
1367
1405
// conservatively, that drop elaboration will do.
@@ -1472,12 +1510,12 @@ impl MirPass for QualifyAndPromoteConstants {
1472
1510
let id = tcx. hir ( ) . as_local_hir_id ( def_id) . unwrap ( ) ;
1473
1511
let mut const_promoted_temps = None ;
1474
1512
let mode = match tcx. hir ( ) . body_owner_kind_by_hir_id ( id) {
1475
- hir:: BodyOwnerKind :: Closure => Mode :: Fn ,
1513
+ hir:: BodyOwnerKind :: Closure => Mode :: NonConstFn ,
1476
1514
hir:: BodyOwnerKind :: Fn => {
1477
1515
if tcx. is_const_fn ( def_id) {
1478
1516
Mode :: ConstFn
1479
1517
} else {
1480
- Mode :: Fn
1518
+ Mode :: NonConstFn
1481
1519
}
1482
1520
}
1483
1521
hir:: BodyOwnerKind :: Const => {
@@ -1489,7 +1527,7 @@ impl MirPass for QualifyAndPromoteConstants {
1489
1527
} ;
1490
1528
1491
1529
debug ! ( "run_pass: mode={:?}" , mode) ;
1492
- if mode == Mode :: Fn || mode == Mode :: ConstFn {
1530
+ if mode == Mode :: NonConstFn || mode == Mode :: ConstFn {
1493
1531
// This is ugly because Checker holds onto mir,
1494
1532
// which can't be mutated until its scope ends.
1495
1533
let ( temps, candidates) = {
0 commit comments