@@ -34,6 +34,7 @@ use std::usize;
3434use rustc:: hir:: HirId ;
3535use crate :: transform:: { MirPass , MirSource } ;
3636use super :: promote_consts:: { self , Candidate , TempState } ;
37+ use crate :: transform:: check_consts:: validation:: { ops, NonConstOp } ;
3738
3839/// What kind of item we are in.
3940#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -673,12 +674,14 @@ struct Checker<'a, 'tcx> {
673674
674675 temp_promotion_state : IndexVec < Local , TempState > ,
675676 promotion_candidates : Vec < Candidate > ,
677+ errors : Vec < ( Span , String ) > ,
678+ suppress_errors : bool ,
676679}
677680
678681macro_rules! unleash_miri {
679682 ( $this: expr) => { {
680683 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 {
682685 $this. tcx. sess. span_warn( $this. span, "skipping const checks" ) ;
683686 }
684687 return ;
@@ -736,16 +739,19 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
736739 def_id,
737740 rpo,
738741 temp_promotion_state : temps,
739- promotion_candidates : vec ! [ ]
742+ promotion_candidates : vec ! [ ] ,
743+ errors : vec ! [ ] ,
744+ suppress_errors : false ,
740745 }
741746 }
742747
743748 // FIXME(eddyb) we could split the errors into meaningful
744749 // categories, but enabling full miri would make that
745750 // slightly pointless (even with feature-gating).
746- fn not_const ( & mut self ) {
751+ fn not_const ( & mut self , op : impl NonConstOp + fmt :: Debug ) {
747752 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) ;
749755 let mut err = struct_span_err ! (
750756 self . tcx. sess,
751757 self . span,
@@ -763,6 +769,14 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
763769 }
764770 }
765771
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+
766780 /// Assigns an rvalue/call qualification to the given destination.
767781 fn assign ( & mut self , dest : & Place < ' tcx > , source : ValueSource < ' _ , ' tcx > , location : Location ) {
768782 trace ! ( "assign: {:?} <- {:?}" , dest, source) ;
@@ -781,8 +795,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
781795 qualifs[ HasMutInterior ] = false ;
782796 qualifs[ IsNotPromotable ] = true ;
783797
784- if self . mode . requires_const_checking ( ) {
798+ debug ! ( "suppress_errors: {}" , self . suppress_errors) ;
799+ if self . mode . requires_const_checking ( ) && !self . suppress_errors {
785800 if !self . tcx . sess . opts . debugging_opts . unleash_the_miri_inside_of_you {
801+ self . record_error ( ops:: MutBorrow ( kind) ) ;
786802 if let BorrowKind :: Mut { .. } = kind {
787803 let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0017 ,
788804 "references in {}s may only refer \
@@ -927,8 +943,23 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
927943
928944 /// Check a whole const, static initializer or const fn.
929945 fn check_const ( & mut self ) -> ( u8 , & ' tcx BitSet < Local > ) {
946+ use crate :: transform:: check_consts as new_checker;
947+
930948 debug ! ( "const-checking {} {:?}" , self . mode, self . def_id) ;
931949
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+
932963 let body = self . body ;
933964
934965 let mut seen_blocks = BitSet :: new_empty ( body. basic_blocks ( ) . len ( ) ) ;
@@ -937,6 +968,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
937968 seen_blocks. insert ( bb. index ( ) ) ;
938969
939970 self . visit_basic_block_data ( bb, & body[ bb] ) ;
971+ validator. visit_basic_block_data ( bb, & body[ bb] ) ;
940972
941973 let target = match body[ bb] . terminator ( ) . kind {
942974 TerminatorKind :: Goto { target } |
@@ -972,12 +1004,27 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
9721004 bb = target;
9731005 }
9741006 _ => {
975- self . not_const ( ) ;
1007+ self . not_const ( ops:: Loop ) ;
1008+ validator. check_op ( ops:: Loop ) ;
9761009 break ;
9771010 }
9781011 }
9791012 }
9801013
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+ }
9811028
9821029 // Collect all the temps we need to promote.
9831030 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> {
10431090 . get_attrs ( * def_id)
10441091 . iter ( )
10451092 . 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 ) ;
10471095 span_err ! ( self . tcx. sess, self . span, E0625 ,
10481096 "thread-local statics cannot be \
10491097 accessed at compile-time") ;
@@ -1053,7 +1101,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
10531101
10541102 // Only allow statics (not consts) to refer to other statics.
10551103 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+ {
10571108 // this is not strictly necessary as miri will also bail out
10581109 // For interior mutability we can't really catch this statically as that
10591110 // goes through raw pointers and intermediate temporaries, so miri has
@@ -1067,7 +1118,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
10671118 }
10681119 unleash_miri ! ( self ) ;
10691120
1070- if self . mode . requires_const_checking ( ) {
1121+ if self . mode . requires_const_checking ( ) && !self . suppress_errors {
1122+ self . record_error ( ops:: StaticAccess ) ;
10711123 let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0013 ,
10721124 "{}s cannot refer to statics, use \
10731125 a constant instead", self . mode) ;
@@ -1104,14 +1156,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11041156 ProjectionElem :: Deref => {
11051157 if context. is_mutating_use ( ) {
11061158 // `not_const` errors out in const contexts
1107- self . not_const ( )
1159+ self . not_const ( ops :: MutDeref )
11081160 }
11091161 let base_ty = Place :: ty_from ( place_base, proj_base, self . body , self . tcx ) . ty ;
11101162 match self . mode {
1111- Mode :: NonConstFn => { } ,
1163+ Mode :: NonConstFn => { }
1164+ _ if self . suppress_errors => { }
11121165 _ => {
11131166 if let ty:: RawPtr ( _) = base_ty. kind {
11141167 if !self . tcx . features ( ) . const_raw_ptr_deref {
1168+ self . record_error ( ops:: RawPtrDeref ) ;
11151169 emit_feature_err (
11161170 & self . tcx . sess . parse_sess , sym:: const_raw_ptr_deref,
11171171 self . span , GateIssue :: Language ,
@@ -1135,7 +1189,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11351189 if def. is_union ( ) {
11361190 match self . mode {
11371191 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 ) ;
11391196 emit_feature_err (
11401197 & self . tcx . sess . parse_sess , sym:: const_fn_union,
11411198 self . span , GateIssue :: Language ,
@@ -1155,7 +1212,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11551212 }
11561213
11571214 ProjectionElem :: Downcast ( ..) => {
1158- self . not_const ( )
1215+ self . not_const ( ops :: Downcast )
11591216 }
11601217 }
11611218 }
@@ -1241,9 +1298,12 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
12411298 ( CastTy :: Ptr ( _) , CastTy :: Int ( _) ) |
12421299 ( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: NonConstFn => {
12431300 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+ {
12451304 // in const fn and constants require the feature gate
12461305 // FIXME: make it unsafe inside const fn and constants
1306+ self . record_error ( ops:: RawPtrToIntCast ) ;
12471307 emit_feature_err (
12481308 & self . tcx . sess . parse_sess , sym:: const_raw_ptr_to_usize_cast,
12491309 self . span , GateIssue :: Language ,
@@ -1267,8 +1327,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
12671327
12681328 unleash_miri ! ( self ) ;
12691329 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
12711332 {
1333+ self . record_error ( ops:: RawPtrComparison ) ;
12721334 // require the feature gate inside constants and const fn
12731335 // FIXME: make it unsafe to use these operations
12741336 emit_feature_err (
@@ -1284,7 +1346,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
12841346
12851347 Rvalue :: NullaryOp ( NullOp :: Box , _) => {
12861348 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 ) ;
12881351 let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0010 ,
12891352 "allocations are not allowed in {}s" , self . mode) ;
12901353 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> {
13291392 // special intrinsic that can be called diretly without an intrinsic
13301393 // feature gate needs a language feature gate
13311394 "transmute" => {
1332- if self . mode . requires_const_checking ( ) {
1395+ if self . mode . requires_const_checking ( )
1396+ && !self . suppress_errors
1397+ {
13331398 // const eval transmute calls only with the feature gate
13341399 if !self . tcx . features ( ) . const_transmute {
1400+ self . record_error ( ops:: Transmute ) ;
13351401 emit_feature_err (
13361402 & self . tcx . sess . parse_sess , sym:: const_transmute,
13371403 self . span , GateIssue :: Language ,
@@ -1359,7 +1425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13591425 . opts
13601426 . debugging_opts
13611427 . 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+ {
13631432 // stable const fns or unstable const fns
13641433 // with their feature gate active
13651434 // FIXME(eddyb) move stability checks from `is_const_fn` here.
@@ -1370,6 +1439,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13701439 // since the macro is marked with the attribute.
13711440 if !self . tcx . features ( ) . const_panic {
13721441 // Don't allow panics in constants without the feature gate.
1442+ self . record_error ( ops:: Panic ) ;
13731443 emit_feature_err (
13741444 & self . tcx . sess . parse_sess ,
13751445 sym:: const_panic,
@@ -1384,6 +1454,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13841454 // functions without the feature gate active in this crate in
13851455 // order to report a better error message than the one below.
13861456 if !self . span . allows_unstable ( feature) {
1457+ self . record_error ( ops:: FnCallUnstable ( def_id, feature) ) ;
13871458 let mut err = self . tcx . sess . struct_span_err ( self . span ,
13881459 & format ! ( "`{}` is not yet stable as a const fn" ,
13891460 self . tcx. def_path_str( def_id) ) ) ;
@@ -1396,6 +1467,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13961467 err. emit ( ) ;
13971468 }
13981469 } else {
1470+ self . record_error ( ops:: FnCallNonConst ( def_id) ) ;
13991471 let mut err = struct_span_err ! (
14001472 self . tcx. sess,
14011473 self . span,
@@ -1411,13 +1483,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
14111483 }
14121484 }
14131485 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 ) ;
14211489 let mut err = self . tcx . sess . struct_span_err (
14221490 self . span ,
14231491 "function pointers are not allowed in const fn"
@@ -1426,7 +1494,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
14261494 }
14271495 }
14281496 _ => {
1429- self . not_const ( ) ;
1497+ self . not_const ( ops :: FnCallOther ) ;
14301498 }
14311499 }
14321500
@@ -1484,7 +1552,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
14841552 }
14851553
14861554 // 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 {
14881556 unleash_miri ! ( self ) ;
14891557 // HACK(eddyb): emulate a bit of dataflow analysis,
14901558 // conservatively, that drop elaboration will do.
@@ -1505,6 +1573,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
15051573 // Double-check the type being dropped, to minimize false positives.
15061574 let ty = place. ty ( self . body , self . tcx ) . ty ;
15071575 if ty. needs_drop ( self . tcx , self . param_env ) {
1576+ self . record_error_spanned ( ops:: LiveDrop , span) ;
15081577 struct_span_err ! ( self . tcx. sess, span, E0493 ,
15091578 "destructors cannot be evaluated at compile-time" )
15101579 . span_label ( span, format ! ( "{}s cannot evaluate destructors" ,
@@ -1549,7 +1618,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
15491618 self . super_statement ( statement, location) ;
15501619 }
15511620 StatementKind :: FakeRead ( FakeReadCause :: ForMatchedPlace , _) => {
1552- self . not_const ( ) ;
1621+ self . not_const ( ops :: IfOrMatch ) ;
15531622 }
15541623 // FIXME(eddyb) should these really do nothing?
15551624 StatementKind :: FakeRead ( ..) |
0 commit comments