diff options
Diffstat (limited to 'compiler/rustc_const_eval/src')
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/check.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/ops.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/machine.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/errors.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/cast.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/machine.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/memory.rs | 31 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/operand.rs | 79 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/place.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/projection.rs | 4 |
10 files changed, 86 insertions, 65 deletions
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 2cbf242fcf2..463a66d4e2e 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -666,6 +666,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } + // This can be called on stable via the `vec!` macro. if tcx.is_lang_item(callee, LangItem::ExchangeMalloc) { self.check_op(ops::HeapAllocation); return; diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 6eb33c29e1d..5c4a899f28a 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -402,7 +402,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow { DiagImportance::Secondary } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - ccx.dcx().create_err(errors::InteriorMutableDataRefer { + ccx.dcx().create_err(errors::InteriorMutableRefEscaping { span, opt_help: matches!(ccx.const_kind(), hir::ConstContext::Static(_)), kind: ccx.const_kind(), @@ -430,12 +430,12 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { match self.0 { - hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::UnallowedMutableRaw { + hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::MutableRawEscaping { span, kind: ccx.const_kind(), teach: ccx.tcx.sess.teach(E0764), }), - hir::BorrowKind::Ref => ccx.dcx().create_err(errors::UnallowedMutableRefs { + hir::BorrowKind::Ref => ccx.dcx().create_err(errors::MutableRefEscaping { span, kind: ccx.const_kind(), teach: ccx.tcx.sess.teach(E0764), diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 4aec74595bc..2db43a0f787 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -140,7 +140,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> { #[inline(always)] fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> { - self.iter().filter_map(move |(k, v)| f(k, &*v)).collect() + self.iter().filter_map(move |(k, v)| f(k, v)).collect() } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index c60bacb8506..c943236affc 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -118,8 +118,8 @@ pub(crate) struct UnstableConstFn { } #[derive(Diagnostic)] -#[diag(const_eval_unallowed_mutable_refs, code = E0764)] -pub(crate) struct UnallowedMutableRefs { +#[diag(const_eval_mutable_ref_escaping, code = E0764)] +pub(crate) struct MutableRefEscaping { #[primary_span] pub span: Span, pub kind: ConstContext, @@ -128,8 +128,8 @@ pub(crate) struct UnallowedMutableRefs { } #[derive(Diagnostic)] -#[diag(const_eval_unallowed_mutable_raw, code = E0764)] -pub(crate) struct UnallowedMutableRaw { +#[diag(const_eval_mutable_raw_escaping, code = E0764)] +pub(crate) struct MutableRawEscaping { #[primary_span] pub span: Span, pub kind: ConstContext, @@ -181,8 +181,8 @@ pub(crate) struct UnallowedInlineAsm { } #[derive(Diagnostic)] -#[diag(const_eval_interior_mutable_data_refer, code = E0492)] -pub(crate) struct InteriorMutableDataRefer { +#[diag(const_eval_interior_mutable_ref_escaping, code = E0492)] +pub(crate) struct InteriorMutableRefEscaping { #[primary_span] #[label] pub span: Span, diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 565a7d16242..30b5a8d70bc 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -204,12 +204,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { assert!(src.layout.ty.is_any_ptr()); assert!(cast_to.ty.is_unsafe_ptr()); - // Handle casting any ptr to raw ptr (might be a fat ptr). + // Handle casting any ptr to raw ptr (might be a wide ptr). if cast_to.size == src.layout.size { - // Thin or fat pointer that just has the ptr kind of target type changed. + // Thin or wide pointer that just has the ptr kind of target type changed. return interp_ok(ImmTy::from_immediate(**src, cast_to)); } else { - // Casting the metadata away from a fat ptr. + // Casting the metadata away from a wide 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()); diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 0f796c31222..89d49ba046e 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -395,7 +395,7 @@ pub trait Machine<'tcx>: Sized { /// /// This should take care of jumping to the next block (one of `targets`) when asm goto /// is triggered, `targets[0]` when the assembly falls through, or diverge in case of - /// `InlineAsmOptions::NORETURN` being set. + /// naked_asm! or `InlineAsmOptions::NORETURN` being set. fn eval_inline_asm( _ecx: &mut InterpCx<'tcx, Self>, _template: &'tcx [InlineAsmTemplatePiece], diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index e6ab8ca12a8..7700eb792ef 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -993,11 +993,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { bytes } - /// Find leaked allocations. Allocations reachable from `static_roots` or a `Global` allocation - /// are not considered leaked, as well as leaks whose kind's `may_leak()` returns true. - pub fn find_leaked_allocations( - &self, - static_roots: &[AllocId], + /// Find leaked allocations, remove them from memory and return them. Allocations reachable from + /// `static_roots` or a `Global` allocation are not considered leaked, as well as leaks whose + /// kind's `may_leak()` returns true. + /// + /// This is highly destructive, no more execution can happen after this! + pub fn take_leaked_allocations( + &mut self, + static_roots: impl FnOnce(&Self) -> &[AllocId], ) -> Vec<(AllocId, MemoryKind<M::MemoryKind>, Allocation<M::Provenance, M::AllocExtra, M::Bytes>)> { // Collect the set of allocations that are *reachable* from `Global` allocations. @@ -1008,7 +1011,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.memory.alloc_map.filter_map_collect(move |&id, &(kind, _)| { if Some(kind) == global_kind { Some(id) } else { None } }); - todo.extend(static_roots); + todo.extend(static_roots(self)); while let Some(id) = todo.pop() { if reachable.insert(id) { // This is a new allocation, add the allocation it points to `todo`. @@ -1023,13 +1026,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking. - self.memory.alloc_map.filter_map_collect(|id, (kind, alloc)| { - if kind.may_leak() || reachable.contains(id) { - None - } else { - Some((*id, *kind, alloc.clone())) - } - }) + let leaked: Vec<_> = self.memory.alloc_map.filter_map_collect(|&id, &(kind, _)| { + if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) } + }); + let mut result = Vec::new(); + for &id in leaked.iter() { + let (kind, alloc) = self.memory.alloc_map.remove(&id).unwrap(); + result.push((id, kind, alloc)); + } + result } /// Runs the closure in "validation" mode, which means the machine's memory read hooks will be diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 291664d556a..cd5e2aeca85 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -4,13 +4,14 @@ use std::assert_matches::assert_matches; use either::{Either, Left, Right}; +use rustc_abi as abi; +use rustc_abi::{Abi, HasDataLayout, Size}; use rustc_hir::def::Namespace; use rustc_middle::mir::interpret::ScalarSizeMismatch; use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter}; use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, mir, span_bug, ty}; -use rustc_target::abi::{self, Abi, HasDataLayout, Size}; use tracing::trace; use super::{ @@ -113,28 +114,47 @@ impl<Prov: Provenance> Immediate<Prov> { } /// Assert that this immediate is a valid value for the given ABI. - pub fn assert_matches_abi(self, abi: Abi, cx: &impl HasDataLayout) { + pub fn assert_matches_abi(self, abi: Abi, msg: &str, cx: &impl HasDataLayout) { match (self, abi) { (Immediate::Scalar(scalar), Abi::Scalar(s)) => { - assert_eq!(scalar.size(), s.size(cx)); - if !matches!(s.primitive(), abi::Pointer(..)) { + assert_eq!(scalar.size(), s.size(cx), "{msg}: scalar value has wrong size"); + if !matches!(s.primitive(), abi::Primitive::Pointer(..)) { // This is not a pointer, it should not carry provenance. - assert!(matches!(scalar, Scalar::Int(..))); + assert!( + matches!(scalar, Scalar::Int(..)), + "{msg}: scalar value should be an integer, but has provenance" + ); } } (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { - assert_eq!(a_val.size(), a.size(cx)); - if !matches!(a.primitive(), abi::Pointer(..)) { - assert!(matches!(a_val, Scalar::Int(..))); + assert_eq!( + a_val.size(), + a.size(cx), + "{msg}: first component of scalar pair has wrong size" + ); + if !matches!(a.primitive(), abi::Primitive::Pointer(..)) { + assert!( + matches!(a_val, Scalar::Int(..)), + "{msg}: first component of scalar pair should be an integer, but has provenance" + ); } - assert_eq!(b_val.size(), b.size(cx)); - if !matches!(b.primitive(), abi::Pointer(..)) { - assert!(matches!(b_val, Scalar::Int(..))); + assert_eq!( + b_val.size(), + b.size(cx), + "{msg}: second component of scalar pair has wrong size" + ); + if !matches!(b.primitive(), abi::Primitive::Pointer(..)) { + assert!( + matches!(b_val, Scalar::Int(..)), + "{msg}: second component of scalar pair should be an integer, but has provenance" + ); } } - (Immediate::Uninit, _) => {} + (Immediate::Uninit, _) => { + assert!(abi.is_sized(), "{msg}: unsized immediates are not a thing"); + } _ => { - bug!("value {self:?} does not match ABI {abi:?})",) + bug!("{msg}: value {self:?} does not match ABI {abi:?})",) } } } @@ -241,6 +261,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline(always)] pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self { + // Without a `cx` we cannot call `assert_matches_abi`. debug_assert!( match (imm, layout.abi) { (Immediate::Scalar(..), Abi::Scalar(..)) => true, @@ -261,7 +282,6 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_scalar_int(s: ScalarInt, layout: TyAndLayout<'tcx>) -> Self { - assert_eq!(s.size(), layout.size); Self::from_scalar(Scalar::from(s), layout) } @@ -334,7 +354,10 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { /// given layout. // Not called `offset` to avoid confusion with the trait method. fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self { - debug_assert!(layout.is_sized(), "unsized immediates are not a thing"); + // Verify that the input matches its type. + if cfg!(debug_assertions) { + self.assert_matches_abi(self.layout.abi, "invalid input to Immediate::offset", cx); + } // `ImmTy` have already been checked to be in-bounds, so we can just check directly if this // remains in-bounds. This cannot actually be violated since projections are type-checked // and bounds-checked. @@ -368,32 +391,14 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { // the field covers the entire type _ if layout.size == self.layout.size => { assert_eq!(offset.bytes(), 0); - assert!( - match (self.layout.abi, layout.abi) { - (Abi::Scalar(l), Abi::Scalar(r)) => l.size(cx) == r.size(cx), - (Abi::ScalarPair(l1, l2), Abi::ScalarPair(r1, r2)) => - l1.size(cx) == r1.size(cx) && l2.size(cx) == r2.size(cx), - _ => false, - }, - "cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}", - self.layout.ty, - layout.ty, - self.layout.abi, - layout.abi, - ); **self } // extract fields from types with `ScalarPair` ABI (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { - assert_matches!(layout.abi, Abi::Scalar(..)); Immediate::from(if offset.bytes() == 0 { - // It is "okay" to transmute from `usize` to a pointer (GVN relies on that). - // So only compare the size. - assert_eq!(layout.size, a.size(cx)); a_val } else { assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi)); - assert_eq!(layout.size, b.size(cx)); b_val }) } @@ -405,6 +410,8 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { self.layout ), }; + // Ensure the new layout matches the new value. + inner_val.assert_matches_abi(layout.abi, "invalid field type in Immediate::offset", cx); ImmTy::from_immediate(inner_val, layout) } @@ -566,7 +573,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); let scalar = alloc.read_scalar( alloc_range(Size::ZERO, size), - /*read_provenance*/ matches!(s, abi::Pointer(_)), + /*read_provenance*/ matches!(s, abi::Primitive::Pointer(_)), )?; Some(ImmTy::from_scalar(scalar, mplace.layout)) } @@ -582,11 +589,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields let a_val = alloc.read_scalar( alloc_range(Size::ZERO, a_size), - /*read_provenance*/ matches!(a, abi::Pointer(_)), + /*read_provenance*/ matches!(a, abi::Primitive::Pointer(_)), )?; let b_val = alloc.read_scalar( alloc_range(b_offset, b_size), - /*read_provenance*/ matches!(b, abi::Pointer(_)), + /*read_provenance*/ matches!(b, abi::Primitive::Pointer(_)), )?; Some(ImmTy::from_immediate(Immediate::ScalarPair(a_val, b_val), mplace.layout)) } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 49656e10f2a..81b926a1b65 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -658,7 +658,11 @@ where // Things can ge wrong in quite weird ways when this is violated. // Unfortunately this is too expensive to do in release builds. if cfg!(debug_assertions) { - src.assert_matches_abi(local_layout.abi, self); + src.assert_matches_abi( + local_layout.abi, + "invalid immediate for given destination place", + self, + ); } } Left(mplace) => { @@ -679,7 +683,7 @@ where ) -> InterpResult<'tcx> { // We use the sizes from `value` below. // Ensure that matches the type of the place it is written to. - value.assert_matches_abi(layout.abi, self); + value.assert_matches_abi(layout.abi, "invalid immediate for given destination place", self); // Note that it is really important that the type here is the right one, and matches the // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here // to handle padding properly, which is only correct if we never look at this data with the diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index cc49d7cebf4..e636090a324 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -82,6 +82,10 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug { self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx) } + /// This does an offset-by-zero, which is effectively a transmute. Note however that + /// not all transmutes are supported by all projectables -- specifically, if this is an + /// `OpTy` or `ImmTy`, the new layout must have almost the same ABI as the old one + /// (only changing the `valid_range` is allowed and turning integers into pointers). fn transmute<M: Machine<'tcx, Provenance = Prov>>( &self, layout: TyAndLayout<'tcx>, |
