1
Fork 0

make InterpResult a dedicated type to avoid accidentally discarding the error

This commit is contained in:
Ralf Jung 2024-09-29 11:53:23 +02:00
parent 4b8a5bd511
commit c4ce8c114b
102 changed files with 1588 additions and 1466 deletions

View file

@ -6,7 +6,8 @@ use rustc_middle::{bug, span_bug, ty};
use rustc_span::def_id::DefId;
use crate::interpret::{
self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic, throw_machine_stop,
self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic, interp_ok,
throw_machine_stop,
};
/// Macro for machine-specific `InterpError` without allocation.
@ -79,7 +80,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
throw_machine_stop_str!("can't access mutable globals in ConstProp");
}
Ok(())
interp_ok(())
}
fn find_mir_or_eval_fn(
@ -127,7 +128,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
right: &interpret::ImmTy<'tcx, Self::Provenance>,
) -> interpret::InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> {
use rustc_middle::mir::BinOp::*;
Ok(match bin_op {
interp_ok(match bin_op {
Eq | Ne | Lt | Le | Gt | Ge => {
// Types can differ, e.g. fn ptrs with different `for`.
assert_eq!(left.layout.abi, right.layout.abi);

View file

@ -20,7 +20,7 @@ use crate::const_eval::CheckAlignment;
use crate::interpret::{
CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpError,
InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc,
eval_nullary_intrinsic, intern_const_alloc_recursive, throw_exhaust,
eval_nullary_intrinsic, intern_const_alloc_recursive, interp_ok, throw_exhaust,
};
use crate::{CTRL_C_RECEIVED, errors};
@ -98,19 +98,19 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
return Err(ecx
.tcx
.dcx()
.emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })
.into());
.emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }))
.into();
}
Err(InternResult::FoundBadMutablePointer) => {
return Err(ecx
.tcx
.dcx()
.emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind })
.into());
.emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }))
.into();
}
}
Ok(R::make_result(ret, ecx))
interp_ok(R::make_result(ret, ecx))
}
/// The `InterpCx` is only meant to be used to do field and index projections into constants for
@ -147,7 +147,8 @@ pub fn mk_eval_cx_for_const_val<'tcx>(
ty: Ty<'tcx>,
) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> {
let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
let op = ecx.const_val_to_op(val, ty, None).ok()?;
// FIXME: is it a problem to discard the error here?
let op = ecx.const_val_to_op(val, ty, None).discard_err()?;
Some((ecx, op))
}
@ -185,12 +186,16 @@ pub(super) fn op_to_const<'tcx>(
_ => false,
};
let immediate = if force_as_immediate {
match ecx.read_immediate(op) {
match ecx.read_immediate(op).report_err() {
Ok(imm) => Right(imm),
Err(err) if !for_diagnostics => {
panic!("normalization works on validated constants: {err:?}")
Err(err) => {
if for_diagnostics {
// This discard the error, but for diagnostics that's okay.
op.as_mplace_or_imm()
} else {
panic!("normalization works on validated constants: {err:?}")
}
}
_ => op.as_mplace_or_imm(),
}
} else {
op.as_mplace_or_imm()
@ -283,17 +288,19 @@ pub fn eval_to_const_value_raw_provider<'tcx>(
let ty::FnDef(_, args) = ty.kind() else {
bug!("intrinsic with type {:?}", ty);
};
return eval_nullary_intrinsic(tcx, key.param_env, def_id, args).map_err(|error| {
let span = tcx.def_span(def_id);
return eval_nullary_intrinsic(tcx, key.param_env, def_id, args).report_err().map_err(
|error| {
let span = tcx.def_span(def_id);
super::report(
tcx,
error.into_kind(),
span,
|| (span, vec![]),
|span, _| errors::NullaryIntrinsicError { span },
)
});
super::report(
tcx,
error.into_kind(),
span,
|| (span, vec![]),
|span, _| errors::NullaryIntrinsicError { span },
)
},
);
}
tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key))
@ -376,6 +383,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
);
let res = ecx.load_mir(cid.instance.def, cid.promoted);
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
.report_err()
.map_err(|error| report_eval_error(&ecx, cid, error))
}
@ -400,6 +408,7 @@ fn const_validate_mplace<'tcx>(
}
};
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)
.report_err()
// Instead of just reporting the `InterpError` via the usual machinery, we give a more targeted
// error about the validation failure.
.map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?;

View file

@ -24,8 +24,8 @@ use crate::fluent_generated as fluent;
use crate::interpret::{
self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy,
InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, RangeSet, Scalar,
StackPopCleanup, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom,
throw_unsup, throw_unsup_format,
StackPopCleanup, compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub,
throw_ub_custom, throw_unsup, throw_unsup_format,
};
/// When hitting this many interpreted terminators we emit a deny by default lint
@ -247,7 +247,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
let msg = Symbol::intern(self.read_str(&msg_place)?);
let span = self.find_closest_untracked_caller_location();
let (file, line, col) = self.location_triple_for_span(span);
return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into());
return Err(ConstEvalErrKind::Panic { msg, file, line, col }).into();
} else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) {
// For panic_fmt, call const_panic_fmt instead.
let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None);
@ -259,16 +259,16 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
self.cur_span(),
);
return Ok(Some(new_instance));
return interp_ok(Some(new_instance));
} else if self.tcx.is_lang_item(def_id, LangItem::AlignOffset) {
let args = self.copy_fn_args(args);
// For align_offset, we replace the function call if the pointer has no address.
match self.align_offset(instance, &args, dest, ret)? {
ControlFlow::Continue(()) => return Ok(Some(instance)),
ControlFlow::Break(()) => return Ok(None),
ControlFlow::Continue(()) => return interp_ok(Some(instance)),
ControlFlow::Break(()) => return interp_ok(None),
}
}
Ok(Some(instance))
interp_ok(Some(instance))
}
/// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer
@ -323,25 +323,25 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
dest,
StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Unreachable },
)?;
Ok(ControlFlow::Break(()))
interp_ok(ControlFlow::Break(()))
} else {
// Not alignable in const, return `usize::MAX`.
let usize_max = Scalar::from_target_usize(self.target_usize_max(), self);
self.write_scalar(usize_max, dest)?;
self.return_to_block(ret)?;
Ok(ControlFlow::Break(()))
interp_ok(ControlFlow::Break(()))
}
}
Err(_addr) => {
// The pointer has an address, continue with function call.
Ok(ControlFlow::Continue(()))
interp_ok(ControlFlow::Continue(()))
}
}
}
/// See documentation on the `ptr_guaranteed_cmp` intrinsic.
fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> {
Ok(match (a, b) {
interp_ok(match (a, b) {
// Comparisons between integers are always known.
(Scalar::Int { .. }, Scalar::Int { .. }) => {
if a == b {
@ -403,8 +403,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
instance: ty::InstanceKind<'tcx>,
) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
match instance {
ty::InstanceKind::Item(def) => Ok(ecx.tcx.mir_for_ctfe(def)),
_ => Ok(ecx.tcx.instance_mir(instance)),
ty::InstanceKind::Item(def) => interp_ok(ecx.tcx.mir_for_ctfe(def)),
_ => interp_ok(ecx.tcx.instance_mir(instance)),
}
}
@ -422,7 +422,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
// Replace some functions.
let Some(instance) = ecx.hook_special_const_fn(orig_instance, args, dest, ret)? else {
// Call has already been handled.
return Ok(None);
return interp_ok(None);
};
// Only check non-glue functions
@ -444,14 +444,14 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
// This is a const fn. Call it.
// In case of replacement, we return the *original* instance to make backtraces work out
// (and we hope this does not confuse the FnAbi checks too much).
Ok(Some((ecx.load_mir(instance.def, None)?, orig_instance)))
interp_ok(Some((ecx.load_mir(instance.def, None)?, orig_instance)))
}
fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
let msg = Symbol::intern(msg);
let span = ecx.find_closest_untracked_caller_location();
let (file, line, col) = ecx.location_triple_for_span(span);
Err(ConstEvalErrKind::Panic { msg, file, line, col }.into())
Err(ConstEvalErrKind::Panic { msg, file, line, col }).into()
}
fn call_intrinsic(
@ -464,7 +464,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
// Shared intrinsics.
if ecx.eval_intrinsic(instance, args, dest, target)? {
return Ok(None);
return interp_ok(None);
}
let intrinsic_name = ecx.tcx.item_name(instance.def_id());
@ -541,7 +541,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
"intrinsic `{intrinsic_name}` is not supported at compile-time"
);
}
return Ok(Some(ty::Instance {
return interp_ok(Some(ty::Instance {
def: ty::InstanceKind::Item(instance.def_id()),
args: instance.args,
}));
@ -550,7 +550,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
// Intrinsic is done, jump to next block.
ecx.return_to_block(target)?;
Ok(None)
interp_ok(None)
}
fn assert_panic(
@ -581,7 +581,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
}
}
};
Err(ConstEvalErrKind::AssertFailure(err).into())
Err(ConstEvalErrKind::AssertFailure(err)).into()
}
fn binary_ptr_op(
@ -652,7 +652,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
}
}
Ok(())
interp_ok(())
}
#[inline(always)]
@ -670,7 +670,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
if !ecx.recursion_limit.value_within_limit(ecx.stack().len() + 1) {
throw_exhaust!(StackFrameLimitReached)
} else {
Ok(frame)
interp_ok(frame)
}
}
@ -700,22 +700,22 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
if is_write {
// Write access. These are never allowed, but we give a targeted error message.
match alloc.mutability {
Mutability::Not => Err(err_ub!(WriteToReadOnly(alloc_id)).into()),
Mutability::Mut => Err(ConstEvalErrKind::ModifiedGlobal.into()),
Mutability::Not => throw_ub!(WriteToReadOnly(alloc_id)),
Mutability::Mut => Err(ConstEvalErrKind::ModifiedGlobal).into(),
}
} else {
// Read access. These are usually allowed, with some exceptions.
if machine.can_access_mut_global == CanAccessMutGlobal::Yes {
// Machine configuration allows us read from anything (e.g., `static` initializer).
Ok(())
interp_ok(())
} else if alloc.mutability == Mutability::Mut {
// Machine configuration does not allow us to read statics (e.g., `const`
// initializer).
Err(ConstEvalErrKind::ConstAccessesMutGlobal.into())
Err(ConstEvalErrKind::ConstAccessesMutGlobal).into()
} else {
// Immutable global, this read is fine.
assert_eq!(alloc.mutability, Mutability::Not);
Ok(())
interp_ok(())
}
}
}
@ -748,9 +748,9 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
// even when there is interior mutability.)
place.map_provenance(CtfeProvenance::as_shared_ref)
};
Ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout))
interp_ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout))
} else {
Ok(val.clone())
interp_ok(val.clone())
}
}
@ -763,20 +763,20 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
) -> InterpResult<'tcx> {
if range.size == Size::ZERO {
// Nothing to check.
return Ok(());
return interp_ok(());
}
// Reject writes through immutable pointers.
if immutable {
return Err(ConstEvalErrKind::WriteThroughImmutablePointer.into());
return Err(ConstEvalErrKind::WriteThroughImmutablePointer).into();
}
// Everything else is fine.
Ok(())
interp_ok(())
}
fn before_alloc_read(ecx: &InterpCx<'tcx, Self>, alloc_id: AllocId) -> InterpResult<'tcx> {
// Check if this is the currently evaluated static.
if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
return Err(ConstEvalErrKind::RecursiveStatic.into());
return Err(ConstEvalErrKind::RecursiveStatic).into();
}
// If this is another static, make sure we fire off the query to detect cycles.
// But only do that when checks for static recursion are enabled.
@ -788,7 +788,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
}
}
Ok(())
interp_ok(())
}
fn cached_union_data_range<'e>(

