diff options
Diffstat (limited to 'compiler/rustc_const_eval/src/interpret')
16 files changed, 176 insertions, 147 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 37677f9e048..79c14b204e3 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; use either::{Left, Right}; use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; use rustc_hir::def_id::DefId; -use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::sym; diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 9e15f4572d7..1036935bb10 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -6,7 +6,7 @@ use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir::CastKind; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_middle::{bug, span_bug}; use tracing::trace; diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 6c4b000e16b..b7e7f65c95c 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -1,7 +1,7 @@ //! Functions for reading and writing discriminants of multi-variant layouts (enums and coroutines). use rustc_abi::{self as abi, FieldIdx, TagEncoding, VariantIdx, Variants}; -use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; +use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; use rustc_middle::ty::{self, CoroutineArgsExt, ScalarInt, Ty}; use rustc_middle::{mir, span_bug}; use tracing::{instrument, trace}; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index b69bc0918be..46c784b41c6 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -7,7 +7,8 @@ use rustc_hir::def_id::DefId; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ - self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, + self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers, + TyAndLayout, }; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypingEnv, Variance}; use rustc_middle::{mir, span_bug}; @@ -21,7 +22,7 @@ use super::{ 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}; +use crate::{ReportErrorExt, enter_trace_span, fluent_generated as fluent, util}; pub struct InterpCx<'tcx, M: Machine<'tcx>> { /// Stores the `Machine` instance. @@ -91,6 +92,20 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { } } +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { + /// This inherent method takes priority over the trait method with the same name in LayoutOf, + /// and allows wrapping the actual [LayoutOf::layout_of] with a tracing span. + /// See [LayoutOf::layout_of] for the original documentation. + #[inline] + pub fn layout_of( + &self, + ty: Ty<'tcx>, + ) -> <InterpCx<'tcx, M> as LayoutOfHelpers<'tcx>>::LayoutOfResult { + let _span = enter_trace_span!(M, "InterpCx::layout_of", "ty = {:?}", ty.kind()); + LayoutOf::layout_of(self, ty) + } +} + impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpErrorKind<'tcx>>; @@ -284,6 +299,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, value: T, ) -> Result<T, ErrorHandled> { + let _span = enter_trace_span!( + M, + "instantiate_from_frame_and_normalize_erasing_regions", + "{}", + frame.instance + ); frame .instance .try_instantiate_mir_and_normalize_erasing_regions( @@ -362,7 +383,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns the actual dynamic size and alignment of the place at the given type. /// Only the "meta" (metadata) part of the place matters. /// This can fail to provide an answer for extern types. - pub(super) fn size_and_align_of( + pub(super) fn size_and_align_from_meta( &self, metadata: &MemPlaceMeta<M::Provenance>, layout: &TyAndLayout<'tcx>, @@ -388,7 +409,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // adjust alignment and size for them? let field = layout.field(self, layout.fields.count() - 1); let Some((unsized_size, mut unsized_align)) = - self.size_and_align_of(metadata, &field)? + self.size_and_align_from_meta(metadata, &field)? else { // A field with an extern type. We don't know the actual dynamic size // or the alignment. @@ -450,11 +471,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } #[inline] - pub fn size_and_align_of_mplace( + pub fn size_and_align_of_val( &self, - mplace: &MPlaceTy<'tcx, M::Provenance>, + val: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { - self.size_and_align_of(&mplace.meta(), &mplace.layout) + self.size_and_align_from_meta(&val.meta(), &val.layout()) } /// Jump to the given block. diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 96c39c7bb32..d7cede71293 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -6,10 +6,9 @@ use std::assert_matches::assert_matches; use rustc_abi::Size; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_hir::def_id::DefId; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; -use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::layout::{TyAndLayout, ValidityRequirement}; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_span::{Symbol, sym}; use tracing::trace; @@ -17,8 +16,8 @@ use tracing::trace; use super::memory::MemoryKind; use super::util::ensure_monomorphic_enough; use super::{ - Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, Machine, - OpTy, PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom, + Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy, + PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom, err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; @@ -30,73 +29,6 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll tcx.mk_const_alloc(alloc) } -/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated -/// inside an `InterpCx` and instead have their value computed directly from rustc internal info. -pub(crate) fn eval_nullary_intrinsic<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - def_id: DefId, - args: GenericArgsRef<'tcx>, -) -> InterpResult<'tcx, ConstValue<'tcx>> { - let tp_ty = args.type_at(0); - let name = tcx.item_name(def_id); - interp_ok(match name { - sym::type_name => { - ensure_monomorphic_enough(tcx, tp_ty)?; - let alloc = alloc_type_name(tcx, tp_ty); - ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() } - } - sym::needs_drop => { - ensure_monomorphic_enough(tcx, tp_ty)?; - ConstValue::from_bool(tp_ty.needs_drop(tcx, typing_env)) - } - sym::type_id => { - ensure_monomorphic_enough(tcx, tp_ty)?; - ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()) - } - sym::variant_count => match match tp_ty.kind() { - // Pattern types have the same number of variants as their base type. - // Even if we restrict e.g. which variants are valid, the variants are essentially just uninhabited. - // And `Result<(), !>` still has two variants according to `variant_count`. - ty::Pat(base, _) => *base, - _ => tp_ty, - } - .kind() - { - // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough. - ty::Adt(adt, _) => ConstValue::from_target_usize(adt.variants().len() as u64, &tcx), - ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => { - throw_inval!(TooGeneric) - } - ty::Pat(..) => unreachable!(), - ty::Bound(_, _) => bug!("bound ty during ctfe"), - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Slice(_) - | ty::RawPtr(_, _) - | ty::Ref(_, _, _) - | ty::FnDef(_, _) - | ty::FnPtr(..) - | ty::Dynamic(_, _, _) - | ty::Closure(_, _) - | ty::CoroutineClosure(_, _) - | ty::Coroutine(_, _) - | ty::CoroutineWitness(..) - | ty::UnsafeBinder(_) - | ty::Never - | ty::Tuple(_) - | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), - }, - other => bug!("`{}` is not a zero arg intrinsic", other), - }) -} - impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns `true` if emulation happened. /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own @@ -110,8 +42,77 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, bool> { let instance_args = instance.args; let intrinsic_name = self.tcx.item_name(instance.def_id()); + let tcx = self.tcx.tcx; match intrinsic_name { + sym::type_name => { + let tp_ty = instance.args.type_at(0); + ensure_monomorphic_enough(tcx, tp_ty)?; + let alloc = alloc_type_name(tcx, tp_ty); + let val = ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() }; + let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::needs_drop => { + let tp_ty = instance.args.type_at(0); + ensure_monomorphic_enough(tcx, tp_ty)?; + let val = ConstValue::from_bool(tp_ty.needs_drop(tcx, self.typing_env)); + let val = self.const_val_to_op(val, tcx.types.bool, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::type_id => { + let tp_ty = instance.args.type_at(0); + ensure_monomorphic_enough(tcx, tp_ty)?; + let val = ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()); + let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::variant_count => { + let tp_ty = instance.args.type_at(0); + let ty = match tp_ty.kind() { + // Pattern types have the same number of variants as their base type. + // Even if we restrict e.g. which variants are valid, the variants are essentially just uninhabited. + // And `Result<(), !>` still has two variants according to `variant_count`. + ty::Pat(base, _) => *base, + _ => tp_ty, + }; + let val = match ty.kind() { + // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough. + ty::Adt(adt, _) => { + ConstValue::from_target_usize(adt.variants().len() as u64, &tcx) + } + ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => { + throw_inval!(TooGeneric) + } + ty::Pat(..) => unreachable!(), + ty::Bound(_, _) => bug!("bound ty during ctfe"), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(..) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::UnsafeBinder(_) + | ty::Never + | ty::Tuple(_) + | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), + }; + let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::caller_location => { let span = self.find_closest_untracked_caller_location(); let val = self.tcx.span_as_caller_location(span); @@ -125,7 +126,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // dereferenceable! let place = self.ref_to_mplace(&self.read_immediate(&args[0])?)?; let (size, align) = self - .size_and_align_of_mplace(&place)? + .size_and_align_of_val(&place)? .ok_or_else(|| err_unsup_format!("`extern type` does not have known layout"))?; let result = match intrinsic_name { @@ -137,21 +138,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(result, self), dest)?; } - sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => { - let gid = GlobalId { instance, promoted: None }; - let ty = self - .tcx - .fn_sig(instance.def_id()) - .instantiate(self.tcx.tcx, instance.args) - .output() - .no_bound_vars() - .unwrap(); - let val = self - .ctfe_query(|tcx| tcx.const_eval_global_id(self.typing_env, gid, tcx.span))?; - let val = self.const_val_to_op(val, ty, Some(dest.layout))?; - self.copy_op(&val, dest)?; - } - sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index b9e022c9604..d6d230fbd17 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -443,7 +443,11 @@ 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> { + fn before_alloc_access( + _tcx: TyCtxtAt<'tcx>, + _machine: &Self, + _alloc_id: AllocId, + ) -> InterpResult<'tcx> { interp_ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 57bf867e389..69fceb02ff9 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -720,7 +720,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked. if !self.memory.validation_in_progress.get() { if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) { - M::before_alloc_read(self, alloc_id)?; + M::before_alloc_access(self.tcx, &self.machine, alloc_id)?; } } @@ -821,6 +821,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some((alloc_id, offset, prov, alloc, machine)) = ptr_and_alloc { let range = alloc_range(offset, size); if !validation_in_progress { + // For writes, it's okay to only call those when there actually is a non-zero + // amount of bytes to be written: a zero-sized write doesn't manifest anything. + M::before_alloc_access(tcx, machine, alloc_id)?; M::before_memory_write( tcx, machine, @@ -1396,6 +1399,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let src_parts = self.get_ptr_access(src, size)?; let dest_parts = self.get_ptr_access(dest, size * num_copies)?; // `Size` multiplication + // Similar to `get_ptr_alloc`, we need to call `before_alloc_access` even for zero-sized + // reads. However, just like in `get_ptr_alloc_mut`, the write part is okay to skip for + // zero-sized writes. + if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(src, size.bytes().try_into().unwrap()) + { + M::before_alloc_access(tcx, &self.machine, alloc_id)?; + } + // FIXME: we look up both allocations twice here, once before for the `check_ptr_access` // and once below to get the underlying `&[mut] Allocation`. @@ -1408,12 +1419,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let src_range = alloc_range(src_offset, size); assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation"); - // Trigger read hooks. - // For the overlapping case, it is crucial that we trigger the read hooks + // Trigger read hook. + // For the overlapping case, it is crucial that we trigger the read hook // before the write hook -- the aliasing model cares about the order. - if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(src, size.bytes() as i64) { - M::before_alloc_read(self, alloc_id)?; - } M::before_memory_read( tcx, &self.machine, @@ -1438,16 +1446,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let provenance = src_alloc .provenance() .prepare_copy(src_range, dest_offset, num_copies, self) - .map_err(|e| e.to_interp_error(dest_alloc_id))?; + .map_err(|e| e.to_interp_error(src_alloc_id))?; // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); - // Destination alloc preparations and access hooks. - let (dest_alloc, extra) = self.get_alloc_raw_mut(dest_alloc_id)?; + // Destination alloc preparations... + let (dest_alloc, machine) = self.get_alloc_raw_mut(dest_alloc_id)?; let dest_range = alloc_range(dest_offset, size * num_copies); + // ...and access hooks. + M::before_alloc_access(tcx, machine, dest_alloc_id)?; M::before_memory_write( tcx, - extra, + machine, &mut dest_alloc.extra, dest, (dest_alloc_id, dest_prov), diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index f5792aba207..f8b3c92debb 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -29,7 +29,6 @@ pub use self::intern::{ HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop, intern_const_alloc_recursive, }; -pub(crate) use self::intrinsics::eval_nullary_intrinsic; pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine}; pub use self::memory::{AllocInfo, AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; use self::operand::Operand; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 77667ba823a..62cbbae24a8 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -8,7 +8,7 @@ use rustc_abi as abi; use rustc_abi::{BackendRepr, HasDataLayout, Size}; use rustc_hir::def::Namespace; use rustc_middle::mir::interpret::ScalarSizeMismatch; -use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, TyAndLayout}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter}; use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, mir, span_bug, ty}; @@ -878,9 +878,9 @@ mod size_asserts { use super::*; // tidy-alphabetical-start - static_assert_size!(Immediate, 48); static_assert_size!(ImmTy<'_>, 64); - static_assert_size!(Operand, 56); + static_assert_size!(Immediate, 48); static_assert_size!(OpTy<'_>, 72); + static_assert_size!(Operand, 56); // tidy-alphabetical-end } diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 899670aeb62..74f8a0a7b09 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -3,7 +3,7 @@ use rustc_abi::Size; use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir::NullOp; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; -use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::sym; diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index f5d3de7b1b2..3028568dd8f 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -7,7 +7,7 @@ use std::assert_matches::assert_matches; use either::{Either, Left, Right}; use rustc_abi::{BackendRepr, HasDataLayout, Size}; use rustc_middle::ty::Ty; -use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, mir, span_bug}; use tracing::{instrument, trace}; @@ -470,7 +470,7 @@ where ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> { let (size, _align) = self - .size_and_align_of_mplace(mplace)? + .size_and_align_of_val(mplace)? .unwrap_or((mplace.layout.size, mplace.layout.align.abi)); // We check alignment separately, and *after* checking everything else. // If an access is both OOB and misaligned, we want to see the bounds error. @@ -486,7 +486,7 @@ where ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> { let (size, _align) = self - .size_and_align_of_mplace(mplace)? + .size_and_align_of_val(mplace)? .unwrap_or((mplace.layout.size, mplace.layout.align.abi)); // 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. @@ -888,11 +888,11 @@ where trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout().ty); let dest = dest.force_mplace(self)?; - let Some((dest_size, _)) = self.size_and_align_of_mplace(&dest)? else { + let Some((dest_size, _)) = self.size_and_align_of_val(&dest)? else { span_bug!(self.cur_span(), "copy_op needs (dynamically) sized values") }; if cfg!(debug_assertions) { - let src_size = self.size_and_align_of_mplace(&src)?.unwrap().0; + let src_size = self.size_and_align_of_val(&src)?.unwrap().0; assert_eq!(src_size, dest_size, "Cannot copy differently-sized data"); } else { // As a cheap approximation, we compare the fixed parts of the size. @@ -980,7 +980,7 @@ where kind: MemoryKind<M::MemoryKind>, meta: MemPlaceMeta<M::Provenance>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let Some((size, align)) = self.size_and_align_of(&meta, &layout)? else { + let Some((size, align)) = self.size_and_align_from_meta(&meta, &layout)? else { span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known") }; let ptr = self.allocate_ptr(size, align, kind, AllocInit::Uninit)?; @@ -1056,9 +1056,9 @@ mod size_asserts { use super::*; // tidy-alphabetical-start + static_assert_size!(MPlaceTy<'_>, 64); static_assert_size!(MemPlace, 48); static_assert_size!(MemPlaceMeta, 24); - static_assert_size!(MPlaceTy<'_>, 64); static_assert_size!(Place, 48); static_assert_size!(PlaceTy<'_>, 64); // tidy-alphabetical-end diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index ad47a19a14d..306697d4ec9 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -12,7 +12,7 @@ use std::ops::Range; use rustc_abi::{self as abi, FieldIdx, Size, VariantIdx}; use rustc_middle::ty::Ty; -use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, mir, span_bug, ty}; use tracing::{debug, instrument}; @@ -168,7 +168,7 @@ where // Re-use parent metadata to determine dynamic field layout. // With custom DSTS, this *will* execute user-defined code, but the same // happens at run-time so that's okay. - match self.size_and_align_of(&base_meta, &field_layout)? { + match self.size_and_align_from_meta(&base_meta, &field_layout)? { Some((_, align)) => { // For packed types, we need to cap alignment. let align = if let ty::Adt(def, _) = base.layout().ty.kind() diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 2a2d1bb2754..3361a586b8e 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -7,7 +7,7 @@ use either::{Either, Left, Right}; use rustc_hir as hir; use rustc_hir::definitions::DefPathData; use rustc_index::IndexVec; -use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_mir_dataflow::impls::always_storage_live_locals; diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 7249ef23bf6..8b634955bb7 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,6 +1,5 @@ use rustc_abi::{Align, FieldIdx, Size}; use rustc_middle::mir::interpret::{InterpResult, Pointer}; -use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt, Ty, TyCtxt, VtblEntry}; use tracing::trace; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 7d76d925ef2..998ef3729ea 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -24,7 +24,7 @@ use rustc_middle::mir::interpret::{ ExpectedKind, InterpErrorKind, InvalidMetaKind, Misalignment, PointerKind, Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, }; -use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; use rustc_middle::ty::{self, Ty}; use rustc_span::{Symbol, sym}; use tracing::trace; @@ -35,6 +35,7 @@ use super::{ Machine, MemPlaceMeta, PlaceTy, Pointer, Projectable, Scalar, ValueVisitor, err_ub, format_interp_error, }; +use crate::enter_trace_span; // for the validation errors #[rustfmt::skip] @@ -493,7 +494,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } // Make sure this is dereferenceable and all. let size_and_align = try_validation!( - self.ecx.size_and_align_of_mplace(&place), + self.ecx.size_and_align_of_val(&place), self.path, Ub(InvalidMeta(msg)) => match msg { InvalidMetaKind::SliceTooBig => InvalidMetaSliceTooLarge { ptr_kind }, @@ -570,6 +571,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { }; let (size, _align) = global_alloc.size_and_align(*self.ecx.tcx, self.ecx.typing_env); + let alloc_actual_mutbl = + global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env); if let GlobalAlloc::Static(did) = global_alloc { let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { @@ -597,9 +600,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { skip_recursive_check = !nested; } CtfeValidationMode::Const { .. } => { - // We can't recursively validate `extern static`, so we better reject them. - if self.ecx.tcx.is_foreign_item(did) { - throw_validation_failure!(self.path, ConstRefToExtern); + // If this is mutable memory or an `extern static`, there's no point in checking it -- we'd + // just get errors trying to read the value. + if alloc_actual_mutbl.is_mut() || self.ecx.tcx.is_foreign_item(did) + { + skip_recursive_check = true; } } } @@ -618,9 +623,6 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { mutbl } }; - // Determine what it actually points to. - let alloc_actual_mutbl = - global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env); // Mutable pointer to immutable memory is no good. if ptr_expected_mutbl == Mutability::Mut && alloc_actual_mutbl == Mutability::Not @@ -628,12 +630,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // This can actually occur with transmutes. throw_validation_failure!(self.path, MutableRefToImmutable); } - // In a const, everything must be completely immutable. + // In a const, any kind of mutable reference is not good. if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) { - if ptr_expected_mutbl == Mutability::Mut - || alloc_actual_mutbl == Mutability::Mut - { - throw_validation_failure!(self.path, ConstRefToMutable); + if ptr_expected_mutbl == Mutability::Mut { + throw_validation_failure!(self.path, MutableRefInConst); } } } @@ -906,7 +906,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { let (_prov, start_offset) = mplace.ptr().into_parts(); let (size, _align) = self .ecx - .size_and_align_of_mplace(&mplace)? + .size_and_align_of_val(&mplace)? .unwrap_or((mplace.layout.size, mplace.layout.align.abi)); // If there is no padding at all, we can skip the rest: check for // a single data range covering the entire value. @@ -1086,8 +1086,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, ) -> InterpResult<'tcx> { // Special check for CTFE validation, preventing `UnsafeCell` inside unions in immutable memory. if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) { - if !val.layout.is_zst() && !val.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.typing_env) - { + // Unsized unions are currently not a thing, but let's keep this code consistent with + // the check in `visit_value`. + let zst = self.ecx.size_and_align_of_val(val)?.is_some_and(|(s, _a)| s.bytes() == 0); + if !zst && !val.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.typing_env) { if !self.in_mutable_memory(val) { throw_validation_failure!(self.path, UnsafeCellInImmutable); } @@ -1131,7 +1133,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // Special check preventing `UnsafeCell` in the inner part of constants if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) { - if !val.layout.is_zst() + // Exclude ZST values. We need to compute the dynamic size/align to properly + // handle slices and trait objects. + let zst = self.ecx.size_and_align_of_val(val)?.is_some_and(|(s, _a)| s.bytes() == 0); + if !zst && let Some(def) = val.layout.ty.ty_adt_def() && def.is_unsafe_cell() { @@ -1364,8 +1369,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }) } - /// This function checks the data at `op` to be const-valid. - /// `op` is assumed to cover valid memory if it is an indirect operand. + /// This function checks the data at `val` to be const-valid. + /// `val` is assumed to cover valid memory if it is an indirect operand. /// It will error if the bits at the destination do not match the ones described by the layout. /// /// `ref_tracking` is used to record references that we encounter so that they @@ -1391,8 +1396,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) } - /// This function checks the data at `op` to be runtime-valid. - /// `op` is assumed to cover valid memory if it is an indirect operand. + /// This function checks the data at `val` to be runtime-valid. + /// `val` is assumed to cover valid memory if it is an indirect operand. /// It will error if the bits at the destination do not match the ones described by the layout. #[inline(always)] pub fn validate_operand( @@ -1401,6 +1406,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { recursive: bool, reset_provenance_and_padding: bool, ) -> InterpResult<'tcx> { + let _span = enter_trace_span!( + M, + "validate_operand", + "recursive={recursive}, reset_provenance_and_padding={reset_provenance_and_padding}, val={val:?}" + ); + // Note that we *could* actually be in CTFE here with `-Zextra-const-ub-checks`, but it's // still correct to not use `ctfe_mode`: that mode is for validation of the final constant // value, it rules out things like `UnsafeCell` in awkward places. diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 5aea91233bd..d5970b69baf 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -6,7 +6,6 @@ use std::num::NonZero; use rustc_abi::{FieldIdx, FieldsShape, VariantIdx, Variants}; use rustc_index::IndexVec; use rustc_middle::mir::interpret::InterpResult; -use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; use tracing::trace; |
