1//! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy.
2//!
3//! OpTy and PlaceTy generally work by "let's see if we are actually an MPlaceTy, and do something custom if not".
4//! For PlaceTy, the custom thing is basically always to call `force_allocation` and then use the MPlaceTy logic anyway.
5//! For OpTy, the custom thing on field projections has to be pretty clever (since `Operand::Immediate` can have fields),
6//! but for array/slice operations it only has to worry about `Operand::Uninit`. That makes the value part trivial,
7//! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually
8//! implement the logic on OpTy, and MPlaceTy calls that.
910use std::marker::PhantomData;
11use std::ops::Range;
1213use rustc_abi::{selfas abi, FieldIdx, Size, VariantIdx};
14use rustc_middle::ty::Ty;
15use rustc_middle::ty::layout::TyAndLayout;
16use rustc_middle::{bug, mir, span_bug, ty};
17use tracing::{debug, instrument};
1819use super::{
20InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar, err_ub,
21interp_ok, throw_ub, throw_unsup,
22};
2324/// Describes the constraints placed on offset-projections.
25#[derive(#[automatically_derived]
impl ::core::marker::Copy for OffsetMode { }Copy, #[automatically_derived]
impl ::core::clone::Clone for OffsetMode {
#[inline]
fn clone(&self) -> OffsetMode { *self }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for OffsetMode {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
OffsetMode::Inbounds => "Inbounds",
OffsetMode::Wrapping => "Wrapping",
})
}
}Debug)]
26pub enum OffsetMode {
27/// The offset has to be inbounds, like `ptr::offset`.
28Inbounds,
29/// No constraints, just wrap around the edge of the address space.
30Wrapping,
31}
3233/// A thing that we can project into, and that has a layout.
34pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
35/// Get the layout.
36fn layout(&self) -> TyAndLayout<'tcx>;
3738/// Get the metadata of a wide value.
39fn meta(&self) -> MemPlaceMeta<Prov>;
4041/// Get the length of a slice/string/array stored here.
42fn len<M: Machine<'tcx, Provenance = Prov>>(
43&self,
44 ecx: &InterpCx<'tcx, M>,
45 ) -> InterpResult<'tcx, u64> {
46let layout = self.layout();
47if layout.is_unsized() {
48// We need to consult `meta` metadata
49match layout.ty.kind() {
50 ty::Slice(..) | ty::Str => self.meta().unwrap_meta().to_target_usize(ecx),
51_ => ::rustc_middle::util::bug::bug_fmt(format_args!("len not supported on unsized type {0:?}",
layout.ty))bug!("len not supported on unsized type {:?}", layout.ty),
52 }
53 } else {
54// Go through the layout. There are lots of types that support a length,
55 // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
56match layout.fields {
57 abi::FieldsShape::Array { count, .. } => interp_ok(count),
58_ => ::rustc_middle::util::bug::bug_fmt(format_args!("len not supported on sized type {0:?}",
layout.ty))bug!("len not supported on sized type {:?}", layout.ty),
59 }
60 }
61 }
6263/// Offset the value by the given amount, replacing the layout and metadata.
64fn offset_with_meta<M: Machine<'tcx, Provenance = Prov>>(
65&self,
66 offset: Size,
67 mode: OffsetMode,
68 meta: MemPlaceMeta<Prov>,
69 layout: TyAndLayout<'tcx>,
70 ecx: &InterpCx<'tcx, M>,
71 ) -> InterpResult<'tcx, Self>;
7273fn offset<M: Machine<'tcx, Provenance = Prov>>(
74&self,
75 offset: Size,
76 layout: TyAndLayout<'tcx>,
77 ecx: &InterpCx<'tcx, M>,
78 ) -> InterpResult<'tcx, Self> {
79if !layout.is_sized() {
::core::panicking::panic("assertion failed: layout.is_sized()")
};assert!(layout.is_sized());
80// We sometimes do pointer arithmetic with this function, disregarding the source type.
81 // So we don't check the sizes here.
82self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx)
83 }
8485/// This does an offset-by-zero, which is effectively a transmute. Note however that
86 /// not all transmutes are supported by all projectables -- specifically, if this is an
87 /// `OpTy` or `ImmTy`, the new layout must have almost the same ABI as the old one
88 /// (only changing the `valid_range` is allowed and turning integers into pointers).
89fn transmute<M: Machine<'tcx, Provenance = Prov>>(
90&self,
91 layout: TyAndLayout<'tcx>,
92 ecx: &InterpCx<'tcx, M>,
93 ) -> InterpResult<'tcx, Self> {
94if !(self.layout().is_sized() && layout.is_sized()) {
::core::panicking::panic("assertion failed: self.layout().is_sized() && layout.is_sized()")
};assert!(self.layout().is_sized() && layout.is_sized());
95match (&self.layout().size, &layout.size) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(self.layout().size, layout.size);
96self.offset_with_meta(Size::ZERO, OffsetMode::Wrapping, MemPlaceMeta::None, layout, ecx)
97 }
9899/// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
100 /// reading from this thing.
101fn to_op<M: Machine<'tcx, Provenance = Prov>>(
102&self,
103 ecx: &InterpCx<'tcx, M>,
104 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
105}
106107/// A type representing iteration over the elements of an array.
108pub struct ArrayIterator<'a, 'tcx, Prov: Provenance, P: Projectable<'tcx, Prov>> {
109 base: &'a P,
110 range: Range<u64>,
111 stride: Size,
112 field_layout: TyAndLayout<'tcx>,
113 _phantom: PhantomData<Prov>, // otherwise it says `Prov` is never used...
114}
115116impl<'a, 'tcx, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'a, 'tcx, Prov, P> {
117/// Should be the same `ecx` on each call, and match the one used to create the iterator.
118pub fn next<M: Machine<'tcx, Provenance = Prov>>(
119&mut self,
120 ecx: &InterpCx<'tcx, M>,
121 ) -> InterpResult<'tcx, Option<(u64, P)>> {
122let Some(idx) = self.range.next() else { return interp_ok(None) };
123// We use `Wrapping` here since the offset has already been checked when the iterator was created.
124interp_ok(Some((
125idx,
126self.base.offset_with_meta(
127self.stride * idx,
128 OffsetMode::Wrapping,
129 MemPlaceMeta::None,
130self.field_layout,
131ecx,
132 )?,
133 )))
134 }
135}
136137// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
138impl<'tcx, Prov, M> InterpCx<'tcx, M>
139where
140Prov: Provenance,
141 M: Machine<'tcx, Provenance = Prov>,
142{
143/// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
144 /// always possible without allocating, so it can take `&self`. Also return the field's layout.
145 /// This supports both struct and array fields, but not slices!
146 ///
147 /// This also works for arrays, but then the `FieldIdx` index type is restricting.
148 /// For indexing into arrays, use [`Self::project_index`].
149pub fn project_field<P: Projectable<'tcx, M::Provenance>>(
150&self,
151 base: &P,
152 field: FieldIdx,
153 ) -> InterpResult<'tcx, P> {
154// Slices nominally have length 0, so they will panic somewhere in `fields.offset`.
155if true {
if !!#[allow(non_exhaustive_omitted_patterns)] match base.layout().ty.kind()
{
ty::Slice(..) => true,
_ => false,
} {
{
::core::panicking::panic_fmt(format_args!("`field` projection called on a slice -- call `index` projection instead"));
}
};
};debug_assert!(
156 !matches!(base.layout().ty.kind(), ty::Slice(..)),
157"`field` projection called on a slice -- call `index` projection instead"
158);
159let offset = base.layout().fields.offset(field.as_usize());
160// Computing the layout does normalization, so we get a normalized type out of this
161 // even if the field type is non-normalized (possible e.g. via associated types).
162let field_layout = base.layout().field(self, field.as_usize());
163164// Offset may need adjustment for unsized fields.
165let (meta, offset) = if field_layout.is_unsized() {
166if !!base.layout().is_sized() {
::core::panicking::panic("assertion failed: !base.layout().is_sized()")
};assert!(!base.layout().is_sized());
167let base_meta = base.meta();
168// Re-use parent metadata to determine dynamic field layout.
169 // With custom DSTS, this *will* execute user-defined code, but the same
170 // happens at run-time so that's okay.
171match self.size_and_align_from_meta(&base_meta, &field_layout)? {
172Some((_, align)) => {
173// For packed types, we need to cap alignment.
174let align = if let ty::Adt(def, _) = base.layout().ty.kind()
175 && let Some(packed) = def.repr().pack
176 {
177align.min(packed)
178 } else {
179align180 };
181 (base_meta, offset.align_to(align))
182 }
183Noneif offset == Size::ZERO => {
184// If the offset is 0, then rounding it up to alignment wouldn't change anything,
185 // so we can do this even for types where we cannot determine the alignment.
186(base_meta, offset)
187 }
188None => {
189// We cannot know the alignment of this field, so we cannot adjust.
190do yeet ::rustc_middle::mir::interpret::InterpErrorKind::Unsupported(::rustc_middle::mir::interpret::UnsupportedOpInfo::ExternTypeField)throw_unsup!(ExternTypeField)191 }
192 }
193 } else {
194// base_meta could be present; we might be accessing a sized field of an unsized
195 // struct.
196(MemPlaceMeta::None, offset)
197 };
198199base.offset_with_meta(offset, OffsetMode::Inbounds, meta, field_layout, self)
200 }
201202/// Projects multiple fields at once. See [`Self::project_field`] for details.
203pub fn project_fields<P: Projectable<'tcx, M::Provenance>, const N: usize>(
204&self,
205 base: &P,
206 fields: [FieldIdx; N],
207 ) -> InterpResult<'tcx, [P; N]> {
208fields.try_map(|field| self.project_field(base, field))
209 }
210211/// Downcasting to an enum variant.
212pub fn project_downcast<P: Projectable<'tcx, M::Provenance>>(
213&self,
214 base: &P,
215 variant: VariantIdx,
216 ) -> InterpResult<'tcx, P> {
217if !!base.meta().has_meta() {
::core::panicking::panic("assertion failed: !base.meta().has_meta()")
};assert!(!base.meta().has_meta());
218// Downcasts only change the layout.
219 // (In particular, no check about whether this is even the active variant -- that's by design,
220 // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
221 // So we just "offset" by 0.
222let layout = base.layout().for_variant(self, variant);
223// This variant may in fact be uninhabited.
224 // See <https://github.com/rust-lang/rust/issues/120337>.
225226 // This cannot be `transmute` as variants *can* have a smaller size than the entire enum.
227base.offset(Size::ZERO, layout, self)
228 }
229230/// Compute the offset and field layout for accessing the given index.
231pub fn project_index<P: Projectable<'tcx, M::Provenance>>(
232&self,
233 base: &P,
234 index: u64,
235 ) -> InterpResult<'tcx, P> {
236// Not using the layout method because we want to compute on u64
237let (offset, field_layout) = match base.layout().fields {
238 abi::FieldsShape::Array { stride, count: _ } => {
239// `count` is nonsense for slices, use the dynamic length instead.
240let len = base.len(self)?;
241if index >= len {
242// This can only be reached in ConstProp and non-rustc-MIR.
243do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::BoundsCheckFailed {
len,
index,
});throw_ub!(BoundsCheckFailed { len, index });
244 }
245// With raw slices, `len` can be so big that this *can* overflow.
246let offset = self247 .compute_size_in_bytes(stride, index)
248 .ok_or_else(|| ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::PointerArithOverflow)err_ub!(PointerArithOverflow))?;
249250// All fields have the same layout.
251let field_layout = base.layout().field(self, 0);
252 (offset, field_layout)
253 }
254_ => ::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
format_args!("`project_index` called on non-array type {0:?}",
base.layout().ty))span_bug!(
255self.cur_span(),
256"`project_index` called on non-array type {:?}",
257 base.layout().ty
258 ),
259 };
260261base.offset(offset, field_layout, self)
262 }
263264/// Converts a repr(simd) value into an array of the right size, such that `project_index`
265 /// accesses the SIMD elements. Also returns the number of elements.
266pub fn project_to_simd<P: Projectable<'tcx, M::Provenance>>(
267&self,
268 base: &P,
269 ) -> InterpResult<'tcx, (P, u64)> {
270if !base.layout().ty.ty_adt_def().unwrap().repr().simd() {
::core::panicking::panic("assertion failed: base.layout().ty.ty_adt_def().unwrap().repr().simd()")
};assert!(base.layout().ty.ty_adt_def().unwrap().repr().simd());
271// SIMD types must be newtypes around arrays, so all we have to do is project to their only field.
272let array = self.project_field(base, FieldIdx::ZERO)?;
273let len = array.len(self)?;
274interp_ok((array, len))
275 }
276277fn project_constant_index<P: Projectable<'tcx, M::Provenance>>(
278&self,
279 base: &P,
280 offset: u64,
281 min_length: u64,
282 from_end: bool,
283 ) -> InterpResult<'tcx, P> {
284let n = base.len(self)?;
285if n < min_length {
286// This can only be reached in ConstProp and non-rustc-MIR.
287do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::BoundsCheckFailed {
len: min_length,
index: n,
});throw_ub!(BoundsCheckFailed { len: min_length, index: n });
288 }
289290let index = if from_end {
291if !(0 < offset && offset <= min_length) {
::core::panicking::panic("assertion failed: 0 < offset && offset <= min_length")
};assert!(0 < offset && offset <= min_length);
292n.checked_sub(offset).unwrap()
293 } else {
294if !(offset < min_length) {
::core::panicking::panic("assertion failed: offset < min_length")
};assert!(offset < min_length);
295offset296 };
297298self.project_index(base, index)
299 }
300301/// Iterates over all fields of an array. Much more efficient than doing the
302 /// same by repeatedly calling `project_index`.
303pub fn project_array_fields<'a, P: Projectable<'tcx, M::Provenance>>(
304&self,
305 base: &'a P,
306 ) -> InterpResult<'tcx, ArrayIterator<'a, 'tcx, M::Provenance, P>> {
307let abi::FieldsShape::Array { stride, .. } = base.layout().fields else {
308::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
format_args!("project_array_fields: expected an array layout, got {0:#?}",
base.layout()));span_bug!(
309self.cur_span(),
310"project_array_fields: expected an array layout, got {:#?}",
311 base.layout()
312 );
313 };
314let len = base.len(self)?;
315let field_layout = base.layout().field(self, 0);
316// Ensure that all the offsets are in-bounds once, up-front.
317{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/interpret/projection.rs:317",
"rustc_const_eval::interpret::projection",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/projection.rs"),
::tracing_core::__macro_support::Option::Some(317u32),
::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::projection"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("project_array_fields: {0:?} {1}",
base, len) as &dyn Value))])
});
} else { ; }
};debug!("project_array_fields: {base:?} {len}");
318base.offset(len * stride, self.layout_of(self.tcx.types.unit).unwrap(), self)?;
319// Create the iterator.
320interp_ok(ArrayIterator {
321base,
322 range: 0..len,
323stride,
324field_layout,
325 _phantom: PhantomData,
326 })
327 }
328329/// Subslicing
330fn project_subslice<P: Projectable<'tcx, M::Provenance>>(
331&self,
332 base: &P,
333 from: u64,
334 to: u64,
335 from_end: bool,
336 ) -> InterpResult<'tcx, P> {
337let len = base.len(self)?; // also asserts that we have a type where this makes sense
338let actual_to = if from_end {
339if from.checked_add(to).is_none_or(|to| to > len) {
340// This can only be reached in ConstProp and non-rustc-MIR.
341do yeet ::rustc_middle::mir::interpret::InterpErrorKind::UndefinedBehavior(::rustc_middle::mir::interpret::UndefinedBehaviorInfo::BoundsCheckFailed {
len,
index: from.saturating_add(to),
});throw_ub!(BoundsCheckFailed { len, index: from.saturating_add(to) });
342 }
343len.checked_sub(to).unwrap()
344 } else {
345to346 };
347348// Not using layout method because that works with usize, and does not work with slices
349 // (that have count 0 in their layout).
350let from_offset = match base.layout().fields {
351 abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked
352_ => {
353::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
format_args!("unexpected layout of index access: {0:#?}", base.layout()))span_bug!(
354self.cur_span(),
355"unexpected layout of index access: {:#?}",
356 base.layout()
357 )358 }
359 };
360361// Compute meta and new layout
362let inner_len = actual_to.checked_sub(from).unwrap();
363let (meta, ty) = match base.layout().ty.kind() {
364// It is not nice to match on the type, but that seems to be the only way to
365 // implement this.
366ty::Array(inner, _) => {
367 (MemPlaceMeta::None, Ty::new_array(self.tcx.tcx, *inner, inner_len))
368 }
369 ty::Slice(..) => {
370let len = Scalar::from_target_usize(inner_len, self);
371 (MemPlaceMeta::Meta(len), base.layout().ty)
372 }
373_ => {
374::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
format_args!("cannot subslice non-array type: `{0:?}`", base.layout().ty))span_bug!(
375self.cur_span(),
376"cannot subslice non-array type: `{:?}`",
377 base.layout().ty
378 )379 }
380 };
381let layout = self.layout_of(ty)?;
382383base.offset_with_meta(from_offset, OffsetMode::Inbounds, meta, layout, self)
384 }
385386/// Applying a general projection
387#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("project",
"rustc_const_eval::interpret::projection",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/projection.rs"),
::tracing_core::__macro_support::Option::Some(387u32),
::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::projection"),
::tracing_core::field::FieldSet::new(&["base", "proj_elem"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&base)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&proj_elem)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: InterpResult<'tcx, P> = loop {};
return __tracing_attr_fake_return;
}
{
use rustc_middle::mir::ProjectionElem::*;
interp_ok(match proj_elem {
OpaqueCast(ty) => {
::rustc_middle::util::bug::span_bug_fmt(self.cur_span(),
format_args!("OpaqueCast({0}) encountered after borrowck",
ty))
}
UnwrapUnsafeBinder(target) =>
base.transmute(self.layout_of(target)?, self)?,
Field(field, _) => self.project_field(base, field)?,
Downcast(_, variant) =>
self.project_downcast(base, variant)?,
Deref => self.deref_pointer(&base.to_op(self)?)?.into(),
Index(local) => {
let layout = self.layout_of(self.tcx.types.usize)?;
let n = self.local_to_op(local, Some(layout))?;
let n = self.read_target_usize(&n)?;
self.project_index(base, n)?
}
ConstantIndex { offset, min_length, from_end } => {
self.project_constant_index(base, offset, min_length,
from_end)?
}
Subslice { from, to, from_end } =>
self.project_subslice(base, from, to, from_end)?,
})
}
}
}#[instrument(skip(self), level = "trace")]388pub fn project<P>(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpResult<'tcx, P>
389where
390P: Projectable<'tcx, M::Provenance> + From<MPlaceTy<'tcx, M::Provenance>> + std::fmt::Debug,
391 {
392use rustc_middle::mir::ProjectionElem::*;
393 interp_ok(match proj_elem {
394 OpaqueCast(ty) => {
395span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck")
396 }
397 UnwrapUnsafeBinder(target) => base.transmute(self.layout_of(target)?, self)?,
398 Field(field, _) => self.project_field(base, field)?,
399 Downcast(_, variant) => self.project_downcast(base, variant)?,
400 Deref => self.deref_pointer(&base.to_op(self)?)?.into(),
401 Index(local) => {
402let layout = self.layout_of(self.tcx.types.usize)?;
403let n = self.local_to_op(local, Some(layout))?;
404let n = self.read_target_usize(&n)?;
405self.project_index(base, n)?
406}
407 ConstantIndex { offset, min_length, from_end } => {
408self.project_constant_index(base, offset, min_length, from_end)?
409}
410 Subslice { from, to, from_end } => self.project_subslice(base, from, to, from_end)?,
411 })
412 }
413}