View file

@ -1,13 +1,12 @@
// Not in interpret to make sure we do not use private implementation details
use rustc_middle::mir::interpret::InterpErrorInfo;
use rustc_middle::query::{Key, TyCtxtAt};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_target::abi::VariantIdx;
use tracing::instrument;
use crate::interpret::{InterpCx, format_interp_error};
use crate::interpret::InterpCx;
mod dummy_machine;
mod error;
@ -33,17 +32,6 @@ pub(crate) enum ValTreeCreationError<'tcx> {
}
pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError<'tcx>>;
impl<'tcx> From<InterpErrorInfo<'tcx>> for ValTreeCreationError<'tcx> {
fn from(err: InterpErrorInfo<'tcx>) -> Self {
ty::tls::with(|tcx| {
bug!(
"Unexpected Undefined Behavior error during valtree construction: {}",
format_interp_error(tcx.dcx(), err),
)
})
}
}
#[instrument(skip(tcx), level = "debug")]
pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
tcx: TyCtxtAt<'tcx>,
@ -60,8 +48,8 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
return None;
}
ty::Adt(def, _) => {
let variant = ecx.read_discriminant(&op).ok()?;
let down = ecx.project_downcast(&op, variant).ok()?;
let variant = ecx.read_discriminant(&op).discard_err()?;
let down = ecx.project_downcast(&op, variant).discard_err()?;
(def.variants()[variant].fields.len(), Some(variant), down)
}
ty::Tuple(args) => (args.len(), None, op),
@ -70,7 +58,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
let fields_iter = (0..field_count)
.map(|i| {
let field_op = ecx.project_field(&down, i).ok()?;
let field_op = ecx.project_field(&down, i).discard_err()?;
let val = op_to_const(&ecx, &field_op, /* for diagnostics */ true);
Some((val, field_op.layout.ty))
})

View file

@ -92,7 +92,7 @@ fn const_to_valtree_inner<'tcx>(
Ok(ty::ValTree::zst())
}
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
let val = ecx.read_immediate(place)?;
let val = ecx.read_immediate(place).unwrap();
let val = val.to_scalar_int().unwrap();
*num_nodes += 1;
@ -114,7 +114,7 @@ fn const_to_valtree_inner<'tcx>(
// equality at compile-time (see `ptr_guaranteed_cmp`).
// However we allow those that are just integers in disguise.
// First, get the pointer. Remember it might be wide!
let val = ecx.read_immediate(place)?;
let val = ecx.read_immediate(place).unwrap();
// We could allow wide raw pointers where both sides are integers in the future,
// but for now we reject them.
if matches!(val.layout.abi, Abi::ScalarPair(..)) {
@ -135,7 +135,7 @@ fn const_to_valtree_inner<'tcx>(
ty::FnPtr(..) => Err(ValTreeCreationError::NonSupportedType(ty)),
ty::Ref(_, _, _) => {
let derefd_place = ecx.deref_pointer(place)?;
let derefd_place = ecx.deref_pointer(place).unwrap();
const_to_valtree_inner(ecx, &derefd_place, num_nodes)
}
@ -159,7 +159,7 @@ fn const_to_valtree_inner<'tcx>(
bug!("uninhabited types should have errored and never gotten converted to valtree")
}
let variant = ecx.read_discriminant(place)?;
let variant = ecx.read_discriminant(place).unwrap();
branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
}

View file

@ -15,8 +15,8 @@ use tracing::{info, instrument, trace};
use super::{
CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy,
Projectable, Provenance, ReturnAction, Scalar, StackPopCleanup, StackPopInfo, throw_ub,
throw_ub_custom, throw_unsup_format,
Projectable, Provenance, ReturnAction, Scalar, StackPopCleanup, StackPopInfo, interp_ok,
throw_ub, throw_ub_custom, throw_unsup_format,
};
use crate::fluent_generated as fluent;
@ -64,7 +64,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
arg: &FnArg<'tcx, M::Provenance>,
field: usize,
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
Ok(match arg {
interp_ok(match arg {
FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?),
FnArg::InPlace(mplace) => FnArg::InPlace(self.project_field(mplace, field)?),
})
@ -97,7 +97,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// another type.
let ty::Adt(def, args) = layout.ty.kind() else {
// Not an ADT, so definitely no NPO.
return Ok(layout);
return interp_ok(layout);
};
let inner = if self.tcx.is_diagnostic_item(sym::Option, def.did()) {
// The wrapped type is the only arg.
@ -111,10 +111,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
} else if rhs.is_1zst() {
lhs
} else {
return Ok(layout); // no NPO
return interp_ok(layout); // no NPO
}
} else {
return Ok(layout); // no NPO
return interp_ok(layout); // no NPO
};
// Check if the inner type is one of the NPO-guaranteed ones.
@ -126,7 +126,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Stop at NPO types so that we don't miss that attribute in the check below!
def.is_struct() && !is_npo(def)
});
Ok(match inner.ty.kind() {
interp_ok(match inner.ty.kind() {
ty::Ref(..) | ty::FnPtr(..) => {
// Option<&T> behaves like &T, and same for fn()
inner
@ -153,11 +153,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx, bool> {
// Fast path: equal types are definitely compatible.
if caller.ty == callee.ty {
return Ok(true);
return interp_ok(true);
}
// 1-ZST are compatible with all 1-ZST (and with nothing else).
if caller.is_1zst() || callee.is_1zst() {
return Ok(caller.is_1zst() && callee.is_1zst());
return interp_ok(caller.is_1zst() && callee.is_1zst());
}
// Unfold newtypes and NPO optimizations.
let unfold = |layout: TyAndLayout<'tcx>| {
@ -180,17 +180,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
_ => None,
};
if let (Some(caller), Some(callee)) = (thin_pointer(caller), thin_pointer(callee)) {
return Ok(caller == callee);
return interp_ok(caller == callee);
}
// For wide pointers we have to get the pointee type.
let pointee_ty = |ty: Ty<'tcx>| -> InterpResult<'tcx, Option<Ty<'tcx>>> {
// We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>`.
Ok(Some(match ty.kind() {
interp_ok(Some(match ty.kind() {
ty::Ref(_, ty, _) => *ty,
ty::RawPtr(ty, _) => *ty,
// We only accept `Box` with the default allocator.
_ if ty.is_box_global(*self.tcx) => ty.expect_boxed_ty(),
_ => return Ok(None),
_ => return interp_ok(None),
}))
};
if let (Some(caller), Some(callee)) = (pointee_ty(caller.ty)?, pointee_ty(callee.ty)?) {
@ -202,7 +202,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let normalize = |ty| self.tcx.normalize_erasing_regions(self.param_env, ty);
ty.ptr_metadata_ty(*self.tcx, normalize)
};
return Ok(meta_ty(caller) == meta_ty(callee));
return interp_ok(meta_ty(caller) == meta_ty(callee));
}
// Compatible integer types (in particular, usize vs ptr-sized-u32/u64).
@ -217,11 +217,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
};
if let (Some(caller), Some(callee)) = (int_ty(caller.ty), int_ty(callee.ty)) {
// This is okay if they are the same integer type.
return Ok(caller == callee);
return interp_ok(caller == callee);
}
// Fall back to exact equality.
Ok(caller == callee)
interp_ok(caller == callee)
}
fn check_argument_compat(
@ -235,13 +235,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Ensure that our checks imply actual ABI compatibility for this concrete call.
// (This can fail e.g. if `#[rustc_nonnull_optimization_guaranteed]` is used incorrectly.)
assert!(caller_abi.eq_abi(callee_abi));
Ok(true)
interp_ok(true)
} else {
trace!(
"check_argument_compat: incompatible ABIs:\ncaller: {:?}\ncallee: {:?}",
caller_abi, callee_abi
);
Ok(false)
interp_ok(false)
}
}
@ -266,7 +266,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if !already_live {
self.storage_live(callee_arg.as_local().unwrap())?;
}
return Ok(());
return interp_ok(());
}
// Find next caller arg.
let Some((caller_arg, caller_abi)) = caller_args.next() else {
@ -308,7 +308,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if let FnArg::InPlace(mplace) = caller_arg {
M::protect_in_place_function_argument(self, mplace)?;
}
Ok(())
interp_ok(())
}
/// The main entry point for creating a new stack frame: performs ABI checks and initializes
@ -536,7 +536,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
unwind,
);
} else {
Ok(())
interp_ok(())
}
}
ty::InstanceKind::VTableShim(..)
@ -561,7 +561,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
unwind,
)?
else {
return Ok(());
return interp_ok(());
};
// Special handling for the closure ABI: untuple the last argument.
@ -572,7 +572,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
trace!("init_fn_call: Will pass last argument by untupling");
Cow::from(
args.iter()
.map(|a| Ok(a.clone()))
.map(|a| interp_ok(a.clone()))
.chain(
(0..untuple_arg.layout().fields.count())
.map(|i| self.fn_arg_field(untuple_arg, i)),
@ -886,27 +886,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// this transmute.
res
} else {
Ok(())
interp_ok(())
};
// All right, now it is time to actually pop the frame.
let stack_pop_info = self.pop_stack_frame_raw(unwinding)?;
// Report error from return value copy, if any.
copy_ret_result?;
// An error here takes precedence over the copy error.
let (stack_pop_info, ()) = self.pop_stack_frame_raw(unwinding).and(copy_ret_result)?;
match stack_pop_info.return_action {
ReturnAction::Normal => {}
ReturnAction::NoJump => {
// The hook already did everything.
return Ok(());
return interp_ok(());
}
ReturnAction::NoCleanup => {
// If we are not doing cleanup, also skip everything else.
assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked");
assert!(!unwinding, "tried to skip cleanup during unwinding");
// Skip machine hook.
return Ok(());
return interp_ok(());
}
}
@ -931,7 +929,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.stack().is_empty(),
"only the bottommost frame can have StackPopCleanup::Root"
);
Ok(())
interp_ok(())
}
}
}

View file

