]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_const_eval/src/interpret/projection.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / compiler / rustc_const_eval / src / interpret / projection.rs
CommitLineData
064997fb
FG
1//! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy.
2//!
f2b60f7d 3//! OpTy and PlaceTy generally work by "let's see if we are actually an MPlaceTy, and do something custom if not".
064997fb
FG
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.
9
064997fb
FG
10use rustc_middle::mir;
11use rustc_middle::ty;
add651ee 12use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
fe692bf9 13use rustc_middle::ty::Ty;
add651ee
FG
14use rustc_middle::ty::TyCtxt;
15use rustc_target::abi::HasDataLayout;
16use rustc_target::abi::Size;
17use rustc_target::abi::{self, VariantIdx};
18
19use super::{InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar};
064997fb 20
add651ee
FG
21/// A thing that we can project into, and that has a layout.
22pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
23 /// Get the layout.
24 fn layout(&self) -> TyAndLayout<'tcx>;
25
26 /// Get the metadata of a wide value.
27 fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
28 &self,
29 ecx: &InterpCx<'mir, 'tcx, M>,
30 ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>>;
31
32 fn len<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
33 &self,
34 ecx: &InterpCx<'mir, 'tcx, M>,
35 ) -> InterpResult<'tcx, u64> {
36 self.meta(ecx)?.len(self.layout(), ecx)
37 }
38
39 /// Offset the value by the given amount, replacing the layout and metadata.
40 fn offset_with_meta(
41 &self,
42 offset: Size,
43 meta: MemPlaceMeta<Prov>,
44 layout: TyAndLayout<'tcx>,
45 cx: &impl HasDataLayout,
46 ) -> InterpResult<'tcx, Self>;
47
48 fn offset(
49 &self,
50 offset: Size,
51 layout: TyAndLayout<'tcx>,
52 cx: &impl HasDataLayout,
53 ) -> InterpResult<'tcx, Self> {
54 assert!(layout.is_sized());
55 self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
56 }
57
58 fn transmute(
59 &self,
60 layout: TyAndLayout<'tcx>,
61 cx: &impl HasDataLayout,
62 ) -> InterpResult<'tcx, Self> {
63 assert_eq!(self.layout().size, layout.size);
64 self.offset_with_meta(Size::ZERO, MemPlaceMeta::None, layout, cx)
65 }
66
67 /// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
68 /// reading from this thing.
69 fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
70 &self,
71 ecx: &InterpCx<'mir, 'tcx, M>,
72 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
73}
064997fb
FG
74
75// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
76impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
77where
f2b60f7d 78 Prov: Provenance + 'static,
064997fb
FG
79 M: Machine<'mir, 'tcx, Provenance = Prov>,
80{
064997fb
FG
81 /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
82 /// always possible without allocating, so it can take `&self`. Also return the field's layout.
add651ee 83 /// This supports both struct and array fields, but not slices!
064997fb
FG
84 ///
85 /// This also works for arrays, but then the `usize` index type is restricting.
86 /// For indexing into arrays, use `mplace_index`.
add651ee 87 pub fn project_field<P: Projectable<'tcx, M::Provenance>>(
064997fb 88 &self,
add651ee 89 base: &P,
064997fb 90 field: usize,
add651ee
FG
91 ) -> InterpResult<'tcx, P> {
92 // Slices nominally have length 0, so they will panic somewhere in `fields.offset`.
93 debug_assert!(
94 !matches!(base.layout().ty.kind(), ty::Slice(..)),
95 "`field` projection called on a slice -- call `index` projection instead"
96 );
97 let offset = base.layout().fields.offset(field);
98 let field_layout = base.layout().field(self, field);
064997fb
FG
99
100 // Offset may need adjustment for unsized fields.
101 let (meta, offset) = if field_layout.is_unsized() {
add651ee
FG
102 if base.layout().is_sized() {
103 // An unsized field of a sized type? Sure...
104 // But const-prop actually feeds us such nonsense MIR! (see test `const_prop/issue-86351.rs`)
105 throw_inval!(ConstPropNonsense);
106 }
107 let base_meta = base.meta(self)?;
064997fb
FG
108 // Re-use parent metadata to determine dynamic field layout.
109 // With custom DSTS, this *will* execute user-defined code, but the same
110 // happens at run-time so that's okay.
add651ee
FG
111 match self.size_and_align_of(&base_meta, &field_layout)? {
112 Some((_, align)) => (base_meta, offset.align_to(align)),
064997fb
FG
113 None => {
114 // For unsized types with an extern type tail we perform no adjustments.
115 // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
add651ee
FG
116 assert!(matches!(base_meta, MemPlaceMeta::None));
117 (base_meta, offset)
064997fb
FG
118 }
119 }
120 } else {
add651ee 121 // base_meta could be present; we might be accessing a sized field of an unsized
064997fb
FG
122 // struct.
123 (MemPlaceMeta::None, offset)
124 };
125
064997fb
FG
126 base.offset_with_meta(offset, meta, field_layout, self)
127 }
128
add651ee
FG
129 /// Downcasting to an enum variant.
130 pub fn project_downcast<P: Projectable<'tcx, M::Provenance>>(
064997fb 131 &self,
add651ee 132 base: &P,
064997fb 133 variant: VariantIdx,
add651ee
FG
134 ) -> InterpResult<'tcx, P> {
135 assert!(!base.meta(self)?.has_meta());
064997fb
FG
136 // Downcasts only change the layout.
137 // (In particular, no check about whether this is even the active variant -- that's by design,
138 // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
add651ee
FG
139 // So we just "offset" by 0.
140 let layout = base.layout().for_variant(self, variant);
141 if layout.abi.is_uninhabited() {
142 // `read_discriminant` should have excluded uninhabited variants... but ConstProp calls
143 // us on dead code.
144 throw_inval!(ConstPropNonsense)
145 }
146 // This cannot be `transmute` as variants *can* have a smaller size than the entire enum.
147 base.offset(Size::ZERO, layout, self)
064997fb
FG
148 }
149
add651ee
FG
150 /// Compute the offset and field layout for accessing the given index.
151 pub fn project_index<P: Projectable<'tcx, M::Provenance>>(
064997fb 152 &self,
add651ee 153 base: &P,
064997fb 154 index: u64,
add651ee 155 ) -> InterpResult<'tcx, P> {
064997fb 156 // Not using the layout method because we want to compute on u64
add651ee 157 let (offset, field_layout) = match base.layout().fields {
064997fb
FG
158 abi::FieldsShape::Array { stride, count: _ } => {
159 // `count` is nonsense for slices, use the dynamic length instead.
160 let len = base.len(self)?;
161 if index >= len {
162 // This can only be reached in ConstProp and non-rustc-MIR.
163 throw_ub!(BoundsCheckFailed { len, index });
164 }
165 let offset = stride * index; // `Size` multiplication
166 // All fields have the same layout.
add651ee
FG
167 let field_layout = base.layout().field(self, 0);
168 (offset, field_layout)
064997fb
FG
169 }
170 _ => span_bug!(
171 self.cur_span(),
172 "`mplace_index` called on non-array type {:?}",
add651ee 173 base.layout().ty
064997fb 174 ),
064997fb 175 };
064997fb 176
add651ee 177 base.offset(offset, field_layout, self)
064997fb
FG
178 }
179
add651ee 180 fn project_constant_index<P: Projectable<'tcx, M::Provenance>>(
064997fb 181 &self,
add651ee 182 base: &P,
064997fb
FG
183 offset: u64,
184 min_length: u64,
185 from_end: bool,
add651ee 186 ) -> InterpResult<'tcx, P> {
064997fb
FG
187 let n = base.len(self)?;
188 if n < min_length {
189 // This can only be reached in ConstProp and non-rustc-MIR.
190 throw_ub!(BoundsCheckFailed { len: min_length, index: n });
191 }
192
193 let index = if from_end {
194 assert!(0 < offset && offset <= min_length);
195 n.checked_sub(offset).unwrap()
196 } else {
197 assert!(offset < min_length);
198 offset
199 };
200
add651ee 201 self.project_index(base, index)
064997fb
FG
202 }
203
add651ee
FG
204 /// Iterates over all fields of an array. Much more efficient than doing the
205 /// same by repeatedly calling `operand_index`.
206 pub fn project_array_fields<'a, P: Projectable<'tcx, M::Provenance>>(
207 &self,
208 base: &'a P,
209 ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, P>> + 'a>
210 where
211 'tcx: 'a,
212 {
213 let abi::FieldsShape::Array { stride, .. } = base.layout().fields else {
214 span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
215 };
216 let len = base.len(self)?;
217 let field_layout = base.layout().field(self, 0);
218 let tcx: TyCtxt<'tcx> = *self.tcx;
219 // `Size` multiplication
220 Ok((0..len).map(move |i| {
221 base.offset_with_meta(stride * i, MemPlaceMeta::None, field_layout, &tcx)
222 }))
064997fb
FG
223 }
224
add651ee
FG
225 /// Subslicing
226 fn project_subslice<P: Projectable<'tcx, M::Provenance>>(
064997fb 227 &self,
add651ee 228 base: &P,
064997fb
FG
229 from: u64,
230 to: u64,
231 from_end: bool,
add651ee 232 ) -> InterpResult<'tcx, P> {
064997fb
FG
233 let len = base.len(self)?; // also asserts that we have a type where this makes sense
234 let actual_to = if from_end {
235 if from.checked_add(to).map_or(true, |to| to > len) {
236 // This can only be reached in ConstProp and non-rustc-MIR.
237 throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) });
238 }
239 len.checked_sub(to).unwrap()
240 } else {
241 to
242 };
243
244 // Not using layout method because that works with usize, and does not work with slices
245 // (that have count 0 in their layout).
add651ee 246 let from_offset = match base.layout().fields {
064997fb
FG
247 abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked
248 _ => {
add651ee
FG
249 span_bug!(
250 self.cur_span(),
251 "unexpected layout of index access: {:#?}",
252 base.layout()
253 )
064997fb
FG
254 }
255 };
256
257 // Compute meta and new layout
258 let inner_len = actual_to.checked_sub(from).unwrap();
add651ee 259 let (meta, ty) = match base.layout().ty.kind() {
064997fb
FG
260 // It is not nice to match on the type, but that seems to be the only way to
261 // implement this.
fe692bf9
FG
262 ty::Array(inner, _) => {
263 (MemPlaceMeta::None, Ty::new_array(self.tcx.tcx, *inner, inner_len))
264 }
064997fb 265 ty::Slice(..) => {
9ffffee4 266 let len = Scalar::from_target_usize(inner_len, self);
add651ee 267 (MemPlaceMeta::Meta(len), base.layout().ty)
064997fb
FG
268 }
269 _ => {
add651ee
FG
270 span_bug!(
271 self.cur_span(),
272 "cannot subslice non-array type: `{:?}`",
273 base.layout().ty
274 )
064997fb
FG
275 }
276 };
277 let layout = self.layout_of(ty)?;
064997fb 278
add651ee 279 base.offset_with_meta(from_offset, meta, layout, self)
064997fb
FG
280 }
281
add651ee 282 /// Applying a general projection
064997fb 283 #[instrument(skip(self), level = "trace")]
add651ee
FG
284 pub fn project<P>(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpResult<'tcx, P>
285 where
286 P: Projectable<'tcx, M::Provenance> + From<MPlaceTy<'tcx, M::Provenance>> + std::fmt::Debug,
287 {
064997fb
FG
288 use rustc_middle::mir::ProjectionElem::*;
289 Ok(match proj_elem {
add651ee
FG
290 OpaqueCast(ty) => base.transmute(self.layout_of(ty)?, self)?,
291 Field(field, _) => self.project_field(base, field.index())?,
292 Downcast(_, variant) => self.project_downcast(base, variant)?,
293 Deref => self.deref_pointer(&base.to_op(self)?)?.into(),
064997fb
FG
294 Index(local) => {
295 let layout = self.layout_of(self.tcx.types.usize)?;
296 let n = self.local_to_op(self.frame(), local, Some(layout))?;
9ffffee4 297 let n = self.read_target_usize(&n)?;
add651ee 298 self.project_index(base, n)?
064997fb
FG
299 }
300 ConstantIndex { offset, min_length, from_end } => {
add651ee 301 self.project_constant_index(base, offset, min_length, from_end)?
064997fb 302 }
add651ee 303 Subslice { from, to, from_end } => self.project_subslice(base, from, to, from_end)?,
064997fb
FG
304 })
305 }
306}