]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/interpret/validity.rs
Auto merge of #71853 - Dylan-DPC:rollup-4qi6ry9, r=Dylan-DPC
[rust.git] / src / librustc_mir / interpret / validity.rs
index ab440cc5ebab80e8caddc200cd170ba5ab2a364d..b6991349ff4dc881b9361eac1727850499950ab4 100644 (file)
 };
 
 macro_rules! throw_validation_failure {
-    ($what:expr, $where:expr $(, $details:expr )?) => {{
+    ($what:expr, $where:expr $(, $expected:expr )?) => {{
         let mut msg = format!("encountered {}", $what);
         let where_ = &$where;
         if !where_.is_empty() {
             msg.push_str(" at ");
             write_path(&mut msg, where_);
         }
-        $( write!(&mut msg, ", but expected {}", $details).unwrap(); )?
+        $( write!(&mut msg, ", but expected {}", $expected).unwrap(); )?
         throw_ub!(ValidationFailure(msg))
     }};
 }
 
 /// Returns a validation failure for any Err value of $e.
+// FIXME: Replace all usages of try_validation! with try_validation_pat!.
 macro_rules! try_validation {
-    ($e:expr, $what:expr, $where:expr $(, $details:expr )?) => {{
-        try_validation_pat!($e, _, $what, $where $(, $details )?)
+    ($e:expr, $what:expr, $where:expr $(, $expected:expr )?) => {{
+        try_validation_pat!($e, $where, {
+            _ => { "{}", $what } $( expected { "{}", $expected } )?,
+        })
     }};
 }
 /// Like try_validation, but will throw a validation error if any of the patterns in $p are
 /// matched. Other errors are passed back to the caller, unchanged. This lets you use the patterns
 /// as a kind of validation blacklist:
 ///
-/// ```rust
-/// let v = try_validation_pat(some_fn(), Foo | Bar | Baz, "some failure", "some place");
+/// ```
+/// let v = try_validation_pat!(some_fn(), some_path, {
+///     Foo | Bar | Baz => { "some failure" },
+/// });
 /// // Failures that match $p are thrown up as validation errors, but other errors are passed back
 /// // unchanged.
 /// ```
+///
+/// An additional expected parameter can also be added to the failure message:
+///
+/// ```
+/// let v = try_validation_pat!(some_fn(), some_path, {
+///     Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" },
+/// });
+/// ```
+///
+/// An additional nicety is that both parameters actually take format args, so you can just write
+/// the format string in directly:
+///
+/// ```
+/// let v = try_validation_pat!(some_fn(), some_path, {
+///     Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value },
+/// });
+/// ```
+///
 macro_rules! try_validation_pat {
-    ($e:expr, $( $p:pat )|*, $what:expr, $where:expr $(, $details:expr )?) => {{
+    ($e:expr, $where:expr, { $( $p:pat )|+ =>
+        { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? $( , )?}) => {{
         match $e {
             Ok(x) => x,
             // We catch the error and turn it into a validation failure. We are okay with
             // allocation here as this can only slow down builds that fail anyway.
-            $( Err($p) )|* if true => throw_validation_failure!($what, $where $(, $details)?),
-            Err(e) =>  Err::<!, _>(e)?,
+            $( Err(InterpErrorInfo { kind: $p, .. }) )|+ =>
+                throw_validation_failure!(
+                    format_args!($( $what_fmt ),+),
+                    $where
+                    $(, format_args!($( $expected_fmt ),+))?
+                ),
+            #[allow(unreachable_patterns)]
+            Err(e) => Err::<!, _>(e)?,
         }
     }};
 }
@@ -359,10 +389,10 @@ fn check_safe_pointer(
                     place.ptr, size, align
                 );
                 match err.kind {
-                    err_ub!(InvalidIntPointerUsage(0)) => {
+                    err_ub!(DanglingIntPointer(0, _)) => {
                         throw_validation_failure!(format_args!("a NULL {}", kind), self.path)
                     }
-                    err_ub!(InvalidIntPointerUsage(i)) => throw_validation_failure!(
+                    err_ub!(DanglingIntPointer(i, _)) => throw_validation_failure!(
                         format_args!("a {} to unallocated address {}", kind, i),
                         self.path
                     ),
@@ -491,12 +521,9 @@ fn try_visit_primitive(
                 // We are conservative with undef for integers, but try to
                 // actually enforce the strict rules for raw pointers (mostly because
                 // that lets us re-use `ref_to_mplace`).
-                let place = try_validation_pat!(
-                    self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?),
-                    InterpErrorInfo { kind: err_ub!(InvalidUndefBytes(..)), ..},
-                    "uninitialized raw pointer",
-                    self.path
-                );
+                let place = try_validation_pat!(self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?), self.path, {
+                    err_ub!(InvalidUndefBytes(..)) => { "uninitialized raw pointer" },
+                });
                 if place.layout.is_unsized() {
                     self.check_wide_ptr_meta(place.meta, place.layout)?;
                 }
@@ -800,7 +827,7 @@ fn visit_aggregate(
 
                                 throw_validation_failure!("uninitialized bytes", self.path)
                             }
-                            // Other errors shouldn't be possible
+                            // Propagate upwards (that will also check for unexpected errors).
                             _ => return Err(err),
                         }
                     }
@@ -843,10 +870,10 @@ fn validate_operand_internal(
         // Run it.
         match visitor.visit_value(op) {
             Ok(()) => Ok(()),
-            // Allow validation failures to be returned.
+            // Pass through validation failures.
             Err(err) if matches!(err.kind, err_ub!(ValidationFailure { .. })) => Err(err),
-            // Also allow InvalidProgram to be returned, because it's likely that different callers
-            // will want to do different things in this situation.
+            // Also pass through InvalidProgram, those just indicate that we could not
+            // validate and each caller will know best what to do with them.
             Err(err) if matches!(err.kind, InterpError::InvalidProgram(_)) => Err(err),
             // Avoid other errors as those do not show *where* in the value the issue lies.
             Err(err) => bug!("Unexpected error during validation: {}", err),