@ -14,7 +14,8 @@ use tracing::trace;
use super::util::ensure_monomorphic_enough;
use super::{
FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, throw_ub, throw_ub_custom,
FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, interp_ok, throw_ub,
throw_ub_custom,
};
use crate::fluent_generated as fluent;
@ -157,7 +158,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.copy_op_allow_transmute(src, dest)?;
}
}
Ok(())
interp_ok(())
}
/// Handles 'IntToInt' and 'IntToFloat' casts.
@ -169,7 +170,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
assert!(src.layout.ty.is_integral() || src.layout.ty.is_char() || src.layout.ty.is_bool());
assert!(cast_to.ty.is_floating_point() || cast_to.ty.is_integral() || cast_to.ty.is_char());
Ok(ImmTy::from_scalar(
interp_ok(ImmTy::from_scalar(
self.cast_from_int_like(src.to_scalar(), src.layout, cast_to.ty)?,
cast_to,
))
@ -192,7 +193,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
FloatTy::F64 => self.cast_from_float(src.to_scalar().to_f64()?, cast_to.ty),
FloatTy::F128 => self.cast_from_float(src.to_scalar().to_f128()?, cast_to.ty),
};
Ok(ImmTy::from_scalar(val, cast_to))
interp_ok(ImmTy::from_scalar(val, cast_to))
}
/// Handles 'FnPtrToPtr' and 'PtrToPtr' casts.
@ -206,14 +207,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Handle casting any ptr to raw ptr (might be a fat ptr).
if cast_to.size == src.layout.size {
// Thin or fat pointer that just has the ptr kind of target type changed.
return Ok(ImmTy::from_immediate(**src, cast_to));
return interp_ok(ImmTy::from_immediate(**src, cast_to));
} else {
// Casting the metadata away from a fat ptr.
assert_eq!(src.layout.size, 2 * self.pointer_size());
assert_eq!(cast_to.size, self.pointer_size());
assert!(src.layout.ty.is_unsafe_ptr());
return match **src {
Immediate::ScalarPair(data, _) => Ok(ImmTy::from_scalar(data, cast_to)),
Immediate::ScalarPair(data, _) => interp_ok(ImmTy::from_scalar(data, cast_to)),
Immediate::Scalar(..) => span_bug!(
self.cur_span(),
"{:?} input to a fat-to-thin cast ({} -> {})",
@ -240,7 +241,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Ok(ptr) => M::expose_ptr(self, ptr)?,
Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP.
};
Ok(ImmTy::from_scalar(self.cast_from_int_like(scalar, src.layout, cast_to.ty)?, cast_to))
interp_ok(ImmTy::from_scalar(
self.cast_from_int_like(scalar, src.layout, cast_to.ty)?,
cast_to,
))
}
pub fn pointer_with_exposed_provenance_cast(
@ -258,7 +262,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Then turn address into pointer.
let ptr = M::ptr_from_addr_cast(self, addr)?;
Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(ptr, self), cast_to))
interp_ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(ptr, self), cast_to))
}
/// Low-level cast helper function. This works directly on scalars and can take 'int-like' input
@ -280,7 +284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
_ => span_bug!(self.cur_span(), "invalid int-like cast from {}", src_layout.ty),
};
Ok(match *cast_ty.kind() {
interp_ok(match *cast_ty.kind() {
// int -> int
Int(_) | Uint(_) => {
let size = match *cast_ty.kind() {
@ -505,7 +509,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.unsize_into(&src_field, cast_ty_field, &dst_field)?;
}
}
Ok(())
interp_ok(())
}
_ => {
// Do not ICE if we are not monomorphic enough.

View file

@ -7,7 +7,8 @@ use rustc_target::abi::{self, TagEncoding, VariantIdx, Variants};
use tracing::{instrument, trace};
use super::{
ImmTy, InterpCx, InterpResult, Machine, Projectable, Scalar, Writeable, err_ub, throw_ub,
ImmTy, InterpCx, InterpResult, Machine, Projectable, Scalar, Writeable, err_ub, interp_ok,
throw_ub,
};
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
@ -48,7 +49,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if actual_variant != variant_index {
throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty });
}
Ok(())
interp_ok(())
}
}
}
@ -89,7 +90,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
throw_ub!(UninhabitedEnumVariantRead(index))
}
}
return Ok(index);
return interp_ok(index);
}
Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
(tag, tag_encoding, tag_field)
@ -205,7 +206,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if op.layout().for_variant(self, index).abi.is_uninhabited() {
throw_ub!(UninhabitedEnumVariantRead(index))
}
Ok(index)
interp_ok(index)
}
pub fn discriminant_for_variant(
@ -226,7 +227,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Scalar::from_uint(variant.as_u32(), discr_layout.size)
}
};
Ok(ImmTy::from_scalar(discr_value, discr_layout))
interp_ok(ImmTy::from_scalar(discr_value, discr_layout))
}
/// Computes how to write the tag of a given variant of enum `ty`:
@ -247,7 +248,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// discriminant is encoded implicitly, so any attempt to write
// the wrong discriminant for a `Single` enum will reliably
// result in UB.
Ok(None)
interp_ok(None)
}
abi::Variants::Multiple {
@ -265,7 +266,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let tag_size = tag_layout.size(self);
let tag_val = tag_size.truncate(discr_val);
let tag = ScalarInt::try_from_uint(tag_val, tag_size).unwrap();
Ok(Some((tag, tag_field)))
interp_ok(Some((tag, tag_field)))
}
abi::Variants::Multiple {
@ -274,7 +275,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
} if untagged_variant == variant_index => {
// The untagged variant is implicitly encoded simply by having a
// value that is outside the niche variants.
Ok(None)
interp_ok(None)
}
abi::Variants::Multiple {
@ -299,7 +300,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let tag = self
.binary_op(mir::BinOp::Add, &variant_index_relative_val, &niche_start_val)?
.to_scalar_int()?;
Ok(Some((tag, tag_field)))
interp_ok(Some((tag, tag_field)))
}
}
}

View file

@ -19,9 +19,9 @@ use rustc_trait_selection::traits::ObligationCtxt;
use tracing::{debug, instrument, trace};
use super::{
Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlaceMeta,
Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval,
throw_inval, throw_ub, throw_ub_custom,
Frame, FrameInfo, GlobalId, InterpError, InterpErrorInfo, InterpResult, MPlaceTy, Machine,
MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance,
err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom,
};
use crate::{ReportErrorExt, fluent_generated as fluent, util};
@ -73,7 +73,7 @@ where
}
impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> {
type LayoutOfResult = InterpResult<'tcx, TyAndLayout<'tcx>>;
type LayoutOfResult = Result<TyAndLayout<'tcx>, InterpError<'tcx>>;
#[inline]
fn layout_tcx_at_span(&self) -> Span {
@ -82,29 +82,24 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> {
}
#[inline]
fn handle_layout_err(
&self,
err: LayoutError<'tcx>,
_: Span,
_: Ty<'tcx>,
) -> InterpErrorInfo<'tcx> {
err_inval!(Layout(err)).into()
fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> InterpError<'tcx> {
err_inval!(Layout(err))
}
}
impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> {
type FnAbiOfResult = InterpResult<'tcx, &'tcx FnAbi<'tcx, Ty<'tcx>>>;
type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpError<'tcx>>;
fn handle_fn_abi_err(
&self,
err: FnAbiError<'tcx>,
_span: Span,
_fn_abi_request: FnAbiRequest<'tcx>,
) -> InterpErrorInfo<'tcx> {
) -> InterpError<'tcx> {
match err {
FnAbiError::Layout(err) => err_inval!(Layout(err)).into(),
FnAbiError::Layout(err) => err_inval!(Layout(err)),
FnAbiError::AdjustForForeignAbi(err) => {
err_inval!(FnAbiAdjustForForeignAbi(err)).into()
err_inval!(FnAbiAdjustForForeignAbi(err))
}
}
}
@ -160,7 +155,7 @@ pub(super) fn from_known_layout<'tcx>(
);
}
}
Ok(known_layout)
interp_ok(known_layout)
}
}
}
@ -262,7 +257,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if let Some(err) = body.tainted_by_errors {
throw_inval!(AlreadyReported(ReportedErrorInfo::tainted_by_errors(err)));
}
Ok(body)
interp_ok(body)
}
/// Call this on things you got out of the MIR (so it is as generic as the current
@ -305,7 +300,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
trace!("param_env: {:#?}", self.param_env);
trace!("args: {:#?}", args);
match ty::Instance::try_resolve(*self.tcx, self.param_env, def, args) {
Ok(Some(instance)) => Ok(instance),
Ok(Some(instance)) => interp_ok(instance),
Ok(None) => throw_inval!(TooGeneric),
// FIXME(eddyb) this could be a bit more specific than `AlreadyReported`.
@ -401,7 +396,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
layout: &TyAndLayout<'tcx>,
) -> InterpResult<'tcx, Option<(Size, Align)>> {
if layout.is_sized() {
return Ok(Some((layout.size, layout.align.abi)));
return interp_ok(Some((layout.size, layout.align.abi)));
}
match layout.ty.kind() {
ty::Adt(..) | ty::Tuple(..) => {
@ -425,7 +420,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
else {
// A field with an extern type. We don't know the actual dynamic size
// or the alignment.
return Ok(None);
return interp_ok(None);
};
// # First compute the dynamic alignment
@ -456,12 +451,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if full_size > self.max_size_of_val() {
throw_ub!(InvalidMeta(InvalidMetaKind::TooBig));
}
Ok(Some((full_size, full_align)))
interp_ok(Some((full_size, full_align)))
}
ty::Dynamic(expected_trait, _, ty::Dyn) => {
let vtable = metadata.unwrap_meta().to_pointer(self)?;
// Read size and align from vtable (already checks size).
Ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?))
interp_ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?))
}
ty::Slice(_) | ty::Str => {
@ -474,10 +469,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if size > self.max_size_of_val() {
throw_ub!(InvalidMeta(InvalidMetaKind::SliceTooBig));
}
Ok(Some((size, elem.align.abi)))
interp_ok(Some((size, elem.align.abi)))
}
ty::Foreign(_) => Ok(None),
ty::Foreign(_) => interp_ok(None),
_ => span_bug!(self.cur_span(), "size_and_align_of::<{}> not supported", layout.ty),
}
@ -503,7 +498,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
pub fn return_to_block(&mut self, target: Option<mir::BasicBlock>) -> InterpResult<'tcx> {
if let Some(target) = target {
self.go_to_block(target);
Ok(())
interp_ok(())
} else {
throw_ub!(Unreachable)
}
@ -530,10 +525,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
M::unwind_terminate(self, reason)?;
// This might have pushed a new stack frame, or it terminated execution.
// Either way, `loc` will not be updated.
return Ok(());
return interp_ok(());
}
};
Ok(())
interp_ok(())
}
/// Call a query that can return `ErrorHandled`. Should be used for statics and other globals.

View file

@ -26,7 +26,9 @@ use rustc_span::def_id::LocalDefId;
use rustc_span::sym;
use tracing::{instrument, trace};
use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub};
use super::{
AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok,
};
use crate::const_eval;
use crate::errors::NestedStaticInThreadLocal;
@ -307,7 +309,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>
) -> InterpResult<'tcx, ()> {
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
// The constant is already in global memory. Do nothing.
return Ok(());
return interp_ok(());
}
// Move allocation to `tcx`.
if let Some(_) =
@ -318,7 +320,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
panic!("`intern_const_alloc_for_constprop` called on allocation with nested provenance")
}
Ok(())
interp_ok(())
}
impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> {
@ -342,6 +344,6 @@ impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> {
panic!("`intern_with_temp_alloc` with nested allocations");
}
}
Ok(alloc_id)
interp_ok(alloc_id)
}
}

View file

