about summary refs log tree commit diff
path: root/compiler/rustc_const_eval/src/interpret/validity.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_const_eval/src/interpret/validity.rs')
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs176
1 files changed, 86 insertions, 90 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 203cceccd9d..13641ef2bd3 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -17,8 +17,8 @@ use rustc_hir as hir;
 use rustc_middle::bug;
 use rustc_middle::mir::interpret::ValidationErrorKind::{self, *};
 use rustc_middle::mir::interpret::{
-    ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
-    UnsupportedOpInfo, ValidationErrorInfo, alloc_range,
+    ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind,
+    Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok,
 };
 use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Ty};
@@ -32,7 +32,7 @@ use super::machine::AllocMap;
 use super::{
     AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult,
     MPlaceTy, Machine, MemPlaceMeta, PlaceTy, Pointer, Projectable, Scalar, ValueVisitor, err_ub,
-    format_interp_error, throw_ub,
+    format_interp_error,
 };
 
 // for the validation errors
@@ -42,7 +42,7 @@ use super::InterpError::Unsupported as Unsup;
 use super::UndefinedBehaviorInfo::*;
 use super::UnsupportedOpInfo::*;
 
-macro_rules! throw_validation_failure {
+macro_rules! err_validation_failure {
     ($where:expr, $kind: expr) => {{
         let where_ = &$where;
         let path = if !where_.is_empty() {
@@ -53,10 +53,16 @@ macro_rules! throw_validation_failure {
             None
         };
 
-        throw_ub!(ValidationError(ValidationErrorInfo { path, kind: $kind }))
+        err_ub!(ValidationError(ValidationErrorInfo { path, kind: $kind }))
     }};
 }
 
+macro_rules! throw_validation_failure {
+    ($where:expr, $kind: expr) => {
+        do yeet err_validation_failure!($where, $kind)
+    };
+}
+
 /// If $e throws an error matching the pattern, throw a validation failure.
 /// Other errors are passed back to the caller, unchanged -- and if they reach the root of
 /// the visitor, we make sure only validation errors and `InvalidProgram` errors are left.
@@ -91,22 +97,22 @@ macro_rules! try_validation {
     ($e:expr, $where:expr,
     $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)?
     ) => {{
-        match $e {
-            Ok(x) => x,
+        $e.map_err(|e| {
             // 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(e) => match e.kind() {
+            let (kind, backtrace) = e.into_parts();
+            match kind {
                 $(
-                    $($p)|+ =>
-                       throw_validation_failure!(
+                    $($p)|+ => {
+                        err_validation_failure!(
                             $where,
                             $kind
-                        )
+                        ).into()
+                    }
                 ),+,
-                #[allow(unreachable_patterns)]
-                _ => Err::<!, _>(e)?,
+                _ => InterpErrorInfo::from_parts(kind, backtrace),
             }
-        }
+        })?
     }};
 }
 
@@ -378,7 +384,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
         // Undo changes
         self.path.truncate(path_len);
         // Done
-        Ok(r)
+        interp_ok(r)
     }
 
     fn read_immediate(
@@ -386,7 +392,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
         val: &PlaceTy<'tcx, M::Provenance>,
         expected: ExpectedKind,
     ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
-        Ok(try_validation!(
+        interp_ok(try_validation!(
             self.ecx.read_immediate(val),
             self.path,
             Ub(InvalidUninitBytes(None)) =>
@@ -404,7 +410,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
         val: &PlaceTy<'tcx, M::Provenance>,
         expected: ExpectedKind,
     ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
-        Ok(self.read_immediate(val, expected)?.to_scalar())
+        interp_ok(self.read_immediate(val, expected)?.to_scalar())
     }
 
     fn deref_pointer(
@@ -469,7 +475,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
             _ => bug!("Unexpected unsized type tail: {:?}", tail),
         }
 
-        Ok(())
+        interp_ok(())
     }
 
     /// Check a reference or `Box`.
@@ -510,7 +516,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
             Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
                 ptr_kind,
                 // FIXME this says "null pointer" when null but we need translate
-                pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(*i))
+                pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(i))
             },
             Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
                 ptr_kind
@@ -632,7 +638,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                 }
                 // Potentially skip recursive check.
                 if skip_recursive_check {
-                    return Ok(());
+                    return interp_ok(());
                 }
             } else {
                 // This is not CTFE, so it's Miri with recursive checking.
@@ -641,7 +647,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                 // FIXME: should we also skip `UnsafeCell` behind shared references? Currently that is not
                 // needed since validation reads bypass Stacked Borrows and data race checks.
                 if matches!(ptr_kind, PointerKind::Box) {
-                    return Ok(());
+                    return interp_ok(());
                 }
             }
             let path = &self.path;
@@ -654,7 +660,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                 new_path
             });
         }
-        Ok(())
+        interp_ok(())
     }
 
     /// Check if this is a value of primitive type, and if yes check the validity of the value
