]>
Commit | Line | Data |
---|---|---|
a1dfa0c6 XL |
1 | //! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound |
2 | //! types until we arrive at the leaves, with custom handling for primitive types. | |
3 | ||
ba9703b0 XL |
4 | use rustc_middle::mir::interpret::InterpResult; |
5 | use rustc_middle::ty; | |
6 | use rustc_middle::ty::layout::TyAndLayout; | |
7 | use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; | |
8 | ||
9 | use std::num::NonZeroUsize; | |
a1dfa0c6 | 10 | |
dfeec247 | 11 | use super::{InterpCx, MPlaceTy, Machine, OpTy}; |
a1dfa0c6 XL |
12 | |
13 | // A thing that we can project into, and that has a layout. | |
14 | // This wouldn't have to depend on `Machine` but with the current type inference, | |
15 | // that's just more convenient to work with (avoids repeating all the `Machine` bounds). | |
dc9dc135 | 16 | pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { |
9fa01778 | 17 | /// Gets this value's layout. |
ba9703b0 | 18 | fn layout(&self) -> TyAndLayout<'tcx>; |
a1dfa0c6 | 19 | |
9fa01778 | 20 | /// Makes this into an `OpTy`. |
dfeec247 | 21 | fn to_op(self, ecx: &InterpCx<'mir, 'tcx, M>) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; |
a1dfa0c6 | 22 | |
9fa01778 XL |
23 | /// Creates this from an `MPlaceTy`. |
24 | fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self; | |
a1dfa0c6 | 25 | |
9fa01778 | 26 | /// Projects to the given enum variant. |
a1dfa0c6 XL |
27 | fn project_downcast( |
28 | self, | |
416331ca | 29 | ecx: &InterpCx<'mir, 'tcx, M>, |
a1dfa0c6 | 30 | variant: VariantIdx, |
dc9dc135 | 31 | ) -> InterpResult<'tcx, Self>; |
a1dfa0c6 | 32 | |
9fa01778 | 33 | /// Projects to the n-th field. |
ba9703b0 XL |
34 | fn project_field(self, ecx: &InterpCx<'mir, 'tcx, M>, field: usize) |
35 | -> InterpResult<'tcx, Self>; | |
a1dfa0c6 XL |
36 | } |
37 | ||
38 | // Operands and memory-places are both values. | |
39 | // Places in general are not due to `place_field` having to do `force_allocation`. | |
ba9703b0 | 40 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> { |
a1dfa0c6 | 41 | #[inline(always)] |
ba9703b0 | 42 | fn layout(&self) -> TyAndLayout<'tcx> { |
a1dfa0c6 XL |
43 | self.layout |
44 | } | |
45 | ||
46 | #[inline(always)] | |
47 | fn to_op( | |
48 | self, | |
416331ca | 49 | _ecx: &InterpCx<'mir, 'tcx, M>, |
dc9dc135 | 50 | ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { |
a1dfa0c6 XL |
51 | Ok(self) |
52 | } | |
53 | ||
54 | #[inline(always)] | |
55 | fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { | |
56 | mplace.into() | |
57 | } | |
58 | ||
59 | #[inline(always)] | |
60 | fn project_downcast( | |
61 | self, | |
416331ca | 62 | ecx: &InterpCx<'mir, 'tcx, M>, |
a1dfa0c6 | 63 | variant: VariantIdx, |
dc9dc135 | 64 | ) -> InterpResult<'tcx, Self> { |
a1dfa0c6 XL |
65 | ecx.operand_downcast(self, variant) |
66 | } | |
67 | ||
68 | #[inline(always)] | |
ba9703b0 XL |
69 | fn project_field( |
70 | self, | |
71 | ecx: &InterpCx<'mir, 'tcx, M>, | |
72 | field: usize, | |
73 | ) -> InterpResult<'tcx, Self> { | |
a1dfa0c6 XL |
74 | ecx.operand_field(self, field) |
75 | } | |
76 | } | |
dc9dc135 | 77 | |
ba9703b0 XL |
78 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> |
79 | for MPlaceTy<'tcx, M::PointerTag> | |
80 | { | |
a1dfa0c6 | 81 | #[inline(always)] |
ba9703b0 | 82 | fn layout(&self) -> TyAndLayout<'tcx> { |
a1dfa0c6 XL |
83 | self.layout |
84 | } | |
85 | ||
86 | #[inline(always)] | |
87 | fn to_op( | |
88 | self, | |
416331ca | 89 | _ecx: &InterpCx<'mir, 'tcx, M>, |
dc9dc135 | 90 | ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { |
a1dfa0c6 XL |
91 | Ok(self.into()) |
92 | } | |
93 | ||
94 | #[inline(always)] | |
95 | fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { | |
96 | mplace | |
97 | } | |
98 | ||
99 | #[inline(always)] | |
100 | fn project_downcast( | |
101 | self, | |
416331ca | 102 | ecx: &InterpCx<'mir, 'tcx, M>, |
a1dfa0c6 | 103 | variant: VariantIdx, |
dc9dc135 | 104 | ) -> InterpResult<'tcx, Self> { |
a1dfa0c6 XL |
105 | ecx.mplace_downcast(self, variant) |
106 | } | |
107 | ||
108 | #[inline(always)] | |
ba9703b0 XL |
109 | fn project_field( |
110 | self, | |
111 | ecx: &InterpCx<'mir, 'tcx, M>, | |
112 | field: usize, | |
113 | ) -> InterpResult<'tcx, Self> { | |
a1dfa0c6 XL |
114 | ecx.mplace_field(self, field) |
115 | } | |
116 | } | |
117 | ||
118 | macro_rules! make_value_visitor { | |
9fa01778 | 119 | ($visitor_trait_name:ident, $($mutability:ident)?) => { |
a1dfa0c6 | 120 | // How to traverse a value and what to do when we are at the leaves. |
dc9dc135 XL |
121 | pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { |
122 | type V: Value<'mir, 'tcx, M>; | |
a1dfa0c6 | 123 | |
416331ca | 124 | /// The visitor must have an `InterpCx` in it. |
9fa01778 | 125 | fn ecx(&$($mutability)? self) |
416331ca | 126 | -> &$($mutability)? InterpCx<'mir, 'tcx, M>; |
a1dfa0c6 | 127 | |
f035d41b XL |
128 | /// `read_discriminant` can be hooked for better error messages. |
129 | #[inline(always)] | |
130 | fn read_discriminant( | |
131 | &mut self, | |
132 | op: OpTy<'tcx, M::PointerTag>, | |
133 | ) -> InterpResult<'tcx, VariantIdx> { | |
134 | Ok(self.ecx().read_discriminant(op)?.1) | |
135 | } | |
136 | ||
a1dfa0c6 | 137 | // Recursive actions, ready to be overloaded. |
9fa01778 | 138 | /// Visits the given value, dispatching as appropriate to more specialized visitors. |
a1dfa0c6 | 139 | #[inline(always)] |
dc9dc135 | 140 | fn visit_value(&mut self, v: Self::V) -> InterpResult<'tcx> |
a1dfa0c6 XL |
141 | { |
142 | self.walk_value(v) | |
143 | } | |
9fa01778 | 144 | /// Visits the given value as a union. No automatic recursion can happen here. |
a1dfa0c6 | 145 | #[inline(always)] |
ba9703b0 | 146 | fn visit_union(&mut self, _v: Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> |
a1dfa0c6 XL |
147 | { |
148 | Ok(()) | |
149 | } | |
48663c56 | 150 | /// Visits this value as an aggregate, you are getting an iterator yielding |
dc9dc135 | 151 | /// all the fields (still in an `InterpResult`, you have to do error handling yourself). |
a1dfa0c6 XL |
152 | /// Recurses into the fields. |
153 | #[inline(always)] | |
154 | fn visit_aggregate( | |
155 | &mut self, | |
156 | v: Self::V, | |
dc9dc135 XL |
157 | fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>, |
158 | ) -> InterpResult<'tcx> { | |
a1dfa0c6 XL |
159 | self.walk_aggregate(v, fields) |
160 | } | |
161 | ||
162 | /// Called each time we recurse down to a field of a "product-like" aggregate | |
48663c56 XL |
163 | /// (structs, tuples, arrays and the like, but not enums), passing in old (outer) |
164 | /// and new (inner) value. | |
a1dfa0c6 XL |
165 | /// This gives the visitor the chance to track the stack of nested fields that |
166 | /// we are descending through. | |
167 | #[inline(always)] | |
168 | fn visit_field( | |
169 | &mut self, | |
170 | _old_val: Self::V, | |
171 | _field: usize, | |
172 | new_val: Self::V, | |
dc9dc135 | 173 | ) -> InterpResult<'tcx> { |
a1dfa0c6 XL |
174 | self.visit_value(new_val) |
175 | } | |
a1dfa0c6 | 176 | /// Called when recursing into an enum variant. |
74b04a01 XL |
177 | /// This gives the visitor the chance to track the stack of nested fields that |
178 | /// we are descending through. | |
a1dfa0c6 XL |
179 | #[inline(always)] |
180 | fn visit_variant( | |
181 | &mut self, | |
182 | _old_val: Self::V, | |
183 | _variant: VariantIdx, | |
184 | new_val: Self::V, | |
dc9dc135 | 185 | ) -> InterpResult<'tcx> { |
a1dfa0c6 XL |
186 | self.visit_value(new_val) |
187 | } | |
188 | ||
a1dfa0c6 XL |
189 | // Default recursors. Not meant to be overloaded. |
190 | fn walk_aggregate( | |
191 | &mut self, | |
192 | v: Self::V, | |
dc9dc135 XL |
193 | fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>, |
194 | ) -> InterpResult<'tcx> { | |
a1dfa0c6 XL |
195 | // Now iterate over it. |
196 | for (idx, field_val) in fields.enumerate() { | |
197 | self.visit_field(v, idx, field_val?)?; | |
198 | } | |
199 | Ok(()) | |
200 | } | |
dc9dc135 | 201 | fn walk_value(&mut self, v: Self::V) -> InterpResult<'tcx> |
a1dfa0c6 XL |
202 | { |
203 | trace!("walk_value: type: {}", v.layout().ty); | |
a1dfa0c6 | 204 | |
74b04a01 | 205 | // Special treatment for special types, where the (static) layout is not sufficient. |
e74abb32 | 206 | match v.layout().ty.kind { |
74b04a01 | 207 | // If it is a trait object, switch to the real type that was used to create it. |
a1dfa0c6 XL |
208 | ty::Dynamic(..) => { |
209 | // immediate trait objects are not a thing | |
dfeec247 | 210 | let dest = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); |
a1dfa0c6 XL |
211 | let inner = self.ecx().unpack_dyn_trait(dest)?.1; |
212 | trace!("walk_value: dyn object layout: {:#?}", inner.layout); | |
213 | // recurse with the inner type | |
214 | return self.visit_field(v, 0, Value::from_mem_place(inner)); | |
215 | }, | |
74b04a01 XL |
216 | // Slices do not need special handling here: they have `Array` field |
217 | // placement with length 0, so we enter the `Array` case below which | |
218 | // indirectly uses the metadata to determine the actual length. | |
a1dfa0c6 XL |
219 | _ => {}, |
220 | }; | |
221 | ||
74b04a01 | 222 | // Visit the fields of this value. |
a1dfa0c6 | 223 | match v.layout().fields { |
ba9703b0 XL |
224 | FieldsShape::Primitive => {}, |
225 | FieldsShape::Union(fields) => { | |
74b04a01 | 226 | self.visit_union(v, fields)?; |
a1dfa0c6 | 227 | }, |
ba9703b0 | 228 | FieldsShape::Arbitrary { ref offsets, .. } => { |
48663c56 XL |
229 | // FIXME: We collect in a vec because otherwise there are lifetime |
230 | // errors: Projecting to a field needs access to `ecx`. | |
dc9dc135 | 231 | let fields: Vec<InterpResult<'tcx, Self::V>> = |
48663c56 | 232 | (0..offsets.len()).map(|i| { |
ba9703b0 | 233 | v.project_field(self.ecx(), i) |
48663c56 XL |
234 | }) |
235 | .collect(); | |
74b04a01 | 236 | self.visit_aggregate(v, fields.into_iter())?; |
a1dfa0c6 | 237 | }, |
ba9703b0 | 238 | FieldsShape::Array { .. } => { |
a1dfa0c6 | 239 | // Let's get an mplace first. |
dfeec247 | 240 | let mplace = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); |
a1dfa0c6 | 241 | // Now we can go over all the fields. |
74b04a01 XL |
242 | // This uses the *run-time length*, i.e., if we are a slice, |
243 | // the dynamic info from the metadata is used. | |
a1dfa0c6 XL |
244 | let iter = self.ecx().mplace_array_fields(mplace)? |
245 | .map(|f| f.and_then(|f| { | |
246 | Ok(Value::from_mem_place(f)) | |
247 | })); | |
74b04a01 XL |
248 | self.visit_aggregate(v, iter)?; |
249 | } | |
250 | } | |
251 | ||
252 | match v.layout().variants { | |
253 | // If this is a multi-variant layout, find the right variant and proceed | |
254 | // with *its* fields. | |
ba9703b0 | 255 | Variants::Multiple { .. } => { |
74b04a01 | 256 | let op = v.to_op(self.ecx())?; |
f035d41b | 257 | let idx = self.read_discriminant(op)?; |
74b04a01 XL |
258 | let inner = v.project_downcast(self.ecx(), idx)?; |
259 | trace!("walk_value: variant layout: {:#?}", inner.layout()); | |
260 | // recurse with the inner type | |
261 | self.visit_variant(v, idx, inner) | |
a1dfa0c6 | 262 | } |
74b04a01 | 263 | // For single-variant layouts, we already did anything there is to do. |
ba9703b0 | 264 | Variants::Single { .. } => Ok(()) |
a1dfa0c6 XL |
265 | } |
266 | } | |
267 | } | |
268 | } | |
269 | } | |
270 | ||
271 | make_value_visitor!(ValueVisitor,); | |
dfeec247 | 272 | make_value_visitor!(MutValueVisitor, mut); |