@ -18,7 +18,7 @@ use super::util::ensure_monomorphic_enough;
use super::{
Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult,
MPlaceTy, Machine, OpTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval,
err_ub_custom, err_unsup_format, throw_inval, throw_ub_custom, throw_ub_format,
err_ub_custom, err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
};
use crate::fluent_generated as fluent;
@ -39,7 +39,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
) -> InterpResult<'tcx, ConstValue<'tcx>> {
let tp_ty = args.type_at(0);
let name = tcx.item_name(def_id);
Ok(match name {
interp_ok(match name {
sym::type_name => {
ensure_monomorphic_enough(tcx, tp_ty)?;
let alloc = alloc_type_name(tcx, tp_ty);
@ -329,6 +329,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
fluent::const_eval_offset_from_different_allocations,
name = intrinsic_name,
)
.into()
})?;
// Perform division by size to compute return value.
@ -378,7 +379,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
M::panic_nounwind(self, &msg)?;
// Skip the `return_to_block` at the end (we panicked, we do not return).
return Ok(true);
return interp_ok(true);
}
}
sym::simd_insert => {
@ -438,12 +439,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
// Unsupported intrinsic: skip the return_to_block below.
_ => return Ok(false),
_ => return interp_ok(false),
}
trace!("{:?}", self.dump_place(&dest.clone().into()));
self.return_to_block(ret)?;
Ok(true)
interp_ok(true)
}
pub(super) fn eval_nondiverging_intrinsic(
@ -457,7 +458,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if !cond {
throw_ub_custom!(fluent::const_eval_assume_false);
}
Ok(())
interp_ok(())
}
NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping {
count,
@ -499,7 +500,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
_ => bug!("not a numeric intrinsic: {}", name),
};
Ok(Scalar::from_uint(bits_out, ret_layout.size))
interp_ok(Scalar::from_uint(bits_out, ret_layout.size))
}
pub fn exact_div(
@ -540,7 +541,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let (val, overflowed) =
self.binary_op(mir_op.wrapping_to_overflowing().unwrap(), l, r)?.to_scalar_pair();
Ok(if overflowed.to_bool()? {
interp_ok(if overflowed.to_bool()? {
let size = l.layout.size;
if l.layout.abi.is_signed() {
// For signed ints the saturated value depends on the sign of the first
@ -582,7 +583,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// The offset must be in bounds starting from `ptr`.
self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?;
// This also implies that there is no overflow, so we are done.
Ok(ptr.wrapping_signed_offset(offset_bytes, self))
interp_ok(ptr.wrapping_signed_offset(offset_bytes, self))
}
/// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
@ -628,7 +629,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.copy_op(&right, &left)?;
self.copy_op(&temp, &right)?;
self.deallocate_ptr(temp.ptr(), None, kind)?;
Ok(())
interp_ok(())
}
pub fn write_bytes_intrinsic(
@ -669,7 +670,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// `Ordering`'s discriminants are -1/0/+1, so casting does the right thing.
let result = Ord::cmp(left_bytes, right_bytes) as i32;
Ok(Scalar::from_i32(result))
interp_ok(Scalar::from_i32(result))
}
pub(crate) fn raw_eq_intrinsic(
@ -687,13 +688,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
this.check_ptr_align(ptr, layout.align.abi)?;
let Some(alloc_ref) = self.get_ptr_alloc(ptr, layout.size)? else {
// zero-sized access
return Ok(&[]);
return interp_ok(&[]);
};
alloc_ref.get_bytes_strip_provenance()
};
let lhs_bytes = get_bytes(self, lhs)?;
let rhs_bytes = get_bytes(self, rhs)?;
Ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
}
}

View file

@ -20,7 +20,8 @@ use rustc_target::spec::abi::Abi as CallAbi;
use super::{
AllocBytes, AllocId, AllocKind, AllocRange, Allocation, CTFE_ALLOC_SALT, ConstAllocation,
CtfeProvenance, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, MemoryKind,
Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, throw_unsup, throw_unsup_format,
Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, interp_ok, throw_unsup,
throw_unsup_format,
};
/// Data returned by [`Machine::after_stack_pop`], and consumed by
@ -185,7 +186,7 @@ pub trait Machine<'tcx>: Sized {
ecx: &InterpCx<'tcx, Self>,
instance: ty::InstanceKind<'tcx>,
) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
Ok(ecx.tcx.instance_mir(instance))
interp_ok(ecx.tcx.instance_mir(instance))
}
/// Entry point to all function calls.
@ -280,7 +281,7 @@ pub trait Machine<'tcx>: Sized {
/// Called before a basic block terminator is executed.
#[inline]
fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Determines the result of a `NullaryOp::UbChecks` invocation.
@ -290,7 +291,7 @@ pub trait Machine<'tcx>: Sized {
/// You can use this to detect long or endlessly running programs.
#[inline]
fn increment_const_eval_counter(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Called before a global allocation is accessed.
@ -304,7 +305,7 @@ pub trait Machine<'tcx>: Sized {
_static_def_id: Option<DefId>,
_is_write: bool,
) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Return the `AllocId` for the given thread-local static in the current thread.
@ -422,7 +423,7 @@ pub trait Machine<'tcx>: Sized {
_prov: (AllocId, Self::ProvenanceExtra),
_range: AllocRange,
) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Hook for performing extra checks on any memory read access,
@ -433,7 +434,7 @@ pub trait Machine<'tcx>: Sized {
/// Used to prevent statics from self-initializing by reading from their own memory
/// as it is being initialized.
fn before_alloc_read(_ecx: &InterpCx<'tcx, Self>, _alloc_id: AllocId) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Hook for performing extra checks on a memory write access.
@ -446,7 +447,7 @@ pub trait Machine<'tcx>: Sized {
_prov: (AllocId, Self::ProvenanceExtra),
_range: AllocRange,
) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Hook for performing extra operations on a memory deallocation.
@ -460,7 +461,7 @@ pub trait Machine<'tcx>: Sized {
_align: Align,
_kind: MemoryKind<Self::MemoryKind>,
) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Executes a retagging operation for a single pointer.
@ -471,7 +472,7 @@ pub trait Machine<'tcx>: Sized {
_kind: mir::RetagKind,
val: &ImmTy<'tcx, Self::Provenance>,
) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> {
Ok(val.clone())
interp_ok(val.clone())
}
/// Executes a retagging operation on a compound value.
@ -482,7 +483,7 @@ pub trait Machine<'tcx>: Sized {
_kind: mir::RetagKind,
_place: &PlaceTy<'tcx, Self::Provenance>,
) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Called on places used for in-place function argument and return value handling.
@ -516,7 +517,7 @@ pub trait Machine<'tcx>: Sized {
/// Called immediately after a stack frame got pushed and its locals got initialized.
fn after_stack_push(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Called just before the return value is copied to the caller-provided return place.
@ -524,7 +525,7 @@ pub trait Machine<'tcx>: Sized {
_ecx: &InterpCx<'tcx, Self>,
_frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>,
) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Called immediately after a stack frame got popped, but before jumping back to the caller.
@ -537,14 +538,14 @@ pub trait Machine<'tcx>: Sized {
) -> InterpResult<'tcx, ReturnAction> {
// By default, we do not support unwinding from panics
assert!(!unwinding);
Ok(ReturnAction::Normal)
interp_ok(ReturnAction::Normal)
}
/// Called immediately after an "immediate" local variable is read
/// (i.e., this is called for reads that do not end up accessing addressable memory).
#[inline(always)]
fn after_local_read(_ecx: &InterpCx<'tcx, Self>, _local: mir::Local) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Called immediately after an "immediate" local variable is assigned a new value
@ -556,7 +557,7 @@ pub trait Machine<'tcx>: Sized {
_local: mir::Local,
_storage_live: bool,
) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Called immediately after actual memory was allocated for a local
@ -567,7 +568,7 @@ pub trait Machine<'tcx>: Sized {
_local: mir::Local,
_mplace: &MPlaceTy<'tcx, Self::Provenance>,
) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Evaluate the given constant. The `eval` function will do all the required evaluation,
@ -645,7 +646,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
) -> InterpResult<$tcx> {
// For now we don't do any checking here. We can't use `tcx.sess` because that can differ
// between crates, and we need to ensure that const-eval always behaves the same.
Ok(())
interp_ok(())
}
#[inline(always)]
@ -665,7 +666,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
// unsound differences in evaluating the same constant at different instantiation sites.
Ok(true)
interp_ok(true)
}
#[inline(always)]
@ -675,7 +676,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
alloc: &'b Allocation,
) -> InterpResult<$tcx, Cow<'b, Allocation<Self::Provenance>>> {
// Overwrite default implementation: no need to adjust anything.
Ok(Cow::Borrowed(alloc))
interp_ok(Cow::Borrowed(alloc))
}
fn init_alloc_extra(
@ -685,7 +686,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
_size: Size,
_align: Align,
) -> InterpResult<$tcx, Self::AllocExtra> {
Ok(())
interp_ok(())
}
fn extern_static_pointer(
@ -693,7 +694,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
def_id: DefId,
) -> InterpResult<$tcx, Pointer> {
// Use the `AllocId` associated with the `DefId`. Any actual *access* will fail.
Ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id).into(), Size::ZERO))
interp_ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id).into(), Size::ZERO))
}
#[inline(always)]
@ -702,7 +703,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
ptr: Pointer<CtfeProvenance>,
_kind: Option<MemoryKind<Self::MemoryKind>>,
) -> InterpResult<$tcx, Pointer<CtfeProvenance>> {
Ok(ptr)
interp_ok(ptr)
}
#[inline(always)]
@ -713,7 +714,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
// Allow these casts, but make the pointer not dereferenceable.
// (I.e., they behave like transmutation.)
// This is correct because no pointers can ever be exposed in compile-time evaluation.
Ok(Pointer::from_addr_invalid(addr))
interp_ok(Pointer::from_addr_invalid(addr))
}
#[inline(always)]

View file