@@ -681,7 +687,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                     self.ecx.clear_provenance(value)?;
                     self.add_data_range_place(value);
                 }
-                Ok(true)
+                interp_ok(true)
             }
             ty::Char => {
                 let scalar = self.read_scalar(value, ExpectedKind::Char)?;
@@ -696,7 +702,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                     self.ecx.clear_provenance(value)?;
                     self.add_data_range_place(value);
                 }
-                Ok(true)
+                interp_ok(true)
             }
             ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
                 // NOTE: Keep this in sync with the array optimization for int/float
@@ -713,18 +719,18 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                     self.ecx.clear_provenance(value)?;
                     self.add_data_range_place(value);
                 }
-                Ok(true)
+                interp_ok(true)
             }
             ty::RawPtr(..) => {
                 let place = self.deref_pointer(value, ExpectedKind::RawPtr)?;
                 if place.layout.is_unsized() {
                     self.check_wide_ptr_meta(place.meta(), place.layout)?;
                 }
-                Ok(true)
+                interp_ok(true)
             }
             ty::Ref(_, _ty, mutbl) => {
                 self.check_safe_pointer(value, PointerKind::Ref(*mutbl))?;
-                Ok(true)
+                interp_ok(true)
             }
             ty::FnPtr(..) => {
                 let scalar = self.read_scalar(value, ExpectedKind::FnPtr)?;
@@ -753,12 +759,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                     }
                     self.add_data_range_place(value);
                 }
-                Ok(true)
+                interp_ok(true)
             }
             ty::Never => throw_validation_failure!(self.path, NeverVal),
             ty::Foreign(..) | ty::FnDef(..) => {
                 // Nothing to check.
-                Ok(true)
+                interp_ok(true)
             }
             // The above should be all the primitive types. The rest is compound, we
             // check them by visiting their fields/variants.
@@ -771,7 +777,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
             | ty::Closure(..)
             | ty::Pat(..)
             | ty::CoroutineClosure(..)
-            | ty::Coroutine(..) => Ok(false),
+            | ty::Coroutine(..) => interp_ok(false),
             // Some types only occur during typechecking, they have no layout.
             // We should not see them here and we could not check them anyway.
             ty::Error(_)
@@ -808,11 +814,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                             max_value
                         })
                     } else {
-                        return Ok(());
+                        return interp_ok(());
                     }
                 } else if scalar_layout.is_always_valid(self.ecx) {
                     // Easy. (This is reachable if `enforce_number_validity` is set.)
-                    return Ok(());
+                    return interp_ok(());
                 } else {
                     // Conservatively, we reject, because the pointer *could* have a bad
                     // value.
@@ -825,7 +831,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
         };
         // Now compare.
         if valid_range.contains(bits) {
-            Ok(())
+            interp_ok(())
         } else {
             throw_validation_failure!(self.path, OutOfRange {
                 value: format!("{bits}"),
@@ -884,7 +890,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
     }
 
     fn reset_padding(&mut self, place: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
-        let Some(data_bytes) = self.data_bytes.as_mut() else { return Ok(()) };
+        let Some(data_bytes) = self.data_bytes.as_mut() else { return interp_ok(()) };
         // Our value must be in memory, otherwise we would not have set up `data_bytes`.
         let mplace = self.ecx.force_allocation(place)?;
         // Determine starting offset and size.
@@ -896,14 +902,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
         // If there is no padding at all, we can skip the rest: check for
         // a single data range covering the entire value.
         if data_bytes.0 == &[(start_offset, size)] {
-            return Ok(());
+            return interp_ok(());
         }
         // Get a handle for the allocation. Do this only once, to avoid looking up the same
         // allocation over and over again. (Though to be fair, iterating the value already does
         // exactly that.)
         let Some(mut alloc) = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)? else {
             // A ZST, no padding to clear.
-            return Ok(());
+            return interp_ok(());
         };
         // Add a "finalizer" data range at the end, so that the iteration below finds all gaps
         // between ranges.
@@ -930,7 +936,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
             padding_cleared_until = offset + size;
         }
         assert!(padding_cleared_until == start_offset + size);
-        Ok(())
+        interp_ok(())
     }
 
     /// Computes the data range of this union type:
