@@ -11,6 +11,7 @@ use std::ops::RangeInclusive;
1111
1212use rustc_data_structures:: fx:: FxHashSet ;
1313use rustc_hir as hir;
14+ use rustc_middle:: mir:: interpret:: { InterpError , InterpErrorInfo } ;
1415use rustc_middle:: ty;
1516use rustc_middle:: ty:: layout:: TyAndLayout ;
1617use rustc_span:: symbol:: { sym, Symbol } ;
@@ -24,43 +25,71 @@ use super::{
2425} ;
2526
2627macro_rules! throw_validation_failure {
27- ( $what: expr, $where: expr, $details: expr) => { {
28- let mut msg = format!( "encountered {}" , $what) ;
29- let where_ = & $where;
30- if !where_. is_empty( ) {
31- msg. push_str( " at " ) ;
32- write_path( & mut msg, where_) ;
33- }
34- write!( & mut msg, ", but expected {}" , $details) . unwrap( ) ;
35- throw_ub!( ValidationFailure ( msg) )
36- } } ;
37- ( $what: expr, $where: expr) => { {
28+ ( $what: expr, $where: expr $( , $expected: expr ) ?) => { {
3829 let mut msg = format!( "encountered {}" , $what) ;
3930 let where_ = & $where;
4031 if !where_. is_empty( ) {
4132 msg. push_str( " at " ) ;
4233 write_path( & mut msg, where_) ;
4334 }
35+ $( write!( & mut msg, ", but expected {}" , $expected) . unwrap( ) ; ) ?
4436 throw_ub!( ValidationFailure ( msg) )
4537 } } ;
4638}
4739
40+ /// Returns a validation failure for any Err value of $e.
41+ // FIXME: Replace all usages of try_validation! with try_validation_pat!.
4842macro_rules! try_validation {
49- ( $e: expr, $what: expr, $where: expr, $details: expr) => { {
50- match $e {
51- Ok ( x) => x,
52- // We re-throw the error, so we are okay with allocation:
53- // this can only slow down builds that fail anyway.
54- Err ( _) => throw_validation_failure!( $what, $where, $details) ,
55- }
43+ ( $e: expr, $what: expr, $where: expr $( , $expected: expr ) ?) => { {
44+ try_validation_pat!( $e, $where, {
45+ _ => { "{}" , $what } $( expected { "{}" , $expected } ) ?,
46+ } )
5647 } } ;
57-
58- ( $e: expr, $what: expr, $where: expr) => { {
48+ }
49+ /// Like try_validation, but will throw a validation error if any of the patterns in $p are
50+ /// matched. Other errors are passed back to the caller, unchanged. This lets you use the patterns
51+ /// as a kind of validation blacklist:
52+ ///
53+ /// ```
54+ /// let v = try_validation_pat!(some_fn(), some_path, {
55+ /// Foo | Bar | Baz => { "some failure" },
56+ /// });
57+ /// // Failures that match $p are thrown up as validation errors, but other errors are passed back
58+ /// // unchanged.
59+ /// ```
60+ ///
61+ /// An additional expected parameter can also be added to the failure message:
62+ ///
63+ /// ```
64+ /// let v = try_validation_pat!(some_fn(), some_path, {
65+ /// Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" },
66+ /// });
67+ /// ```
68+ ///
69+ /// An additional nicety is that both parameters actually take format args, so you can just write
70+ /// the format string in directly:
71+ ///
72+ /// ```
73+ /// let v = try_validation_pat!(some_fn(), some_path, {
74+ /// Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value },
75+ /// });
76+ /// ```
77+ ///
78+ macro_rules! try_validation_pat {
79+ ( $e: expr, $where: expr, { $( $p: pat ) |+ =>
80+ { $( $what_fmt: expr ) ,+ } $( expected { $( $expected_fmt: expr ) ,+ } ) ? $( , ) ?} ) => { {
5981 match $e {
6082 Ok ( x) => x,
61- // We re-throw the error, so we are okay with allocation:
62- // this can only slow down builds that fail anyway.
63- Err ( _) => throw_validation_failure!( $what, $where) ,
83+ // We catch the error and turn it into a validation failure. We are okay with
84+ // allocation here as this can only slow down builds that fail anyway.
85+ $( Err ( InterpErrorInfo { kind: $p, .. } ) ) |+ =>
86+ throw_validation_failure!(
87+ format_args!( $( $what_fmt ) ,+) ,
88+ $where
89+ $( , format_args!( $( $expected_fmt ) ,+) ) ?
90+ ) ,
91+ #[ allow( unreachable_patterns) ]
92+ Err ( e) => Err :: <!, _>( e) ?,
6493 }
6594 } } ;
6695}
@@ -492,11 +521,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
492521 // We are conservative with undef for integers, but try to
493522 // actually enforce the strict rules for raw pointers (mostly because
494523 // that lets us re-use `ref_to_mplace`).
495- let place = try_validation ! (
496- self . ecx. ref_to_mplace( self . ecx. read_immediate( value) ?) ,
497- "uninitialized raw pointer" ,
498- self . path
499- ) ;
524+ let place = try_validation_pat ! ( self . ecx. ref_to_mplace( self . ecx. read_immediate( value) ?) , self . path, {
525+ err_ub!( InvalidUndefBytes ( ..) ) => { "uninitialized raw pointer" } ,
526+ } ) ;
500527 if place. layout . is_unsized ( ) {
501528 self . check_wide_ptr_meta ( place. meta , place. layout ) ?;
502529 }
@@ -800,7 +827,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
800827
801828 throw_validation_failure ! ( "uninitialized bytes" , self . path)
802829 }
803- // Other errors shouldn't be possible
830+ // Propagate upwards (that will also check for unexpected errors).
804831 _ => return Err ( err) ,
805832 }
806833 }
@@ -843,9 +870,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
843870 // Run it.
844871 match visitor. visit_value ( op) {
845872 Ok ( ( ) ) => Ok ( ( ) ) ,
846- // We should only get validation errors here. Avoid other errors as
847- // those do not show *where* in the value the issue lies.
873+ // Pass through validation failures.
848874 Err ( err) if matches ! ( err. kind, err_ub!( ValidationFailure { .. } ) ) => Err ( err) ,
875+ // Also pass through InvalidProgram, those just indicate that we could not
876+ // validate and each caller will know best what to do with them.
877+ Err ( err) if matches ! ( err. kind, InterpError :: InvalidProgram ( _) ) => Err ( err) ,
878+ // Avoid other errors as those do not show *where* in the value the issue lies.
849879 Err ( err) => bug ! ( "Unexpected error during validation: {}" , err) ,
850880 }
851881 }
0 commit comments