@ -23,7 +23,7 @@ use tracing::{debug, instrument, trace};
use super::{
AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg, CheckInAllocMsg,
CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer,
PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, throw_ub,
PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub,
throw_ub_custom, throw_unsup, throw_unsup_format,
};
use crate::fluent_generated as fluent;
@ -82,7 +82,7 @@ pub enum FnVal<'tcx, Other> {
impl<'tcx, Other> FnVal<'tcx, Other> {
pub fn as_instance(self) -> InterpResult<'tcx, Instance<'tcx>> {
match self {
FnVal::Instance(instance) => Ok(instance),
FnVal::Instance(instance) => interp_ok(instance),
FnVal::Other(_) => {
throw_unsup_format!("'foreign' function pointers are not supported in this context")
}
@ -284,7 +284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.mem_copy(ptr, new_ptr.into(), old_size.min(new_size), /*nonoverlapping*/ true)?;
self.deallocate_ptr(ptr, old_size_and_align, kind)?;
Ok(new_ptr)
interp_ok(new_ptr)
}
#[instrument(skip(self), level = "debug")]
@ -330,8 +330,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
)
}
None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccessTest)),
}
.into());
})
.into();
};
if alloc.mutability.is_not() {
@ -376,7 +376,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
bug!("Nothing can be deallocated twice");
}
Ok(())
interp_ok(())
}
/// Internal helper function to determine the allocation and offset of a pointer (if any).
@ -395,7 +395,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|this, alloc_id, offset, prov| {
let (size, align) = this
.get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccessTest)?;
Ok((size, align, (alloc_id, offset, prov)))
interp_ok((size, align, (alloc_id, offset, prov)))
},
)
}
@ -412,9 +412,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let size = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
Self::check_and_deref_ptr(self, ptr, size, msg, |this, alloc_id, _, _| {
let (size, align) = this.get_live_alloc_size_and_align(alloc_id, msg)?;
Ok((size, align, ()))
interp_ok((size, align, ()))
})?;
Ok(())
interp_ok(())
}
/// Check whether the given pointer points to live memory for a signed amount of bytes.
@ -428,9 +428,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx> {
Self::check_and_deref_ptr(self, ptr, size, msg, |this, alloc_id, _, _| {
let (size, align) = this.get_live_alloc_size_and_align(alloc_id, msg)?;
Ok((size, align, ()))
interp_ok((size, align, ()))
})?;
Ok(())
interp_ok(())
}
/// Low-level helper function to check if a ptr is in-bounds and potentially return a reference
@ -455,10 +455,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx, Option<T>> {
// Everything is okay with size 0.
if size == 0 {
return Ok(None);
return interp_ok(None);
}
Ok(match this.borrow().ptr_try_get_alloc_id(ptr, size) {
interp_ok(match this.borrow().ptr_try_get_alloc_id(ptr, size) {
Err(addr) => {
// We couldn't get a proper allocation.
throw_ub!(DanglingIntPointer { addr, inbounds_size: size, msg });
@ -498,7 +498,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if let Some(misaligned) = misaligned {
throw_ub!(AlignmentCheckFailed(misaligned, msg))
}
Ok(())
interp_ok(())
}
pub(super) fn is_ptr_misaligned(
@ -634,7 +634,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// `get_global_alloc` that we can actually use directly without inserting anything anywhere.
// So the error type is `InterpResult<'tcx, &Allocation<M::Provenance>>`.
let a = self.memory.alloc_map.get_or(id, || {
let alloc = self.get_global_alloc(id, /*is_write*/ false).map_err(Err)?;
// We have to funnel the `InterpErrorInfo` through a `Result` to match the `get_or` API,
// so we use `report_err` for that.
let alloc = self.get_global_alloc(id, /*is_write*/ false).report_err().map_err(Err)?;
match alloc {
Cow::Borrowed(alloc) => {
// We got a ref, cheaply return that as an "error" so that the
@ -653,8 +655,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
});
// Now unpack that funny error type
match a {
Ok(a) => Ok(&a.1),
Err(a) => a,
Ok(a) => interp_ok(&a.1),
Err(a) => a.into(),
}
}
@ -662,7 +664,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// The caller is responsible for calling the access hooks!
pub fn get_alloc_bytes_unchecked_raw(&self, id: AllocId) -> InterpResult<'tcx, *const u8> {
let alloc = self.get_alloc_raw(id)?;
Ok(alloc.get_bytes_unchecked_raw())
interp_ok(alloc.get_bytes_unchecked_raw())
}
/// Bounds-checked *but not align-checked* allocation access.
@ -680,7 +682,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
CheckInAllocMsg::MemoryAccessTest,
|this, alloc_id, offset, prov| {
let alloc = this.get_alloc_raw(alloc_id)?;
Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
},
)?;
// We want to call the hook on *all* accesses that involve an AllocId, including zero-sized
@ -703,20 +705,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
range,
)?;
}
Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
interp_ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
} else {
Ok(None)
interp_ok(None)
}
}
/// Return the `extra` field of the given allocation.
pub fn get_alloc_extra<'a>(&'a self, id: AllocId) -> InterpResult<'tcx, &'a M::AllocExtra> {
Ok(&self.get_alloc_raw(id)?.extra)
interp_ok(&self.get_alloc_raw(id)?.extra)
}
/// Return the `mutability` field of the given allocation.
pub fn get_alloc_mutability<'a>(&'a self, id: AllocId) -> InterpResult<'tcx, Mutability> {
Ok(self.get_alloc_raw(id)?.mutability)
interp_ok(self.get_alloc_raw(id)?.mutability)
}
/// Gives raw mutable access to the `Allocation`, without bounds or alignment checks.
@ -750,7 +752,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if alloc.mutability.is_not() {
throw_ub!(WriteToReadOnly(id))
}
Ok((alloc, &mut self.machine))
interp_ok((alloc, &mut self.machine))
}
/// Gives raw, mutable access to the `Allocation` address, without bounds or alignment checks.
@ -760,7 +762,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
id: AllocId,
) -> InterpResult<'tcx, *mut u8> {
let alloc = self.get_alloc_raw_mut(id)?.0;
Ok(alloc.get_bytes_unchecked_raw_mut())
interp_ok(alloc.get_bytes_unchecked_raw_mut())
}
/// Bounds-checked *but not align-checked* allocation access.
@ -781,7 +783,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
CheckInAllocMsg::MemoryAccessTest,
|this, alloc_id, offset, prov| {
let (alloc, machine) = this.get_alloc_raw_mut(alloc_id)?;
Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine)))
interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine)))
},
)?;
@ -790,9 +792,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if !validation_in_progress {
M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?;
}
Ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id }))
interp_ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id }))
} else {
Ok(None)
interp_ok(None)
}
}
@ -802,7 +804,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
id: AllocId,
) -> InterpResult<'tcx, (&'a mut M::AllocExtra, &'a mut M)> {
let (alloc, machine) = self.get_alloc_raw_mut(id)?;
Ok((&mut alloc.extra, machine))
interp_ok((&mut alloc.extra, machine))
}
/// Check whether an allocation is live. This is faster than calling
@ -904,7 +906,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if matches!(kind, AllocKind::Dead) {
throw_ub!(PointerUseAfterFree(id, msg))
}
Ok((size, align))
interp_ok((size, align))
}
fn get_fn_alloc(&self, id: AllocId) -> Option<FnVal<'tcx, M::ExtraFnVal>> {
@ -928,7 +930,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset)))
}
self.get_fn_alloc(alloc_id)
.ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into())
.ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))))
.into()
}
/// Get the dynamic type of the given vtable pointer.
@ -951,12 +954,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if let Some(expected_dyn_type) = expected_trait {
self.check_vtable_for_type(vtable_dyn_type, expected_dyn_type)?;
}
Ok(ty)
interp_ok(ty)
}
pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not;
Ok(())
interp_ok(())
}
/// Create a lazy debug printer that prints the given allocation and all allocations it points
@ -1144,10 +1147,11 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>
pub fn write_scalar(&mut self, range: AllocRange, val: Scalar<Prov>) -> InterpResult<'tcx> {
let range = self.range.subrange(range);
debug!("write_scalar at {:?}{range:?}: {val:?}", self.alloc_id);
Ok(self
.alloc
self.alloc
.write_scalar(&self.tcx, range, val)
.map_err(|e| e.to_interp_error(self.alloc_id))?)
.map_err(|e| e.to_interp_error(self.alloc_id))
.into()
}
/// `offset` is relative to this allocation reference, not the base of the allocation.
@ -1158,26 +1162,27 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>
/// Mark the given sub-range (relative to this allocation reference) as uninitialized.
pub fn write_uninit(&mut self, range: AllocRange) -> InterpResult<'tcx> {
let range = self.range.subrange(range);
Ok(self
.alloc
self.alloc
.write_uninit(&self.tcx, range)
.map_err(|e| e.to_interp_error(self.alloc_id))?)
.map_err(|e| e.to_interp_error(self.alloc_id))
.into()
}
/// Mark the entire referenced range as uninitialized
pub fn write_uninit_full(&mut self) -> InterpResult<'tcx> {
Ok(self
.alloc
self.alloc
.write_uninit(&self.tcx, self.range)
.map_err(|e| e.to_interp_error(self.alloc_id))?)
.map_err(|e| e.to_interp_error(self.alloc_id))
.into()
}
/// Remove all provenance in the reference range.
pub fn clear_provenance(&mut self) -> InterpResult<'tcx> {
Ok(self
.alloc
self.alloc
.clear_provenance(&self.tcx, self.range)
.map_err(|e| e.to_interp_error(self.alloc_id))?)
.map_err(|e| e.to_interp_error(self.alloc_id))
.into()
}
}
@ -1189,12 +1194,10 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Pr
read_provenance: bool,
) -> InterpResult<'tcx, Scalar<Prov>> {
let range = self.range.subrange(range);
let res = self
.alloc
self.alloc
.read_scalar(&self.tcx, range, read_provenance)
.map_err(|e| e.to_interp_error(self.alloc_id))?;
debug!("read_scalar at {:?}{range:?}: {res:?}", self.alloc_id);
Ok(res)
.map_err(|e| e.to_interp_error(self.alloc_id))
.into()
}
/// `range` is relative to this allocation reference, not the base of the allocation.
@ -1212,10 +1215,10 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Pr
/// `range` is relative to this allocation reference, not the base of the allocation.
pub fn get_bytes_strip_provenance<'b>(&'b self) -> InterpResult<'tcx, &'a [u8]> {
Ok(self
.alloc
self.alloc
.get_bytes_strip_provenance(&self.tcx, self.range)
.map_err(|e| e.to_interp_error(self.alloc_id))?)
.map_err(|e| e.to_interp_error(self.alloc_id))
.into()
}
/// Returns whether the allocation has provenance anywhere in the range of the `AllocRef`.
@ -1236,14 +1239,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx, &[u8]> {
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size)? else {
// zero-sized access
return Ok(&[]);
return interp_ok(&[]);
};
// Side-step AllocRef and directly access the underlying bytes more efficiently.
// (We are staying inside the bounds here so all is good.)
Ok(alloc_ref
.alloc
.get_bytes_strip_provenance(&alloc_ref.tcx, alloc_ref.range)
.map_err(|e| e.to_interp_error(alloc_ref.alloc_id))?)
interp_ok(
alloc_ref
.alloc
.get_bytes_strip_provenance(&alloc_ref.tcx, alloc_ref.range)
.map_err(|e| e.to_interp_error(alloc_ref.alloc_id))?,
)
}
/// Writes the given stream of bytes into memory.
@ -1263,7 +1268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let Some(alloc_ref) = self.get_ptr_alloc_mut(ptr, size)? else {
// zero-sized access
assert_matches!(src.next(), None, "iterator said it was empty but returned an element");
return Ok(());
return interp_ok(());
};
// Side-step AllocRef and directly access the underlying bytes more efficiently.
@ -1279,7 +1284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
*dest = src.next().expect("iterator was shorter than it said it would be");
}
assert_matches!(src.next(), None, "iterator was longer than it said it would be");
Ok(())
interp_ok(())
}
pub fn mem_copy(
@ -1316,7 +1321,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Source alloc preparations and access hooks.
let Some((src_alloc_id, src_offset, src_prov)) = src_parts else {
// Zero-sized *source*, that means dest is also zero-sized and we have nothing to do.
return Ok(());
return interp_ok(());
};
let src_alloc = self.get_alloc_raw(src_alloc_id)?;
let src_range = alloc_range(src_offset, size);
@ -1332,7 +1337,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// We already did the source checks and called the hooks so we are good to return early.
let Some((dest_alloc_id, dest_offset, dest_prov)) = dest_parts else {
// Zero-sized *destination*.
return Ok(());
return interp_ok(());
};
// Prepare getting source provenance.
@ -1375,7 +1380,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
.write_uninit(&tcx, dest_range)
.map_err(|e| e.to_interp_error(dest_alloc_id))?;
// We can forget about the provenance, this is all not initialized anyway.
return Ok(());
return interp_ok(());
}
// SAFE: The above indexing would have panicked if there weren't at least `size` bytes
@ -1432,7 +1437,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// copy the provenance to the destination
dest_alloc.provenance_apply_copy(provenance);
Ok(())
interp_ok(())
}
}
@ -1441,7 +1446,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// Test if this value might be null.
/// If the machine does not support ptr-to-int casts, this is conservative.
pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<'tcx, bool> {
Ok(match scalar.try_to_scalar_int() {
interp_ok(match scalar.try_to_scalar_int() {
Ok(int) => int.is_null(),
Err(_) => {
// Can only happen during CTFE.
@ -1508,13 +1513,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
ptr: Pointer<Option<M::Provenance>>,
size: i64,
) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> {
self.ptr_try_get_alloc_id(ptr, size).map_err(|offset| {
err_ub!(DanglingIntPointer {
addr: offset,
inbounds_size: size,
msg: CheckInAllocMsg::InboundsTest
self.ptr_try_get_alloc_id(ptr, size)
.map_err(|offset| {
err_ub!(DanglingIntPointer {
addr: offset,
inbounds_size: size,
msg: CheckInAllocMsg::InboundsTest
})
})
.into()
})
}
}

View file

@ -16,7 +16,7 @@ use tracing::trace;
use super::{
CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode,
PlaceTy, Pointer, Projectable, Provenance, Scalar, alloc_range, err_ub, from_known_layout,
mir_assign_valid_types, throw_ub,
interp_ok, mir_assign_valid_types, throw_ub,
};
/// An `Immediate` represents a single immediate self-contained Rust value.
@ -149,7 +149,7 @@ impl<Prov: Provenance> Immediate<Prov> {
}
Immediate::Uninit => {}
}
Ok(())
interp_ok(())
}
}
@ -307,7 +307,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
data_size: s.size().bytes(),
}));
}
Ok(s)
interp_ok(s)
}
#[inline]
@ -430,7 +430,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
ecx: &InterpCx<'tcx, M>,
) -> InterpResult<'tcx, Self> {
assert_matches!(meta, MemPlaceMeta::None); // we can't store this anywhere anyway
Ok(self.offset_(offset, layout, ecx))
interp_ok(self.offset_(offset, layout, ecx))
}
#[inline(always)]
@ -438,7 +438,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
&self,
_ecx: &InterpCx<'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.clone().into())
interp_ok(self.clone().into())
}
}
@ -514,11 +514,13 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
ecx: &InterpCx<'tcx, M>,
) -> InterpResult<'tcx, Self> {
match self.as_mplace_or_imm() {
Left(mplace) => Ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into()),
Left(mplace) => {
interp_ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into())
}
Right(imm) => {
assert_matches!(meta, MemPlaceMeta::None); // no place to store metadata here
// Every part of an uninit is uninit.
Ok(imm.offset_(offset, layout, ecx).into())
interp_ok(imm.offset_(offset, layout, ecx).into())
}
}
}
@ -528,7 +530,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
&self,
_ecx: &InterpCx<'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.clone())
interp_ok(self.clone())
}
}
@ -543,12 +545,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::Provenance>>> {
if mplace.layout.is_unsized() {
// Don't touch unsized
return Ok(None);
return interp_ok(None);
}
let Some(alloc) = self.get_place_alloc(mplace)? else {
// zero-sized type can be left uninit
return Ok(Some(ImmTy::uninit(mplace.layout)));
return interp_ok(Some(ImmTy::uninit(mplace.layout)));
};
// It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
@ -557,7 +559,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// case where some of the bytes are initialized and others are not. So, we need an extra
// check that walks over the type of `mplace` to make sure it is truly correct to treat this
// like a `Scalar` (or `ScalarPair`).
Ok(match mplace.layout.abi {
interp_ok(match mplace.layout.abi {
Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => {
let size = s.size(self);
assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
@ -606,7 +608,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
&self,
src: &impl Projectable<'tcx, M::Provenance>,
) -> InterpResult<'tcx, Either<MPlaceTy<'tcx, M::Provenance>, ImmTy<'tcx, M::Provenance>>> {
Ok(match src.to_op(self)?.as_mplace_or_imm() {
interp_ok(match src.to_op(self)?.as_mplace_or_imm() {
Left(ref mplace) => {
if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? {
Right(val)
@ -637,7 +639,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if matches!(*imm, Immediate::Uninit) {
throw_ub!(InvalidUninitBytes(None));
}
Ok(imm)
interp_ok(imm)
}
/// Read a scalar from a place
@ -645,7 +647,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
&self,
op: &impl Projectable<'tcx, M::Provenance>,
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
Ok(self.read_immediate(op)?.to_scalar())
interp_ok(self.read_immediate(op)?.to_scalar())
}
// Pointer-sized reads are fairly common and need target layout access, so we wrap them in
@ -678,7 +680,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let len = mplace.len(self)?;
let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len))?;
let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
Ok(str)
interp_ok(str)
}
/// Read from a local of the current frame.
@ -698,7 +700,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
assert!(!layout.is_unsized());
}
M::after_local_read(self, local)?;
Ok(OpTy { op, layout })
interp_ok(OpTy { op, layout })
}
/// Every place can be read from, so we can turn them into an operand.
@ -709,12 +711,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
place: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
match place.as_mplace_or_local() {
Left(mplace) => Ok(mplace.into()),
Left(mplace) => interp_ok(mplace.into()),
Right((local, offset, locals_addr, _)) => {
debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`.
debug_assert_eq!(locals_addr, self.frame().locals_addr());
let base = self.local_to_op(local, None)?;
Ok(match offset {
interp_ok(match offset {
Some(offset) => base.offset(offset, place.layout, self)?,
None => {
// In the common case this hasn't been projected.
@ -764,7 +766,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
)
}
}
Ok(op)
interp_ok(op)
}
/// Evaluate the operand, returning a place where you can then find the data.
@ -794,7 +796,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
};
trace!("{:?}: {:?}", mir_op, op);
Ok(op)
interp_ok(op)
}
pub(crate) fn const_val_to_op(
@ -805,12 +807,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
// Other cases need layout.
let adjust_scalar = |scalar| -> InterpResult<'tcx, _> {
Ok(match scalar {
interp_ok(match scalar {
Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_root_pointer(ptr)?, size),
Scalar::Int(int) => Scalar::Int(int),
})
};
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
let layout =
from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty).into())?;
let imm = match val_val {
mir::ConstValue::Indirect { alloc_id, offset } => {
// This is const data, no mutation allowed.
@ -818,7 +821,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
CtfeProvenance::from(alloc_id).as_immutable(),
offset,
))?;
return Ok(self.ptr_to_mplace(ptr.into(), layout).into());
return interp_ok(self.ptr_to_mplace(ptr.into(), layout).into());
}
mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
mir::ConstValue::ZeroSized => Immediate::Uninit,
@ -829,7 +832,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Immediate::new_slice(self.global_root_pointer(ptr)?.into(), meta, self)
}
};
Ok(OpTy { op: Operand::Immediate(imm), layout })
interp_ok(OpTy { op: Operand::Immediate(imm), layout })
}
}

View file

@ -9,7 +9,7 @@ use rustc_span::symbol::sym;
use rustc_target::abi::Size;
use tracing::trace;
use super::{ImmTy, InterpCx, Machine, MemPlaceMeta, throw_ub};
use super::{ImmTy, InterpCx, Machine, MemPlaceMeta, interp_ok, throw_ub};
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> {
@ -156,7 +156,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
});
}
return Ok(ImmTy::from_scalar_int(result, left.layout));
return interp_ok(ImmTy::from_scalar_int(result, left.layout));
}
// For the remaining ops, the types must be the same on both sides
@ -181,10 +181,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
_ => None,
};
if let Some(op) = op {
return Ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx));
return interp_ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx));
}
if bin_op == Cmp {
return Ok(self.three_way_compare(l_signed(), r_signed()));
return interp_ok(self.three_way_compare(l_signed(), r_signed()));
}
let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
Div if r.is_null() => throw_ub!(DivisionByZero),
@ -221,7 +221,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
throw_ub!(ArithOverflow { intrinsic });
}
let res = ImmTy::from_scalar_int(result, left.layout);
return Ok(if with_overflow {
return interp_ok(if with_overflow {
let overflow = ImmTy::from_bool(overflow, *self.tcx);
ImmTy::from_pair(res, overflow, *self.tcx)
} else {
@ -234,10 +234,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let r = r_unsigned();
if bin_op == Cmp {
return Ok(self.three_way_compare(l, r));
return interp_ok(self.three_way_compare(l, r));
}
Ok(match bin_op {
interp_ok(match bin_op {
Eq => ImmTy::from_bool(l == r, *self.tcx),
Ne => ImmTy::from_bool(l != r, *self.tcx),
@ -339,7 +339,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
throw_ub!(PointerArithOverflow)
}
let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?;
Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout))
interp_ok(ImmTy::from_scalar(
Scalar::from_maybe_pointer(offset_ptr, self),
left.layout,
))
}
// Fall back to machine hook so Miri can support more pointer ops.
@ -366,20 +369,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
assert_eq!(left.layout.ty, right.layout.ty);
let left = left.to_scalar();
let right = right.to_scalar();
Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
interp_ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
}
ty::Bool => {
assert_eq!(left.layout.ty, right.layout.ty);
let left = left.to_scalar();
let right = right.to_scalar();
Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
interp_ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
}
ty::Float(fty) => {
assert_eq!(left.layout.ty, right.layout.ty);
let layout = left.layout;
let left = left.to_scalar();
let right = right.to_scalar();
Ok(match fty {
interp_ok(match fty {
FloatTy::F16 => {
self.binary_float_op(bin_op, layout, left.to_f16()?, right.to_f16()?)
}
@ -447,7 +450,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Not => !val,
_ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op),
};
Ok(ImmTy::from_bool(res, *self.tcx))
interp_ok(ImmTy::from_bool(res, *self.tcx))
}
ty::Float(fty) => {
let val = val.to_scalar();
@ -462,7 +465,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
FloatTy::F64 => Scalar::from_f64(-val.to_f64()?),
FloatTy::F128 => Scalar::from_f128(-val.to_f128()?),
};
Ok(ImmTy::from_scalar(res, layout))
interp_ok(ImmTy::from_scalar(res, layout))
}
ty::Int(..) => {
let val = val.to_scalar().to_int(layout.size)?;
@ -472,7 +475,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
_ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op),
};
let res = ScalarInt::truncate_from_int(res, layout.size).0;
Ok(ImmTy::from_scalar(res.into(), layout))
interp_ok(ImmTy::from_scalar(res.into(), layout))
}
ty::Uint(..) => {
let val = val.to_scalar().to_uint(layout.size)?;
@ -481,12 +484,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
_ => span_bug!(self.cur_span(), "Invalid unsigned integer op {:?}", un_op),
};
let res = ScalarInt::truncate_from_uint(res, layout.size).0;
Ok(ImmTy::from_scalar(res.into(), layout))
interp_ok(ImmTy::from_scalar(res.into(), layout))
}
ty::RawPtr(..) | ty::Ref(..) => {
assert_eq!(un_op, PtrMetadata);
let (_, meta) = val.to_scalar_and_meta();
Ok(match meta {
interp_ok(match meta {
MemPlaceMeta::Meta(scalar) => {
let ty = un_op.ty(*self.tcx, val.layout.ty);
let layout = self.layout_of(ty)?;
@ -514,7 +517,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let layout = self.layout_of(arg_ty)?;
let usize_layout = || self.layout_of(self.tcx.types.usize).unwrap();
Ok(match null_op {
interp_ok(match null_op {
SizeOf => {
if !layout.abi.is_sized() {
span_bug!(self.cur_span(), "unsized type for `NullaryOp::SizeOf`");

View file

@ -13,9 +13,9 @@ use rustc_target::abi::{Abi, Align, HasDataLayout, Size};
use tracing::{instrument, trace};
use super::{
AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, DiscardInterpError, ImmTy, Immediate,
InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer,
Projectable, Provenance, Scalar, alloc_range, mir_assign_valid_types,
AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult,
Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance,
Scalar, alloc_range, interp_ok, mir_assign_valid_types,
};
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
@ -90,7 +90,7 @@ impl<Prov: Provenance> MemPlace<Prov> {
}
OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx),
};
Ok(MemPlace { ptr, meta, misaligned: self.misaligned })
interp_ok(MemPlace { ptr, meta, misaligned: self.misaligned })
}
}
@ -163,7 +163,10 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
layout: TyAndLayout<'tcx>,
ecx: &InterpCx<'tcx, M>,
) -> InterpResult<'tcx, Self> {
Ok(MPlaceTy { mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?, layout })
interp_ok(MPlaceTy {
mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?,
layout,
})
}
#[inline(always)]
@ -171,7 +174,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
&self,
_ecx: &InterpCx<'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
Ok(self.clone().into())
interp_ok(self.clone().into())
}
}
@ -279,7 +282,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
layout: TyAndLayout<'tcx>,
ecx: &InterpCx<'tcx, M>,
) -> InterpResult<'tcx, Self> {
Ok(match self.as_mplace_or_local() {
interp_ok(match self.as_mplace_or_local() {
Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(),
Right((local, old_offset, locals_addr, _)) => {
debug_assert!(layout.is_sized(), "unsized locals should live in memory");
@ -367,7 +370,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
&self,
_ecx: &mut InterpCx<'tcx, M>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> {
Ok(self.clone())
interp_ok(self.clone())
}
}
@ -425,7 +428,7 @@ where
// `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced;
// we hence can't call `size_and_align_of` since that asserts more validity than we want.
let ptr = ptr.to_pointer(self)?;
Ok(self.ptr_with_meta_to_mplace(ptr, meta, layout, /*unaligned*/ false))
interp_ok(self.ptr_with_meta_to_mplace(ptr, meta, layout, /*unaligned*/ false))
}
/// Turn a mplace into a (thin or wide) mutable raw pointer, pointing to the same space.
@ -437,7 +440,7 @@ where
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
let imm = mplace.mplace.to_ref(self);
let layout = self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, mplace.layout.ty))?;
Ok(ImmTy::from_immediate(imm, layout))
interp_ok(ImmTy::from_immediate(imm, layout))
}
/// Take an operand, representing a pointer, and dereference it to a place.
@ -458,7 +461,7 @@ where
trace!("deref to {} on {:?}", val.layout.ty, *val);
let mplace = self.ref_to_mplace(&val)?;
Ok(mplace)
interp_ok(mplace)
}
#[inline]
@ -474,7 +477,7 @@ where
// If an access is both OOB and misaligned, we want to see the bounds error.
let a = self.get_ptr_alloc(mplace.ptr(), size)?;
self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn)?;
Ok(a)
interp_ok(a)
}
#[inline]
@ -489,17 +492,10 @@ where
// We check alignment separately, and raise that error *after* checking everything else.
// If an access is both OOB and misaligned, we want to see the bounds error.
// However we have to call `check_misalign` first to make the borrow checker happy.
let misalign_err = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn);
match self.get_ptr_alloc_mut(mplace.ptr(), size) {
Ok(a) => {
misalign_err?;
Ok(a)
}
Err(e) => {
misalign_err.discard_interp_err();
Err(e)
}
}
let misalign_res = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn);
// An error from get_ptr_alloc_mut takes precedence.
let (a, ()) = self.get_ptr_alloc_mut(mplace.ptr(), size).and(misalign_res)?;
interp_ok(a)
}
/// Turn a local in the current frame into a place.
@ -519,7 +515,7 @@ where
Operand::Indirect(mplace) => Place::Ptr(*mplace),
}
};
Ok(PlaceTy { place, layout })
interp_ok(PlaceTy { place, layout })
}
/// Computes a place. You should only use this if you intend to write into this
@ -556,7 +552,7 @@ where
)
}
}
Ok(place)
interp_ok(place)
}
/// Given a place, returns either the underlying mplace or a reference to where the value of
@ -572,7 +568,7 @@ where
(&mut Immediate<M::Provenance>, TyAndLayout<'tcx>, mir::Local),
>,
> {
Ok(match place.to_place().as_mplace_or_local() {
interp_ok(match place.to_place().as_mplace_or_local() {
Left(mplace) => Left(mplace),
Right((local, offset, locals_addr, layout)) => {
if offset.is_some() {
@ -617,7 +613,7 @@ where
)?;
}
Ok(())
interp_ok(())
}
/// Write a scalar to a place
@ -667,7 +663,7 @@ where
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)?;
}
}
Ok(())
interp_ok(())
}
/// Write an immediate to memory.
@ -690,7 +686,7 @@ where
let tcx = *self.tcx;
let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else {
// zero-sized access
return Ok(());
return interp_ok(());
};
match value {
@ -715,7 +711,7 @@ where
alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?;
alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?;
// We don't have to reset padding here, `write_immediate` will anyway do a validation run.
Ok(())
interp_ok(())
}
Immediate::Uninit => alloc.write_uninit_full(),
}
@ -736,12 +732,12 @@ where
Left(mplace) => {
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
// Zero-sized access
return Ok(());
return interp_ok(());
};
alloc.write_uninit_full()?;
}
}
Ok(())
interp_ok(())
}
/// Remove all provenance in the given place.
@ -760,12 +756,12 @@ where
Left(mplace) => {
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
// Zero-sized access
return Ok(());
return interp_ok(());
};
alloc.clear_provenance()?;
}
}
Ok(())
interp_ok(())
}
/// Copies the data from an operand to a place.
@ -848,7 +844,7 @@ where
)?;
}
Ok(())
interp_ok(())
}
/// Copies the data from an operand to a place.
@ -925,7 +921,7 @@ where
self.mem_copy(src.ptr(), dest.ptr(), dest_size, /*nonoverlapping*/ true)?;
self.check_misalign(src.mplace.misaligned, CheckAlignMsg::BasedOn)?;
self.check_misalign(dest.mplace.misaligned, CheckAlignMsg::BasedOn)?;
Ok(())
interp_ok(())
}
/// Ensures that a place is in memory, and returns where it is.
@ -987,7 +983,7 @@ where
Place::Ptr(mplace) => mplace,
};
// Return with the original layout and align, so that the caller can go on
Ok(MPlaceTy { mplace, layout: place.layout })
interp_ok(MPlaceTy { mplace, layout: place.layout })
}
pub fn allocate_dyn(
@ -1000,7 +996,7 @@ where
span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known")
};
let ptr = self.allocate_ptr(size, align, kind)?;
Ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false))
interp_ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false))
}
pub fn allocate(
@ -1035,7 +1031,7 @@ where
};
let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
let layout = self.layout_of(self.tcx.types.str_).unwrap();
Ok(self.ptr_with_meta_to_mplace(
interp_ok(self.ptr_with_meta_to_mplace(
ptr.into(),
MemPlaceMeta::Meta(meta),
layout,
@ -1051,7 +1047,7 @@ where
let _ = self.tcx.global_alloc(raw.alloc_id);
let ptr = self.global_root_pointer(Pointer::from(raw.alloc_id))?;
let layout = self.layout_of(raw.ty)?;
Ok(self.ptr_to_mplace(ptr.into(), layout))
interp_ok(self.ptr_to_mplace(ptr.into(), layout))
}
}

View file

@ -18,7 +18,7 @@ use tracing::{debug, instrument};
use super::{
InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar, err_ub,
throw_ub, throw_unsup,
interp_ok, throw_ub, throw_unsup,
};
/// Describes the constraints placed on offset-projections.
@ -54,7 +54,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
// Go through the layout. There are lots of types that support a length,
// e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
match layout.fields {
abi::FieldsShape::Array { count, .. } => Ok(count),
abi::FieldsShape::Array { count, .. } => interp_ok(count),
_ => bug!("len not supported on sized type {:?}", layout.ty),
}
}
@ -115,9 +115,9 @@ impl<'a, 'tcx, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'a, '
&mut self,
ecx: &InterpCx<'tcx, M>,
) -> InterpResult<'tcx, Option<(u64, P)>> {
let Some(idx) = self.range.next() else { return Ok(None) };
let Some(idx) = self.range.next() else { return interp_ok(None) };
// We use `Wrapping` here since the offset has already been checked when the iterator was created.
Ok(Some((
interp_ok(Some((
idx,
self.base.offset_with_meta(
self.stride * idx,
@ -258,7 +258,7 @@ where
// SIMD types must be newtypes around arrays, so all we have to do is project to their only field.
let array = self.project_field(base, 0)?;
let len = array.len(self)?;
Ok((array, len))
interp_ok((array, len))
}
fn project_constant_index<P: Projectable<'tcx, M::Provenance>>(
@ -300,7 +300,13 @@ where
debug!("project_array_fields: {base:?} {len}");
base.offset(len * stride, self.layout_of(self.tcx.types.unit).unwrap(), self)?;
// Create the iterator.
Ok(ArrayIterator { base, range: 0..len, stride, field_layout, _phantom: PhantomData })
interp_ok(ArrayIterator {
base,
range: 0..len,
stride,
field_layout,
_phantom: PhantomData,
})
}
/// Subslicing
@ -367,7 +373,7 @@ where
P: Projectable<'tcx, M::Provenance> + From<MPlaceTy<'tcx, M::Provenance>> + std::fmt::Debug,
{
use rustc_middle::mir::ProjectionElem::*;
Ok(match proj_elem {
interp_ok(match proj_elem {
OpaqueCast(ty) => {
span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck")
}

View file

@ -17,7 +17,7 @@ use tracing::{info_span, instrument, trace};
use super::{
AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace,
MemPlaceMeta, MemoryKind, Operand, Pointer, Provenance, ReturnAction, Scalar,
from_known_layout, throw_ub, throw_unsup,
from_known_layout, interp_ok, throw_ub, throw_unsup,
};
use crate::errors;
@ -189,7 +189,7 @@ impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
pub(super) fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> {
match &self.value {
LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
LocalValue::Live(val) => Ok(val),
LocalValue::Live(val) => interp_ok(val),
}
}
@ -199,7 +199,7 @@ impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> {
match &mut self.value {
LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
LocalValue::Live(val) => Ok(val),
LocalValue::Live(val) => interp_ok(val),
}
}
}
@ -391,7 +391,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let span = info_span!("frame", "{}", instance);
self.frame_mut().tracing_span.enter(span);
Ok(())
interp_ok(())
}
/// Low-level helper that pops a stack frame from the stack and returns some information about
@ -426,7 +426,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
return_action = ReturnAction::NoCleanup;
};
Ok(StackPopInfo { return_action, return_to_block, return_place })
interp_ok(StackPopInfo { return_action, return_to_block, return_place })
}
/// A private helper for [`pop_stack_frame_raw`](InterpCx::pop_stack_frame_raw).
@ -449,7 +449,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
}
Ok(cleanup)
interp_ok(cleanup)
}
/// In the current stack frame, mark all locals as live that are not arguments and don't have
@ -464,7 +464,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.storage_live(local)?;
}
}
Ok(())
interp_ok(())
}
pub fn storage_live_dyn(
@ -550,7 +550,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// If the local is already live, deallocate its old memory.
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
self.deallocate_local(old)?;
Ok(())
interp_ok(())
}
/// Mark a storage as live, killing the previous content.
@ -566,7 +566,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// If the local is already dead, this is a NOP.
let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead);
self.deallocate_local(old)?;
Ok(())
interp_ok(())
}
fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> {
@ -581,7 +581,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
);
self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
};
Ok(())
interp_ok(())
}
#[inline(always)]
@ -593,19 +593,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
let state = &frame.locals[local];
if let Some(layout) = state.layout.get() {
return Ok(layout);
return interp_ok(layout);
}
let layout = from_known_layout(self.tcx, self.param_env, layout, || {
let local_ty = frame.body.local_decls[local].ty;
let local_ty =
self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
self.layout_of(local_ty)
self.layout_of(local_ty).into()
})?;
// Layouts of locals are requested a lot, so we cache them.
state.layout.set(Some(layout));
Ok(layout)
interp_ok(layout)
}
}

