@@ -34,6 +34,7 @@ use std::usize;
34
34
use rustc:: hir:: HirId ;
35
35
use crate :: transform:: { MirPass , MirSource } ;
36
36
use super :: promote_consts:: { self , Candidate , TempState } ;
37
+ use crate :: transform:: check_consts:: validation:: { ops, NonConstOp } ;
37
38
38
39
/// What kind of item we are in.
39
40
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -673,12 +674,14 @@ struct Checker<'a, 'tcx> {
673
674
674
675
temp_promotion_state : IndexVec < Local , TempState > ,
675
676
promotion_candidates : Vec < Candidate > ,
677
+ errors : Vec < ( Span , String ) > ,
678
+ suppress_errors : bool ,
676
679
}
677
680
678
681
macro_rules! unleash_miri {
679
682
( $this: expr) => { {
680
683
if $this. tcx. sess. opts. debugging_opts. unleash_the_miri_inside_of_you {
681
- if $this. mode. requires_const_checking( ) {
684
+ if $this. mode. requires_const_checking( ) && !$this . suppress_errors {
682
685
$this. tcx. sess. span_warn( $this. span, "skipping const checks" ) ;
683
686
}
684
687
return ;
@@ -736,16 +739,19 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
736
739
def_id,
737
740
rpo,
738
741
temp_promotion_state : temps,
739
- promotion_candidates : vec ! [ ]
742
+ promotion_candidates : vec ! [ ] ,
743
+ errors : vec ! [ ] ,
744
+ suppress_errors : false ,
740
745
}
741
746
}
742
747
743
748
// FIXME(eddyb) we could split the errors into meaningful
744
749
// categories, but enabling full miri would make that
745
750
// slightly pointless (even with feature-gating).
746
- fn not_const ( & mut self ) {
751
+ fn not_const ( & mut self , op : impl NonConstOp + fmt :: Debug ) {
747
752
unleash_miri ! ( self ) ;
748
- if self . mode . requires_const_checking ( ) {
753
+ if self . mode . requires_const_checking ( ) && !self . suppress_errors {
754
+ self . record_error ( op) ;
749
755
let mut err = struct_span_err ! (
750
756
self . tcx. sess,
751
757
self . span,
@@ -763,6 +769,14 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
763
769
}
764
770
}
765
771
772
+ fn record_error ( & mut self , op : impl NonConstOp + fmt:: Debug ) {
773
+ self . record_error_spanned ( op, self . span ) ;
774
+ }
775
+
776
+ fn record_error_spanned ( & mut self , op : impl NonConstOp + fmt:: Debug , span : Span ) {
777
+ self . errors . push ( ( span, format ! ( "{:?}" , op) ) ) ;
778
+ }
779
+
766
780
/// Assigns an rvalue/call qualification to the given destination.
767
781
fn assign ( & mut self , dest : & Place < ' tcx > , source : ValueSource < ' _ , ' tcx > , location : Location ) {
768
782
trace ! ( "assign: {:?} <- {:?}" , dest, source) ;
@@ -781,8 +795,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
781
795
qualifs[ HasMutInterior ] = false ;
782
796
qualifs[ IsNotPromotable ] = true ;
783
797
784
- if self . mode . requires_const_checking ( ) {
798
+ debug ! ( "suppress_errors: {}" , self . suppress_errors) ;
799
+ if self . mode . requires_const_checking ( ) && !self . suppress_errors {
785
800
if !self . tcx . sess . opts . debugging_opts . unleash_the_miri_inside_of_you {
801
+ self . record_error ( ops:: MutBorrow ( kind) ) ;
786
802
if let BorrowKind :: Mut { .. } = kind {
787
803
let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0017 ,
788
804
"references in {}s may only refer \
@@ -927,8 +943,23 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
927
943
928
944
/// Check a whole const, static initializer or const fn.
929
945
fn check_const ( & mut self ) -> ( u8 , & ' tcx BitSet < Local > ) {
946
+ use crate :: transform:: check_consts as new_checker;
947
+
930
948
debug ! ( "const-checking {} {:?}" , self . mode, self . def_id) ;
931
949
950
+ // FIXME: Also use the new validator when features that require it (e.g. `const_if`) are
951
+ // enabled.
952
+ let use_new_validator = self . tcx . sess . opts . debugging_opts . unleash_the_miri_inside_of_you ;
953
+ if use_new_validator {
954
+ debug ! ( "Using dataflow-based const validator" ) ;
955
+ }
956
+
957
+ let item = new_checker:: Item :: new ( self . tcx , self . def_id , self . body ) ;
958
+ let mut validator = new_checker:: validation:: Validator :: new ( & item) ;
959
+
960
+ validator. suppress_errors = !use_new_validator;
961
+ self . suppress_errors = use_new_validator;
962
+
932
963
let body = self . body ;
933
964
934
965
let mut seen_blocks = BitSet :: new_empty ( body. basic_blocks ( ) . len ( ) ) ;
@@ -937,6 +968,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
937
968
seen_blocks. insert ( bb. index ( ) ) ;
938
969
939
970
self . visit_basic_block_data ( bb, & body[ bb] ) ;
971
+ validator. visit_basic_block_data ( bb, & body[ bb] ) ;
940
972
941
973
let target = match body[ bb] . terminator ( ) . kind {
942
974
TerminatorKind :: Goto { target } |
@@ -972,12 +1004,27 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
972
1004
bb = target;
973
1005
}
974
1006
_ => {
975
- self . not_const ( ) ;
1007
+ self . not_const ( ops:: Loop ) ;
1008
+ validator. check_op ( ops:: Loop ) ;
976
1009
break ;
977
1010
}
978
1011
}
979
1012
}
980
1013
1014
+ // The new validation pass should agree with the old when running on simple const bodies
1015
+ // (e.g. no `if` or `loop`).
1016
+ if !use_new_validator {
1017
+ let mut new_errors = validator. take_errors ( ) ;
1018
+
1019
+ // FIXME: each checker sometimes emits the same error with the same span twice in a row.
1020
+ self . errors . dedup ( ) ;
1021
+ new_errors. dedup ( ) ;
1022
+ if self . errors != new_errors {
1023
+ error ! ( "old validator: {:?}" , self . errors) ;
1024
+ error ! ( "new validator: {:?}" , new_errors) ;
1025
+ panic ! ( "disagreement between validators:" ) ;
1026
+ }
1027
+ }
981
1028
982
1029
// Collect all the temps we need to promote.
983
1030
let mut promoted_temps = BitSet :: new_empty ( self . temp_promotion_state . len ( ) ) ;
@@ -1043,7 +1090,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1043
1090
. get_attrs ( * def_id)
1044
1091
. iter ( )
1045
1092
. any ( |attr| attr. check_name ( sym:: thread_local) ) {
1046
- if self . mode . requires_const_checking ( ) {
1093
+ if self . mode . requires_const_checking ( ) && !self . suppress_errors {
1094
+ self . record_error ( ops:: ThreadLocalAccess ) ;
1047
1095
span_err ! ( self . tcx. sess, self . span, E0625 ,
1048
1096
"thread-local statics cannot be \
1049
1097
accessed at compile-time") ;
@@ -1053,7 +1101,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1053
1101
1054
1102
// Only allow statics (not consts) to refer to other statics.
1055
1103
if self . mode == Mode :: Static || self . mode == Mode :: StaticMut {
1056
- if self . mode == Mode :: Static && context. is_mutating_use ( ) {
1104
+ if self . mode == Mode :: Static
1105
+ && context. is_mutating_use ( )
1106
+ && !self . suppress_errors
1107
+ {
1057
1108
// this is not strictly necessary as miri will also bail out
1058
1109
// For interior mutability we can't really catch this statically as that
1059
1110
// goes through raw pointers and intermediate temporaries, so miri has
@@ -1067,7 +1118,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1067
1118
}
1068
1119
unleash_miri ! ( self ) ;
1069
1120
1070
- if self . mode . requires_const_checking ( ) {
1121
+ if self . mode . requires_const_checking ( ) && !self . suppress_errors {
1122
+ self . record_error ( ops:: StaticAccess ) ;
1071
1123
let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0013 ,
1072
1124
"{}s cannot refer to statics, use \
1073
1125
a constant instead", self . mode) ;
@@ -1104,14 +1156,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1104
1156
ProjectionElem :: Deref => {
1105
1157
if context. is_mutating_use ( ) {
1106
1158
// `not_const` errors out in const contexts
1107
- self . not_const ( )
1159
+ self . not_const ( ops :: MutDeref )
1108
1160
}
1109
1161
let base_ty = Place :: ty_from ( place_base, proj_base, self . body , self . tcx ) . ty ;
1110
1162
match self . mode {
1111
- Mode :: NonConstFn => { } ,
1163
+ Mode :: NonConstFn => { }
1164
+ _ if self . suppress_errors => { }
1112
1165
_ => {
1113
1166
if let ty:: RawPtr ( _) = base_ty. kind {
1114
1167
if !self . tcx . features ( ) . const_raw_ptr_deref {
1168
+ self . record_error ( ops:: RawPtrDeref ) ;
1115
1169
emit_feature_err (
1116
1170
& self . tcx . sess . parse_sess , sym:: const_raw_ptr_deref,
1117
1171
self . span , GateIssue :: Language ,
@@ -1135,7 +1189,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1135
1189
if def. is_union ( ) {
1136
1190
match self . mode {
1137
1191
Mode :: ConstFn => {
1138
- if !self . tcx . features ( ) . const_fn_union {
1192
+ if !self . tcx . features ( ) . const_fn_union
1193
+ && !self . suppress_errors
1194
+ {
1195
+ self . record_error ( ops:: UnionAccess ) ;
1139
1196
emit_feature_err (
1140
1197
& self . tcx . sess . parse_sess , sym:: const_fn_union,
1141
1198
self . span , GateIssue :: Language ,
@@ -1155,7 +1212,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1155
1212
}
1156
1213
1157
1214
ProjectionElem :: Downcast ( ..) => {
1158
- self . not_const ( )
1215
+ self . not_const ( ops :: Downcast )
1159
1216
}
1160
1217
}
1161
1218
}
@@ -1241,9 +1298,12 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1241
1298
( CastTy :: Ptr ( _) , CastTy :: Int ( _) ) |
1242
1299
( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: NonConstFn => {
1243
1300
unleash_miri ! ( self ) ;
1244
- if !self . tcx . features ( ) . const_raw_ptr_to_usize_cast {
1301
+ if !self . tcx . features ( ) . const_raw_ptr_to_usize_cast
1302
+ && !self . suppress_errors
1303
+ {
1245
1304
// in const fn and constants require the feature gate
1246
1305
// FIXME: make it unsafe inside const fn and constants
1306
+ self . record_error ( ops:: RawPtrToIntCast ) ;
1247
1307
emit_feature_err (
1248
1308
& self . tcx . sess . parse_sess , sym:: const_raw_ptr_to_usize_cast,
1249
1309
self . span , GateIssue :: Language ,
@@ -1267,8 +1327,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1267
1327
1268
1328
unleash_miri ! ( self ) ;
1269
1329
if self . mode . requires_const_checking ( ) &&
1270
- !self . tcx . features ( ) . const_compare_raw_pointers
1330
+ !self . tcx . features ( ) . const_compare_raw_pointers &&
1331
+ !self . suppress_errors
1271
1332
{
1333
+ self . record_error ( ops:: RawPtrComparison ) ;
1272
1334
// require the feature gate inside constants and const fn
1273
1335
// FIXME: make it unsafe to use these operations
1274
1336
emit_feature_err (
@@ -1284,7 +1346,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1284
1346
1285
1347
Rvalue :: NullaryOp ( NullOp :: Box , _) => {
1286
1348
unleash_miri ! ( self ) ;
1287
- if self . mode . requires_const_checking ( ) {
1349
+ if self . mode . requires_const_checking ( ) && !self . suppress_errors {
1350
+ self . record_error ( ops:: HeapAllocation ) ;
1288
1351
let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0010 ,
1289
1352
"allocations are not allowed in {}s" , self . mode) ;
1290
1353
err. span_label ( self . span , format ! ( "allocation not allowed in {}s" , self . mode) ) ;
@@ -1329,9 +1392,12 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1329
1392
// special intrinsic that can be called diretly without an intrinsic
1330
1393
// feature gate needs a language feature gate
1331
1394
"transmute" => {
1332
- if self . mode . requires_const_checking ( ) {
1395
+ if self . mode . requires_const_checking ( )
1396
+ && !self . suppress_errors
1397
+ {
1333
1398
// const eval transmute calls only with the feature gate
1334
1399
if !self . tcx . features ( ) . const_transmute {
1400
+ self . record_error ( ops:: Transmute ) ;
1335
1401
emit_feature_err (
1336
1402
& self . tcx . sess . parse_sess , sym:: const_transmute,
1337
1403
self . span , GateIssue :: Language ,
@@ -1359,7 +1425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1359
1425
. opts
1360
1426
. debugging_opts
1361
1427
. unleash_the_miri_inside_of_you ;
1362
- if self . tcx . is_const_fn ( def_id) || unleash_miri {
1428
+ if self . tcx . is_const_fn ( def_id)
1429
+ || unleash_miri
1430
+ || self . suppress_errors
1431
+ {
1363
1432
// stable const fns or unstable const fns
1364
1433
// with their feature gate active
1365
1434
// FIXME(eddyb) move stability checks from `is_const_fn` here.
@@ -1370,6 +1439,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1370
1439
// since the macro is marked with the attribute.
1371
1440
if !self . tcx . features ( ) . const_panic {
1372
1441
// Don't allow panics in constants without the feature gate.
1442
+ self . record_error ( ops:: Panic ) ;
1373
1443
emit_feature_err (
1374
1444
& self . tcx . sess . parse_sess ,
1375
1445
sym:: const_panic,
@@ -1384,6 +1454,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1384
1454
// functions without the feature gate active in this crate in
1385
1455
// order to report a better error message than the one below.
1386
1456
if !self . span . allows_unstable ( feature) {
1457
+ self . record_error ( ops:: FnCallUnstable ( def_id, feature) ) ;
1387
1458
let mut err = self . tcx . sess . struct_span_err ( self . span ,
1388
1459
& format ! ( "`{}` is not yet stable as a const fn" ,
1389
1460
self . tcx. def_path_str( def_id) ) ) ;
@@ -1396,6 +1467,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1396
1467
err. emit ( ) ;
1397
1468
}
1398
1469
} else {
1470
+ self . record_error ( ops:: FnCallNonConst ( def_id) ) ;
1399
1471
let mut err = struct_span_err ! (
1400
1472
self . tcx. sess,
1401
1473
self . span,
@@ -1411,13 +1483,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1411
1483
}
1412
1484
}
1413
1485
ty:: FnPtr ( _) => {
1414
- let unleash_miri = self
1415
- . tcx
1416
- . sess
1417
- . opts
1418
- . debugging_opts
1419
- . unleash_the_miri_inside_of_you ;
1420
- if self . mode . requires_const_checking ( ) && !unleash_miri {
1486
+ unleash_miri ! ( self ) ;
1487
+ if self . mode . requires_const_checking ( ) && !self . suppress_errors {
1488
+ self . record_error ( ops:: FnCallIndirect ) ;
1421
1489
let mut err = self . tcx . sess . struct_span_err (
1422
1490
self . span ,
1423
1491
"function pointers are not allowed in const fn"
@@ -1426,7 +1494,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1426
1494
}
1427
1495
}
1428
1496
_ => {
1429
- self . not_const ( ) ;
1497
+ self . not_const ( ops :: FnCallOther ) ;
1430
1498
}
1431
1499
}
1432
1500
@@ -1484,7 +1552,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1484
1552
}
1485
1553
1486
1554
// Deny *any* live drops anywhere other than functions.
1487
- if self . mode . requires_const_checking ( ) {
1555
+ if self . mode . requires_const_checking ( ) && ! self . suppress_errors {
1488
1556
unleash_miri ! ( self ) ;
1489
1557
// HACK(eddyb): emulate a bit of dataflow analysis,
1490
1558
// conservatively, that drop elaboration will do.
@@ -1505,6 +1573,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1505
1573
// Double-check the type being dropped, to minimize false positives.
1506
1574
let ty = place. ty ( self . body , self . tcx ) . ty ;
1507
1575
if ty. needs_drop ( self . tcx , self . param_env ) {
1576
+ self . record_error_spanned ( ops:: LiveDrop , span) ;
1508
1577
struct_span_err ! ( self . tcx. sess, span, E0493 ,
1509
1578
"destructors cannot be evaluated at compile-time" )
1510
1579
. span_label ( span, format ! ( "{}s cannot evaluate destructors" ,
@@ -1549,7 +1618,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1549
1618
self . super_statement ( statement, location) ;
1550
1619
}
1551
1620
StatementKind :: FakeRead ( FakeReadCause :: ForMatchedPlace , _) => {
1552
- self . not_const ( ) ;
1621
+ self . not_const ( ops :: IfOrMatch ) ;
1553
1622
}
1554
1623
// FIXME(eddyb) should these really do nothing?
1555
1624
StatementKind :: FakeRead ( ..) |
0 commit comments