1 //! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy.
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 pojections 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.
10 use either::{Left, Right};
12 use rustc_middle::mir;
14 use rustc_middle::ty::layout::LayoutOf;
15 use rustc_target::abi::{self, Abi, VariantIdx};
18 ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy,
22 // FIXME: Working around https://github.com/rust-lang/rust/issues/54385
23 impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
25 Prov: Provenance + 'static,
26 M: Machine<'mir, 'tcx, Provenance = Prov>,
30 /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
31 /// always possible without allocating, so it can take `&self`. Also return the field's layout.
32 /// This supports both struct and array fields.
34 /// This also works for arrays, but then the `usize` index type is restricting.
35 /// For indexing into arrays, use `mplace_index`.
38 base: &MPlaceTy<'tcx, M::Provenance>,
40 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
41 let offset = base.layout.fields.offset(field);
42 let field_layout = base.layout.field(self, field);
44 // Offset may need adjustment for unsized fields.
45 let (meta, offset) = if field_layout.is_unsized() {
46 // Re-use parent metadata to determine dynamic field layout.
47 // With custom DSTS, this *will* execute user-defined code, but the same
48 // happens at run-time so that's okay.
49 match self.size_and_align_of(&base.meta, &field_layout)? {
50 Some((_, align)) => (base.meta, offset.align_to(align)),
52 // For unsized types with an extern type tail we perform no adjustments.
53 // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
54 assert!(matches!(base.meta, MemPlaceMeta::None));
59 // base.meta could be present; we might be accessing a sized field of an unsized
61 (MemPlaceMeta::None, offset)
64 // We do not look at `base.layout.align` nor `field_layout.align`, unlike
65 // codegen -- mostly to see if we can get away with that
66 base.offset_with_meta(offset, meta, field_layout, self)
69 /// Gets the place of a field inside the place, and also the field's type.
70 /// Just a convenience function, but used quite a bit.
71 /// This is the only projection that might have a side-effect: We cannot project
72 /// into the field of a local `ScalarPair`, we have to first allocate it.
75 base: &PlaceTy<'tcx, M::Provenance>,
77 ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
78 // FIXME: We could try to be smarter and avoid allocation for fields that span the
80 let base = self.force_allocation(base)?;
81 Ok(self.mplace_field(&base, field)?.into())
86 base: &OpTy<'tcx, M::Provenance>,
88 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
89 let base = match base.as_mplace_or_imm() {
91 // We can reuse the mplace field computation logic for indirect operands.
92 let field = self.mplace_field(mplace, field)?;
93 return Ok(field.into());
95 Right(value) => value,
98 let field_layout = base.layout.field(self, field);
99 let offset = base.layout.fields.offset(field);
100 // This makes several assumptions about what layouts we will encounter; we match what
101 // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
102 let field_val: Immediate<_> = match (*base, base.layout.abi) {
103 // if the entire value is uninit, then so is the field (can happen in ConstProp)
104 (Immediate::Uninit, _) => Immediate::Uninit,
105 // the field contains no information, can be left uninit
106 _ if field_layout.is_zst() => Immediate::Uninit,
107 // the field covers the entire type
108 _ if field_layout.size == base.layout.size => {
109 assert!(match (base.layout.abi, field_layout.abi) {
110 (Abi::Scalar(..), Abi::Scalar(..)) => true,
111 (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
114 assert!(offset.bytes() == 0);
117 // extract fields from types with `ScalarPair` ABI
118 (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
119 assert!(matches!(field_layout.abi, Abi::Scalar(..)));
120 Immediate::from(if offset.bytes() == 0 {
121 debug_assert_eq!(field_layout.size, a.size(self));
124 debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
125 debug_assert_eq!(field_layout.size, b.size(self));
129 // everything else is a bug
132 "invalid field access on immediate {}, layout {:#?}",
138 Ok(ImmTy::from_immediate(field_val, field_layout).into())
143 pub fn mplace_downcast(
145 base: &MPlaceTy<'tcx, M::Provenance>,
147 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
148 // Downcasts only change the layout.
149 // (In particular, no check about whether this is even the active variant -- that's by design,
150 // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
151 assert!(!base.meta.has_meta());
152 let mut base = *base;
153 base.layout = base.layout.for_variant(self, variant);
157 pub fn place_downcast(
159 base: &PlaceTy<'tcx, M::Provenance>,
161 ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
162 // Downcast just changes the layout
163 let mut base = base.clone();
164 base.layout = base.layout.for_variant(self, variant);
168 pub fn operand_downcast(
170 base: &OpTy<'tcx, M::Provenance>,
172 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
173 // Downcast just changes the layout
174 let mut base = base.clone();
175 base.layout = base.layout.for_variant(self, variant);
182 pub fn operand_index(
184 base: &OpTy<'tcx, M::Provenance>,
186 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
187 // Not using the layout method because we want to compute on u64
188 match base.layout.fields {
189 abi::FieldsShape::Array { stride, count: _ } => {
190 // `count` is nonsense for slices, use the dynamic length instead.
191 let len = base.len(self)?;
193 // This can only be reached in ConstProp and non-rustc-MIR.
194 throw_ub!(BoundsCheckFailed { len, index });
196 let offset = stride * index; // `Size` multiplication
197 // All fields have the same layout.
198 let field_layout = base.layout.field(self, 0);
199 base.offset(offset, field_layout, self)
203 "`mplace_index` called on non-array type {:?}",
209 /// Iterates over all fields of an array. Much more efficient than doing the
210 /// same by repeatedly calling `operand_index`.
211 pub fn operand_array_fields<'a>(
213 base: &'a OpTy<'tcx, Prov>,
214 ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Prov>>> + 'a> {
215 let len = base.len(self)?; // also asserts that we have a type where this makes sense
216 let abi::FieldsShape::Array { stride, .. } = base.layout.fields else {
217 span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
219 let field_layout = base.layout.field(self, 0);
220 let dl = &self.tcx.data_layout;
221 // `Size` multiplication
222 Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl)))
225 /// Index into an array.
228 base: &MPlaceTy<'tcx, M::Provenance>,
230 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
231 Ok(self.operand_index(&base.into(), index)?.assert_mem_place())
236 base: &PlaceTy<'tcx, M::Provenance>,
238 ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
239 // There's not a lot we can do here, since we cannot have a place to a part of a local. If
240 // we are accessing the only element of a 1-element array, it's still the entire local...
241 // that doesn't seem worth it.
242 let base = self.force_allocation(base)?;
243 Ok(self.mplace_index(&base, index)?.into())
246 //# ConstantIndex support
248 fn operand_constant_index(
250 base: &OpTy<'tcx, M::Provenance>,
254 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
255 let n = base.len(self)?;
257 // This can only be reached in ConstProp and non-rustc-MIR.
258 throw_ub!(BoundsCheckFailed { len: min_length, index: n });
261 let index = if from_end {
262 assert!(0 < offset && offset <= min_length);
263 n.checked_sub(offset).unwrap()
265 assert!(offset < min_length);
269 self.operand_index(base, index)
272 fn place_constant_index(
274 base: &PlaceTy<'tcx, M::Provenance>,
278 ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
279 let base = self.force_allocation(base)?;
281 .operand_constant_index(&base.into(), offset, min_length, from_end)?
290 base: &OpTy<'tcx, M::Provenance>,
294 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
295 let len = base.len(self)?; // also asserts that we have a type where this makes sense
296 let actual_to = if from_end {
297 if from.checked_add(to).map_or(true, |to| to > len) {
298 // This can only be reached in ConstProp and non-rustc-MIR.
299 throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) });
301 len.checked_sub(to).unwrap()
306 // Not using layout method because that works with usize, and does not work with slices
307 // (that have count 0 in their layout).
308 let from_offset = match base.layout.fields {
309 abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked
311 span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout)
315 // Compute meta and new layout
316 let inner_len = actual_to.checked_sub(from).unwrap();
317 let (meta, ty) = match base.layout.ty.kind() {
318 // It is not nice to match on the type, but that seems to be the only way to
320 ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)),
322 let len = Scalar::from_machine_usize(inner_len, self);
323 (MemPlaceMeta::Meta(len), base.layout.ty)
326 span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty)
329 let layout = self.layout_of(ty)?;
330 base.offset_with_meta(from_offset, meta, layout, self)
333 pub fn place_subslice(
335 base: &PlaceTy<'tcx, M::Provenance>,
339 ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
340 let base = self.force_allocation(base)?;
341 Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into())
344 //# Applying a general projection
346 /// Projects into a place.
347 #[instrument(skip(self), level = "trace")]
348 pub fn place_projection(
350 base: &PlaceTy<'tcx, M::Provenance>,
351 proj_elem: mir::PlaceElem<'tcx>,
352 ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
353 use rustc_middle::mir::ProjectionElem::*;
356 let mut place = base.clone();
357 place.layout = self.layout_of(ty)?;
360 Field(field, _) => self.place_field(base, field.index())?,
361 Downcast(_, variant) => self.place_downcast(base, variant)?,
362 Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
364 let layout = self.layout_of(self.tcx.types.usize)?;
365 let n = self.local_to_op(self.frame(), local, Some(layout))?;
366 let n = self.read_machine_usize(&n)?;
367 self.place_index(base, n)?
369 ConstantIndex { offset, min_length, from_end } => {
370 self.place_constant_index(base, offset, min_length, from_end)?
372 Subslice { from, to, from_end } => self.place_subslice(base, from, to, from_end)?,
376 #[instrument(skip(self), level = "trace")]
377 pub fn operand_projection(
379 base: &OpTy<'tcx, M::Provenance>,
380 proj_elem: mir::PlaceElem<'tcx>,
381 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
382 use rustc_middle::mir::ProjectionElem::*;
385 let mut op = base.clone();
386 op.layout = self.layout_of(ty)?;
389 Field(field, _) => self.operand_field(base, field.index())?,
390 Downcast(_, variant) => self.operand_downcast(base, variant)?,
391 Deref => self.deref_operand(base)?.into(),
393 let layout = self.layout_of(self.tcx.types.usize)?;
394 let n = self.local_to_op(self.frame(), local, Some(layout))?;
395 let n = self.read_machine_usize(&n)?;
396 self.operand_index(base, n)?
398 ConstantIndex { offset, min_length, from_end } => {
399 self.operand_constant_index(base, offset, min_length, from_end)?
401 Subslice { from, to, from_end } => self.operand_subslice(base, from, to, from_end)?,