View file

@ -14,7 +14,7 @@ use tracing::{info, instrument, trace};
use super::{
FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy,
Projectable, Scalar, throw_ub,
Projectable, Scalar, interp_ok, throw_ub,
};
use crate::util;
@ -36,7 +36,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
#[inline(always)]
pub fn step(&mut self) -> InterpResult<'tcx, bool> {
if self.stack().is_empty() {
return Ok(false);
return interp_ok(false);
}
let Either::Left(loc) = self.frame().loc else {
@ -44,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Just go on unwinding.
trace!("unwinding: skipping frame");
self.return_from_current_stack_frame(/* unwinding */ true)?;
return Ok(true);
return interp_ok(true);
};
let basic_block = &self.body().basic_blocks[loc.block];
@ -55,7 +55,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
assert_eq!(old_frames, self.frame_idx());
// Advance the program counter.
self.frame_mut().loc.as_mut().left().unwrap().statement_index += 1;
return Ok(true);
return interp_ok(true);
}
M::before_terminator(self)?;
@ -67,7 +67,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
info!("// executing {:?}", loc.block);
}
}
Ok(true)
interp_ok(true)
}
/// Runs the interpretation logic for the given `mir::Statement` at the current frame and
@ -145,7 +145,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Nop => {}
}
Ok(())
interp_ok(())
}
/// Evaluate an assignment statement.
@ -277,7 +277,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
trace!("{:?}", self.dump_place(&dest));
Ok(())
interp_ok(())
}
/// Writes the aggregate to the destination.
@ -313,7 +313,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self);
let ptr = ImmTy::from_immediate(ptr_imm, dest.layout);
self.copy_op(&ptr, dest)?;
return Ok(());
return interp_ok(());
}
_ => (FIRST_VARIANT, dest.clone(), None),
};
@ -365,7 +365,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
)?;
}
Ok(())
interp_ok(())
}
/// Evaluate the arguments of a function call
@ -373,7 +373,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
&self,
op: &mir::Operand<'tcx>,
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
Ok(match op {
interp_ok(match op {
mir::Operand::Copy(_) | mir::Operand::Constant(_) => {
// Make a regular copy.
let op = self.eval_operand(op, None)?;
@ -442,7 +442,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
};
Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location })
interp_ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location })
}
fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> {
@ -537,7 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// generic. In order to make sure that generic and non-generic code behaves
// roughly the same (and in keeping with Mir semantics) we do nothing here.
self.go_to_block(target);
return Ok(());
return interp_ok(());
}
trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty);
self.init_drop_in_place_call(&place, instance, target, unwind)?;
@ -566,7 +566,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// By definition, a Resume terminator means
// that we're unwinding
self.return_from_current_stack_frame(/* unwinding */ true)?;
return Ok(());
return interp_ok(());
}
// It is UB to ever encounter this.
@ -584,6 +584,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
}
Ok(())
interp_ok(())
}
}