@@ -1070,7 +1076,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
         val: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, VariantIdx> {
         self.with_elem(PathElem::EnumTag, move |this| {
-            Ok(try_validation!(
+            interp_ok(try_validation!(
                 this.ecx.read_discriminant(val),
                 this.path,
                 Ub(InvalidTag(val)) => InvalidEnumTag {
@@ -1134,7 +1140,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
                 data_bytes.add_range(base_offset + offset, size);
             }
         }
-        Ok(())
+        interp_ok(())
     }
 
     #[inline]
@@ -1144,7 +1150,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
         val: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
         self.check_safe_pointer(val, PointerKind::Box)?;
-        Ok(())
+        interp_ok(())
     }
 
     #[inline]
@@ -1157,7 +1163,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
         // We assume that the Scalar validity range does not restrict these values
         // any further than `try_visit_primitive` does!
         if self.try_visit_primitive(val)? {
-            return Ok(());
+            return interp_ok(());
         }
 
         // Special check preventing `UnsafeCell` in the inner part of constants
@@ -1204,7 +1210,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
                 // If the size is 0, there is nothing to check.
                 // (`size` can only be 0 if `len` is 0, and empty arrays are always valid.)
                 if size == Size::ZERO {
-                    return Ok(());
+                    return interp_ok(());
                 }
                 // Now that we definitely have a non-ZST array, we know it lives in memory -- except it may
                 // be an uninitialized local variable, those are also "immediate".
@@ -1224,36 +1230,33 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
                 // No need for an alignment check here, this is not an actual memory access.
                 let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size)?.expect("we already excluded size 0");
 
-                match alloc.get_bytes_strip_provenance() {
-                    // In the happy case, we needn't check anything else.
-                    Ok(_) => {}
+                alloc.get_bytes_strip_provenance().map_err(|err| {
                     // Some error happened, try to provide a more detailed description.
-                    Err(err) => {
-                        // For some errors we might be able to provide extra information.
-                        // (This custom logic does not fit the `try_validation!` macro.)
-                        match err.kind() {
-                            Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => {
-                                // Some byte was uninitialized, determine which
-                                // element that byte belongs to so we can
-                                // provide an index.
-                                let i = usize::try_from(
-                                    access.bad.start.bytes() / layout.size.bytes(),
-                                )
-                                .unwrap();
-                                self.path.push(PathElem::ArrayElem(i));
-
-                                if matches!(err.kind(), Ub(InvalidUninitBytes(_))) {
-                                    throw_validation_failure!(self.path, Uninit { expected })
-                                } else {
-                                    throw_validation_failure!(self.path, PointerAsInt { expected })
-                                }
+                    // For some errors we might be able to provide extra information.
+                    // (This custom logic does not fit the `try_validation!` macro.)
+                    let (kind, backtrace) = err.into_parts();
+                    match kind {
+                        Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => {
+                            // Some byte was uninitialized, determine which
+                            // element that byte belongs to so we can
+                            // provide an index.
+                            let i = usize::try_from(
+                                access.bad.start.bytes() / layout.size.bytes(),
+                            )
+                            .unwrap();
+                            self.path.push(PathElem::ArrayElem(i));
+
+                            if matches!(kind, Ub(InvalidUninitBytes(_))) {
+                                err_validation_failure!(self.path, Uninit { expected }).into()
+                            } else {
+                                err_validation_failure!(self.path, PointerAsInt { expected }).into()
                             }
-
-                            // Propagate upwards (that will also check for unexpected errors).
-                            _ => return Err(err),
                         }
+
+                        // Propagate upwards (that will also check for unexpected errors).
+                        _ => return InterpErrorInfo::from_parts(kind, backtrace),
                     }
-                }
+                })?;
 
                 // Don't forget that these are all non-pointer types, and thus do not preserve
                 // provenance.
@@ -1282,7 +1285,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
                     // It's not great to catch errors here, since we can't give a very good path,
                     // but it's better than ICEing.
                     Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => {
-                        InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type: *expected_dyn_type }
+                        InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type }
                     },
                 );
             }
@@ -1331,7 +1334,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
             }
         }
 
-        Ok(())
+        interp_ok(())
     }
 }
 
@@ -1347,7 +1350,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty);
 
         // Run the visitor.
-        match self.run_for_validation(|ecx| {
+        self.run_for_validation(|ecx| {
             let reset_padding = reset_provenance_and_padding && {
                 // Check if `val` is actually stored in memory. If not, padding is not even
                 // represented and we need not reset it.
@@ -1363,29 +1366,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             };
             v.visit_value(val)?;
             v.reset_padding(val)?;
-            InterpResult::Ok(())
-        }) {
-            Ok(()) => Ok(()),
-            // Pass through validation failures and "invalid program" issues.
-            Err(err)
-                if matches!(
-                    err.kind(),
-                    err_ub!(ValidationError { .. })
-                        | InterpError::InvalidProgram(_)
-                        | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField)
-                ) =>
-            {
-                Err(err)
-            }
-            // Complain about any other kind of error -- those are bad because we'd like to
-            // report them in a way that shows *where* in the value the issue lies.
-            Err(err) => {
+            interp_ok(())
+        })
+        .map_err(|err| {
+            if !matches!(
+                err.kind(),
+                err_ub!(ValidationError { .. })
+                    | InterpError::InvalidProgram(_)
+                    | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField)
+            ) {
                 bug!(
                     "Unexpected error during validation: {}",
                     format_interp_error(self.tcx.dcx(), err)
                 );
             }
-        }
+            err
+        })
     }
 
     /// This function checks the data at `op` to be const-valid.
@@ -1456,6 +1452,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 /*reset_provenance_and_padding*/ false,
             )?;
         }
-        Ok(())
+        interp_ok(())
     }
 }