View file

@ -5,7 +5,9 @@ use rustc_target::abi::{Align, Size};
use tracing::trace;
use super::util::ensure_monomorphic_enough;
use super::{InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable, throw_ub};
use super::{
InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable, interp_ok, throw_ub,
};
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
@ -31,7 +33,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let salt = M::get_global_alloc_salt(self, None);
let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, dyn_ty, salt);
let vtable_ptr = self.global_root_pointer(Pointer::from(vtable_symbolic_allocation))?;
Ok(vtable_ptr.into())
interp_ok(vtable_ptr.into())
}
pub fn get_vtable_size_and_align(
@ -42,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?;
let layout = self.layout_of(ty)?;
assert!(layout.is_sized(), "there are no vtables for unsized types");
Ok((layout.size, layout.align.abi))
interp_ok((layout.size, layout.align.abi))
}
pub(super) fn vtable_entries(
@ -102,7 +104,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
}
Ok(())
interp_ok(())
}
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
@ -127,7 +129,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
layout,
self,
)?;
Ok(mplace)
interp_ok(mplace)
}
/// Turn a `dyn* Trait` type into an value with the actual dynamic type.
@ -147,6 +149,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// `data` is already the right thing but has the wrong type. So we transmute it.
let layout = self.layout_of(ty)?;
let data = data.transmute(layout, self)?;
Ok(data)
interp_ok(data)
}
}

View file

@ -9,7 +9,7 @@ use rustc_middle::ty::{
};
use tracing::debug;
use super::{InterpCx, MPlaceTy, MemoryKind, throw_inval};
use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval};
use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult};
/// Checks whether a type contains generic parameters which must be instantiated.
@ -23,7 +23,7 @@ where
{
debug!("ensure_monomorphic_enough: ty={:?}", ty);
if !ty.has_param() {
return Ok(());
return interp_ok(());
}
struct FoundParam;
@ -78,7 +78,7 @@ where
if matches!(ty.visit_with(&mut vis), ControlFlow::Break(FoundParam)) {
throw_inval!(TooGeneric);
} else {
Ok(())
interp_ok(())
}
}
@ -103,5 +103,5 @@ pub(crate) fn create_static_alloc<'tcx>(
assert_eq!(ecx.machine.static_root_ids, None);
ecx.machine.static_root_ids = Some((alloc_id, static_def_id));
assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none());
Ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout))
interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout))
}

View file

@ -18,7 +18,7 @@ use rustc_middle::bug;
use rustc_middle::mir::interpret::ValidationErrorKind::{self, *};
use rustc_middle::mir::interpret::{
ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind,
Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range,
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,25 +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) => {
let (kind, backtrace) = e.into_parts();
match kind {
$(
$($p)|+ => {
throw_validation_failure!(
$where,
$kind
)
}
),+,
_ => Err::<!, _>(InterpErrorInfo::from_parts(kind, backtrace))?,
}
let (kind, backtrace) = e.into_parts();
match kind {
$(
$($p)|+ => {
err_validation_failure!(
$where,
$kind
).into()
}
),+,
_ => InterpErrorInfo::from_parts(kind, backtrace),
}
}
})?
}};
}
@ -381,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(
@ -389,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)) =>
@ -407,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(
@ -472,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`.
@ -635,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.
@ -644,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;
@ -657,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
@ -684,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)?;
@ -699,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
@ -716,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)?;
@ -756,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.
@ -774,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(_)
@ -811,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.
@ -828,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}"),
@ -887,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.
@ -899,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.
@ -933,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:
@ -1073,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 {
@ -1137,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]
@ -1147,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]
@ -1160,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
@ -1207,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".
@ -1227,37 +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.)
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));
// 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(_))) {
throw_validation_failure!(self.path, Uninit { expected })
} else {
throw_validation_failure!(self.path, PointerAsInt { expected })
}
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(InterpErrorInfo::from_parts(kind, backtrace)),
}
// 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.
@ -1335,7 +1334,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
}
}
Ok(())
interp_ok(())
}
}
@ -1351,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.
@ -1367,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.
@ -1460,6 +1452,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/*reset_provenance_and_padding*/ false,
)?;
}
Ok(())
interp_ok(())
}
}

View file

@ -10,7 +10,7 @@ use rustc_middle::ty::{self, Ty};
use rustc_target::abi::{FieldIdx, FieldsShape, VariantIdx, Variants};
use tracing::trace;
use super::{InterpCx, MPlaceTy, Machine, Projectable, throw_inval};
use super::{InterpCx, MPlaceTy, Machine, Projectable, interp_ok, throw_inval};
/// How to traverse a value and what to do when we are at the leaves.
pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
@ -46,14 +46,14 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
/// Visits the given value as a union. No automatic recursion can happen here.
#[inline(always)]
fn visit_union(&mut self, _v: &Self::V, _fields: NonZero<usize>) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
/// The type of `v` will be a raw pointer to `T`, but this is a field of `Box<T>` and the
/// pointee type is the actual `T`. `box_ty` provides the full type of the `Box` itself.
#[inline(always)]
fn visit_box(&mut self, _box_ty: Ty<'tcx>, _v: &Self::V) -> InterpResult<'tcx> {
Ok(())
interp_ok(())
}
/// Called each time we recurse down to a field of a "product-like" aggregate
@ -165,7 +165,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
self.visit_field(v, 1, &alloc)?;
// We visited all parts of this one.
return Ok(());
return interp_ok(());
}
// Non-normalized types should never show up here.
@ -222,6 +222,6 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
Variants::Single { .. } => {}
}
Ok(())
interp_ok(())
}
}

View file

@ -1,5 +1,4 @@
use rustc_middle::bug;
use rustc_middle::mir::interpret::DiscardInterpError;
use rustc_middle::ty::layout::{
HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement,
};
@ -76,7 +75,7 @@ fn check_validity_requirement_strict<'tcx>(
/*recursive*/ false,
/*reset_provenance_and_padding*/ false,
)
.discard_interp_err()
.discard_err()
.is_some())
}