]>
Commit | Line | Data |
---|---|---|
e9174d1e SL |
1 | //! See docs in build/expr/mod.rs |
2 | ||
9fa01778 XL |
3 | use crate::build::expr::category::Category; |
4 | use crate::build::ForGuard::{OutsideGuard, RefWithinGuard}; | |
f2b60f7d | 5 | use crate::build::{BlockAnd, BlockAndExtension, Builder, Capture, CaptureMap}; |
064997fb | 6 | use rustc_hir::def_id::LocalDefId; |
923072b8 | 7 | use rustc_middle::hir::place::Projection as HirProjection; |
fc512014 | 8 | use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; |
5869c6ff | 9 | use rustc_middle::middle::region; |
ba9703b0 XL |
10 | use rustc_middle::mir::AssertKind::BoundsCheck; |
11 | use rustc_middle::mir::*; | |
17df50a5 | 12 | use rustc_middle::thir::*; |
6a06907d | 13 | use rustc_middle::ty::AdtDef; |
9ffffee4 | 14 | use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, Variance}; |
dfeec247 | 15 | use rustc_span::Span; |
353b0b11 | 16 | use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; |
e74abb32 | 17 | |
2b03887a | 18 | use std::assert_matches::assert_matches; |
cdc7bbd5 XL |
19 | use std::iter; |
20 | ||
fc512014 | 21 | /// The "outermost" place that holds this value. |
6a06907d | 22 | #[derive(Copy, Clone, Debug, PartialEq)] |
923072b8 | 23 | pub(crate) enum PlaceBase { |
fc512014 XL |
24 | /// Denotes the start of a `Place`. |
25 | Local(Local), | |
26 | ||
27 | /// When building place for an expression within a closure, the place might start off a | |
28 | /// captured path. When `capture_disjoint_fields` is enabled, we might not know the capture | |
29 | /// index (within the desugared closure) of the captured path until most of the projections | |
30 | /// are applied. We use `PlaceBase::Upvar` to keep track of the root variable off of which the | |
31 | /// captured path starts, the closure the capture belongs to and the trait the closure | |
32 | /// implements. | |
33 | /// | |
34 | /// Once we have figured out the capture index, we can convert the place builder to start from | |
35 | /// `PlaceBase::Local`. | |
36 | /// | |
37 | /// Consider the following example | |
38 | /// ```rust | |
04454e1e | 39 | /// let t = (((10, 10), 10), 10); |
fc512014 XL |
40 | /// |
41 | /// let c = || { | |
42 | /// println!("{}", t.0.0.0); | |
43 | /// }; | |
44 | /// ``` | |
45 | /// Here the THIR expression for `t.0.0.0` will be something like | |
46 | /// | |
04454e1e | 47 | /// ```ignore (illustrative) |
fc512014 XL |
48 | /// * Field(0) |
49 | /// * Field(0) | |
50 | /// * Field(0) | |
51 | /// * UpvarRef(t) | |
52 | /// ``` | |
53 | /// | |
54 | /// When `capture_disjoint_fields` is enabled, `t.0.0.0` is captured and we won't be able to | |
55 | /// figure out that it is captured until all the `Field` projections are applied. | |
56 | Upvar { | |
57 | /// HirId of the upvar | |
923072b8 | 58 | var_hir_id: LocalVarId, |
fc512014 | 59 | /// DefId of the closure |
064997fb | 60 | closure_def_id: LocalDefId, |
5869c6ff | 61 | }, |
fc512014 XL |
62 | } |
63 | ||
e74abb32 XL |
64 | /// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a |
65 | /// place by pushing more and more projections onto the end, and then convert the final set into a | |
487cf647 | 66 | /// place using the `to_place` method. |
e74abb32 XL |
67 | /// |
68 | /// This is used internally when building a place for an expression like `a.b.c`. The fields `b` | |
69 | /// and `c` can be progressively pushed onto the place builder that is created when converting `a`. | |
6a06907d | 70 | #[derive(Clone, Debug, PartialEq)] |
2b03887a | 71 | pub(in crate::build) struct PlaceBuilder<'tcx> { |
fc512014 | 72 | base: PlaceBase, |
e74abb32 XL |
73 | projection: Vec<PlaceElem<'tcx>>, |
74 | } | |
75 | ||
fc512014 XL |
76 | /// Given a list of MIR projections, convert them to list of HIR ProjectionKind. |
77 | /// The projections are truncated to represent a path that might be captured by a | |
78 | /// closure/generator. This implies the vector returned from this function doesn't contain | |
79 | /// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be | |
cdc7bbd5 | 80 | /// part of a path that is captured by a closure. We stop applying projections once we see the first |
fc512014 | 81 | /// projection that isn't captured by a closure. |
9c376795 FG |
82 | fn convert_to_hir_projections_and_truncate_for_capture( |
83 | mir_projections: &[PlaceElem<'_>], | |
fc512014 | 84 | ) -> Vec<HirProjectionKind> { |
5869c6ff | 85 | let mut hir_projections = Vec::new(); |
6a06907d | 86 | let mut variant = None; |
fc512014 XL |
87 | |
88 | for mir_projection in mir_projections { | |
89 | let hir_projection = match mir_projection { | |
90 | ProjectionElem::Deref => HirProjectionKind::Deref, | |
91 | ProjectionElem::Field(field, _) => { | |
353b0b11 FG |
92 | let variant = variant.unwrap_or(FIRST_VARIANT); |
93 | HirProjectionKind::Field(*field, variant) | |
5869c6ff | 94 | } |
6a06907d XL |
95 | ProjectionElem::Downcast(.., idx) => { |
96 | // We don't expect to see multi-variant enums here, as earlier | |
97 | // phases will have truncated them already. However, there can | |
98 | // still be downcasts, thanks to single-variant enums. | |
99 | // We keep track of VariantIdx so we can use this information | |
100 | // if the next ProjectionElem is a Field. | |
101 | variant = Some(*idx); | |
102 | continue; | |
5869c6ff | 103 | } |
2b03887a | 104 | // These do not affect anything, they just make sure we know the right type. |
781aab86 | 105 | ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(..) => continue, |
fc512014 XL |
106 | ProjectionElem::Index(..) |
107 | | ProjectionElem::ConstantIndex { .. } | |
108 | | ProjectionElem::Subslice { .. } => { | |
109 | // We don't capture array-access projections. | |
110 | // We can stop here as arrays are captured completely. | |
5869c6ff XL |
111 | break; |
112 | } | |
fc512014 | 113 | }; |
6a06907d | 114 | variant = None; |
fc512014 XL |
115 | hir_projections.push(hir_projection); |
116 | } | |
117 | ||
118 | hir_projections | |
119 | } | |
120 | ||
121 | /// Return true if the `proj_possible_ancestor` represents an ancestor path | |
122 | /// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`, | |
123 | /// assuming they both start off of the same root variable. | |
124 | /// | |
125 | /// **Note:** It's the caller's responsibility to ensure that both lists of projections | |
126 | /// start off of the same root variable. | |
127 | /// | |
128 | /// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of | |
129 | /// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`. | |
130 | /// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`. | |
131 | /// 2. Since we only look at the projections here function will return `bar.x` as an a valid | |
132 | /// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections | |
133 | /// list are being applied to the same root variable. | |
134 | fn is_ancestor_or_same_capture( | |
923072b8 | 135 | proj_possible_ancestor: &[HirProjectionKind], |
5869c6ff | 136 | proj_capture: &[HirProjectionKind], |
fc512014 XL |
137 | ) -> bool { |
138 | // We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false. | |
139 | // Therefore we can't just check if all projections are same in the zipped iterator below. | |
140 | if proj_possible_ancestor.len() > proj_capture.len() { | |
141 | return false; | |
142 | } | |
143 | ||
cdc7bbd5 | 144 | iter::zip(proj_possible_ancestor, proj_capture).all(|(a, b)| a == b) |
fc512014 XL |
145 | } |
146 | ||
fc512014 XL |
147 | /// Given a closure, returns the index of a capture within the desugared closure struct and the |
148 | /// `ty::CapturedPlace` which is the ancestor of the Place represented using the `var_hir_id` | |
149 | /// and `projection`. | |
150 | /// | |
151 | /// Note there will be at most one ancestor for any given Place. | |
152 | /// | |
153 | /// Returns None, when the ancestor is not found. | |
154 | fn find_capture_matching_projections<'a, 'tcx>( | |
f2b60f7d | 155 | upvars: &'a CaptureMap<'tcx>, |
923072b8 | 156 | var_hir_id: LocalVarId, |
5869c6ff | 157 | projections: &[PlaceElem<'tcx>], |
f2b60f7d | 158 | ) -> Option<(usize, &'a Capture<'tcx>)> { |
fc512014 XL |
159 | let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections); |
160 | ||
f2b60f7d | 161 | upvars.get_by_key_enumerated(var_hir_id.0).find(|(_, capture)| { |
923072b8 | 162 | let possible_ancestor_proj_kinds: Vec<_> = |
f2b60f7d | 163 | capture.captured_place.place.projections.iter().map(|proj| proj.kind).collect(); |
5869c6ff | 164 | is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections) |
f2b60f7d | 165 | }) |
fc512014 XL |
166 | } |
167 | ||
487cf647 FG |
168 | /// Takes an upvar place and tries to resolve it into a `PlaceBuilder` |
169 | /// with `PlaceBase::Local` | |
2b03887a FG |
170 | #[instrument(level = "trace", skip(cx), ret)] |
171 | fn to_upvars_resolved_place_builder<'tcx>( | |
2b03887a | 172 | cx: &Builder<'_, 'tcx>, |
487cf647 FG |
173 | var_hir_id: LocalVarId, |
174 | closure_def_id: LocalDefId, | |
175 | projection: &[PlaceElem<'tcx>], | |
176 | ) -> Option<PlaceBuilder<'tcx>> { | |
177 | let Some((capture_index, capture)) = | |
add651ee FG |
178 | find_capture_matching_projections(&cx.upvars, var_hir_id, &projection) |
179 | else { | |
487cf647 | 180 | let closure_span = cx.tcx.def_span(closure_def_id); |
9ffffee4 | 181 | if !enable_precise_capture(closure_span) { |
487cf647 FG |
182 | bug!( |
183 | "No associated capture found for {:?}[{:#?}] even though \ | |
184 | capture_disjoint_fields isn't enabled", | |
185 | var_hir_id, | |
186 | projection | |
187 | ) | |
188 | } else { | |
add651ee | 189 | debug!("No associated capture found for {:?}[{:#?}]", var_hir_id, projection,); |
487cf647 FG |
190 | } |
191 | return None; | |
192 | }; | |
fc512014 | 193 | |
487cf647 FG |
194 | // Access the capture by accessing the field within the Closure struct. |
195 | let capture_info = &cx.upvars[capture_index]; | |
f2b60f7d | 196 | |
487cf647 | 197 | let mut upvar_resolved_place_builder = PlaceBuilder::from(capture_info.use_place); |
fc512014 | 198 | |
487cf647 FG |
199 | // We used some of the projections to build the capture itself, |
200 | // now we apply the remaining to the upvar resolved place. | |
201 | trace!(?capture.captured_place, ?projection); | |
202 | let remaining_projections = strip_prefix( | |
203 | capture.captured_place.place.base_ty, | |
204 | projection, | |
205 | &capture.captured_place.place.projections, | |
206 | ); | |
207 | upvar_resolved_place_builder.projection.extend(remaining_projections); | |
fc512014 | 208 | |
487cf647 | 209 | Some(upvar_resolved_place_builder) |
fc512014 XL |
210 | } |
211 | ||
923072b8 FG |
212 | /// Returns projections remaining after stripping an initial prefix of HIR |
213 | /// projections. | |
214 | /// | |
215 | /// Supports only HIR projection kinds that represent a path that might be | |
216 | /// captured by a closure or a generator, i.e., an `Index` or a `Subslice` | |
217 | /// projection kinds are unsupported. | |
487cf647 | 218 | fn strip_prefix<'a, 'tcx>( |
923072b8 | 219 | mut base_ty: Ty<'tcx>, |
487cf647 | 220 | projections: &'a [PlaceElem<'tcx>], |
923072b8 | 221 | prefix_projections: &[HirProjection<'tcx>], |
487cf647 | 222 | ) -> impl Iterator<Item = PlaceElem<'tcx>> + 'a { |
2b03887a | 223 | let mut iter = projections |
487cf647 FG |
224 | .iter() |
225 | .copied() | |
2b03887a FG |
226 | // Filter out opaque casts, they are unnecessary in the prefix. |
227 | .filter(|elem| !matches!(elem, ProjectionElem::OpaqueCast(..))); | |
923072b8 FG |
228 | for projection in prefix_projections { |
229 | match projection.kind { | |
230 | HirProjectionKind::Deref => { | |
2b03887a | 231 | assert_matches!(iter.next(), Some(ProjectionElem::Deref)); |
923072b8 FG |
232 | } |
233 | HirProjectionKind::Field(..) => { | |
234 | if base_ty.is_enum() { | |
2b03887a | 235 | assert_matches!(iter.next(), Some(ProjectionElem::Downcast(..))); |
923072b8 | 236 | } |
2b03887a | 237 | assert_matches!(iter.next(), Some(ProjectionElem::Field(..))); |
923072b8 | 238 | } |
add651ee FG |
239 | HirProjectionKind::OpaqueCast => { |
240 | assert_matches!(iter.next(), Some(ProjectionElem::OpaqueCast(..))); | |
241 | } | |
923072b8 FG |
242 | HirProjectionKind::Index | HirProjectionKind::Subslice => { |
243 | bug!("unexpected projection kind: {:?}", projection); | |
244 | } | |
245 | } | |
246 | base_ty = projection.ty; | |
247 | } | |
248 | iter | |
249 | } | |
250 | ||
dfeec247 | 251 | impl<'tcx> PlaceBuilder<'tcx> { |
487cf647 FG |
252 | pub(in crate::build) fn to_place(&self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> { |
253 | self.try_to_place(cx).unwrap() | |
fc512014 XL |
254 | } |
255 | ||
487cf647 FG |
256 | /// Creates a `Place` or returns `None` if an upvar cannot be resolved |
257 | pub(in crate::build) fn try_to_place(&self, cx: &Builder<'_, 'tcx>) -> Option<Place<'tcx>> { | |
258 | let resolved = self.resolve_upvar(cx); | |
259 | let builder = resolved.as_ref().unwrap_or(self); | |
260 | let PlaceBase::Local(local) = builder.base else { return None }; | |
9ffffee4 | 261 | let projection = cx.tcx.mk_place_elems(&builder.projection); |
487cf647 | 262 | Some(Place { local, projection }) |
fc512014 XL |
263 | } |
264 | ||
6a06907d | 265 | /// Attempts to resolve the `PlaceBuilder`. |
487cf647 | 266 | /// Returns `None` if this is not an upvar. |
6a06907d XL |
267 | /// |
268 | /// Upvars resolve may fail for a `PlaceBuilder` when attempting to | |
269 | /// resolve a disjoint field whose root variable is not captured | |
270 | /// (destructured assignments) or when attempting to resolve a root | |
271 | /// variable (discriminant matching with only wildcard arm) that is | |
272 | /// not captured. This can happen because the final mir that will be | |
273 | /// generated doesn't require a read for this place. Failures will only | |
274 | /// happen inside closures. | |
487cf647 FG |
275 | pub(in crate::build) fn resolve_upvar( |
276 | &self, | |
2b03887a | 277 | cx: &Builder<'_, 'tcx>, |
487cf647 FG |
278 | ) -> Option<PlaceBuilder<'tcx>> { |
279 | let PlaceBase::Upvar { var_hir_id, closure_def_id } = self.base else { | |
280 | return None; | |
281 | }; | |
282 | to_upvars_resolved_place_builder(cx, var_hir_id, closure_def_id, &self.projection) | |
6a06907d XL |
283 | } |
284 | ||
923072b8 | 285 | pub(crate) fn base(&self) -> PlaceBase { |
fc512014 | 286 | self.base |
e74abb32 XL |
287 | } |
288 | ||
2b03887a FG |
289 | pub(crate) fn projection(&self) -> &[PlaceElem<'tcx>] { |
290 | &self.projection | |
291 | } | |
292 | ||
353b0b11 | 293 | pub(crate) fn field(self, f: FieldIdx, ty: Ty<'tcx>) -> Self { |
e74abb32 XL |
294 | self.project(PlaceElem::Field(f, ty)) |
295 | } | |
296 | ||
923072b8 | 297 | pub(crate) fn deref(self) -> Self { |
e74abb32 XL |
298 | self.project(PlaceElem::Deref) |
299 | } | |
300 | ||
923072b8 | 301 | pub(crate) fn downcast(self, adt_def: AdtDef<'tcx>, variant_index: VariantIdx) -> Self { |
5e7ed085 | 302 | self.project(PlaceElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index)) |
6a06907d XL |
303 | } |
304 | ||
e74abb32 XL |
305 | fn index(self, index: Local) -> Self { |
306 | self.project(PlaceElem::Index(index)) | |
307 | } | |
308 | ||
923072b8 | 309 | pub(crate) fn project(mut self, elem: PlaceElem<'tcx>) -> Self { |
e74abb32 XL |
310 | self.projection.push(elem); |
311 | self | |
312 | } | |
487cf647 FG |
313 | |
314 | /// Same as `.clone().project(..)` but more efficient | |
315 | pub(crate) fn clone_project(&self, elem: PlaceElem<'tcx>) -> Self { | |
316 | Self { | |
317 | base: self.base, | |
318 | projection: Vec::from_iter(self.projection.iter().copied().chain([elem])), | |
319 | } | |
320 | } | |
e74abb32 XL |
321 | } |
322 | ||
dfeec247 | 323 | impl<'tcx> From<Local> for PlaceBuilder<'tcx> { |
e74abb32 | 324 | fn from(local: Local) -> Self { |
fc512014 XL |
325 | Self { base: PlaceBase::Local(local), projection: Vec::new() } |
326 | } | |
327 | } | |
328 | ||
329 | impl<'tcx> From<PlaceBase> for PlaceBuilder<'tcx> { | |
330 | fn from(base: PlaceBase) -> Self { | |
331 | Self { base, projection: Vec::new() } | |
e74abb32 XL |
332 | } |
333 | } | |
3157f602 | 334 | |
f2b60f7d FG |
335 | impl<'tcx> From<Place<'tcx>> for PlaceBuilder<'tcx> { |
336 | fn from(p: Place<'tcx>) -> Self { | |
337 | Self { base: PlaceBase::Local(p.local), projection: p.projection.to_vec() } | |
338 | } | |
339 | } | |
340 | ||
dc9dc135 | 341 | impl<'a, 'tcx> Builder<'a, 'tcx> { |
ff7c6d11 | 342 | /// Compile `expr`, yielding a place that we can move from etc. |
60c5eb7d XL |
343 | /// |
344 | /// WARNING: Any user code might: | |
345 | /// * Invalidate any slice bounds checks performed. | |
346 | /// * Change the address that this `Place` refers to. | |
347 | /// * Modify the memory that this place refers to. | |
348 | /// * Invalidate the memory that this place refers to, this will be caught | |
349 | /// by borrow checking. | |
350 | /// | |
351 | /// Extra care is needed if any user code is allowed to run between calling | |
352 | /// this method and using it, as is the case for `match` and index | |
353 | /// expressions. | |
923072b8 | 354 | pub(crate) fn as_place( |
6a06907d XL |
355 | &mut self, |
356 | mut block: BasicBlock, | |
17df50a5 | 357 | expr: &Expr<'tcx>, |
6a06907d | 358 | ) -> BlockAnd<Place<'tcx>> { |
e74abb32 | 359 | let place_builder = unpack!(block = self.as_place_builder(block, expr)); |
487cf647 | 360 | block.and(place_builder.to_place(self)) |
e74abb32 XL |
361 | } |
362 | ||
363 | /// This is used when constructing a compound `Place`, so that we can avoid creating | |
364 | /// intermediate `Place` values until we know the full set of projections. | |
923072b8 | 365 | pub(crate) fn as_place_builder( |
5869c6ff XL |
366 | &mut self, |
367 | block: BasicBlock, | |
17df50a5 | 368 | expr: &Expr<'tcx>, |
6a06907d | 369 | ) -> BlockAnd<PlaceBuilder<'tcx>> { |
60c5eb7d | 370 | self.expr_as_place(block, expr, Mutability::Mut, None) |
e9174d1e SL |
371 | } |
372 | ||
b7449926 XL |
373 | /// Compile `expr`, yielding a place that we can move from etc. |
374 | /// Mutability note: The caller of this method promises only to read from the resulting | |
375 | /// place. The place itself may or may not be mutable: | |
376 | /// * If this expr is a place expr like a.b, then we will return that place. | |
377 | /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary. | |
923072b8 | 378 | pub(crate) fn as_read_only_place( |
dfeec247 XL |
379 | &mut self, |
380 | mut block: BasicBlock, | |
17df50a5 | 381 | expr: &Expr<'tcx>, |
6a06907d | 382 | ) -> BlockAnd<Place<'tcx>> { |
e74abb32 | 383 | let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr)); |
487cf647 | 384 | block.and(place_builder.to_place(self)) |
e74abb32 XL |
385 | } |
386 | ||
387 | /// This is used when constructing a compound `Place`, so that we can avoid creating | |
388 | /// intermediate `Place` values until we know the full set of projections. | |
389 | /// Mutability note: The caller of this method promises only to read from the resulting | |
390 | /// place. The place itself may or may not be mutable: | |
391 | /// * If this expr is a place expr like a.b, then we will return that place. | |
392 | /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary. | |
6a06907d | 393 | fn as_read_only_place_builder( |
e74abb32 XL |
394 | &mut self, |
395 | block: BasicBlock, | |
17df50a5 | 396 | expr: &Expr<'tcx>, |
6a06907d | 397 | ) -> BlockAnd<PlaceBuilder<'tcx>> { |
60c5eb7d | 398 | self.expr_as_place(block, expr, Mutability::Not, None) |
b7449926 XL |
399 | } |
400 | ||
401 | fn expr_as_place( | |
402 | &mut self, | |
403 | mut block: BasicBlock, | |
17df50a5 | 404 | expr: &Expr<'tcx>, |
b7449926 | 405 | mutability: Mutability, |
60c5eb7d | 406 | fake_borrow_temps: Option<&mut Vec<Local>>, |
e74abb32 | 407 | ) -> BlockAnd<PlaceBuilder<'tcx>> { |
dfeec247 | 408 | debug!("expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability); |
e9174d1e SL |
409 | |
410 | let this = self; | |
411 | let expr_span = expr.span; | |
3157f602 | 412 | let source_info = this.source_info(expr_span); |
e9174d1e | 413 | match expr.kind { |
dfeec247 XL |
414 | ExprKind::Scope { region_scope, lint_level, value } => { |
415 | this.in_scope((region_scope, source_info), lint_level, |this| { | |
17df50a5 | 416 | this.expr_as_place(block, &this.thir[value], mutability, fake_borrow_temps) |
dfeec247 XL |
417 | }) |
418 | } | |
923072b8 FG |
419 | ExprKind::Field { lhs, variant_index, name } => { |
420 | let lhs = &this.thir[lhs]; | |
421 | let mut place_builder = | |
422 | unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,)); | |
423 | if let ty::Adt(adt_def, _) = lhs.ty.kind() { | |
424 | if adt_def.is_enum() { | |
425 | place_builder = place_builder.downcast(*adt_def, variant_index); | |
426 | } | |
427 | } | |
e74abb32 | 428 | block.and(place_builder.field(name, expr.ty)) |
e9174d1e SL |
429 | } |
430 | ExprKind::Deref { arg } => { | |
17df50a5 XL |
431 | let place_builder = unpack!( |
432 | block = | |
433 | this.expr_as_place(block, &this.thir[arg], mutability, fake_borrow_temps,) | |
434 | ); | |
e74abb32 | 435 | block.and(place_builder.deref()) |
e9174d1e | 436 | } |
dfeec247 XL |
437 | ExprKind::Index { lhs, index } => this.lower_index_expression( |
438 | block, | |
17df50a5 XL |
439 | &this.thir[lhs], |
440 | &this.thir[index], | |
dfeec247 XL |
441 | mutability, |
442 | fake_borrow_temps, | |
443 | expr.temp_lifetime, | |
444 | expr_span, | |
445 | source_info, | |
446 | ), | |
fc512014 | 447 | ExprKind::UpvarRef { closure_def_id, var_hir_id } => { |
923072b8 | 448 | this.lower_captured_upvar(block, closure_def_id.expect_local(), var_hir_id) |
fc512014 XL |
449 | } |
450 | ||
e9174d1e | 451 | ExprKind::VarRef { id } => { |
e74abb32 | 452 | let place_builder = if this.is_bound_var_in_guard(id) { |
8faf50e0 | 453 | let index = this.var_local_id(id, RefWithinGuard); |
e74abb32 | 454 | PlaceBuilder::from(index).deref() |
83c7162d XL |
455 | } else { |
456 | let index = this.var_local_id(id, OutsideGuard); | |
e74abb32 | 457 | PlaceBuilder::from(index) |
83c7162d | 458 | }; |
e74abb32 | 459 | block.and(place_builder) |
e9174d1e | 460 | } |
e9174d1e | 461 | |
f2b60f7d | 462 | ExprKind::PlaceTypeAscription { source, ref user_ty } => { |
dfeec247 | 463 | let place_builder = unpack!( |
17df50a5 XL |
464 | block = this.expr_as_place( |
465 | block, | |
466 | &this.thir[source], | |
467 | mutability, | |
468 | fake_borrow_temps, | |
469 | ) | |
dfeec247 | 470 | ); |
0bf4aa26 | 471 | if let Some(user_ty) = user_ty { |
dfeec247 XL |
472 | let annotation_index = |
473 | this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation { | |
9fa01778 | 474 | span: source_info.span, |
f2b60f7d | 475 | user_ty: user_ty.clone(), |
9fa01778 | 476 | inferred_ty: expr.ty, |
dfeec247 | 477 | }); |
e74abb32 | 478 | |
487cf647 | 479 | let place = place_builder.to_place(this); |
0bf4aa26 XL |
480 | this.cfg.push( |
481 | block, | |
482 | Statement { | |
483 | source_info, | |
484 | kind: StatementKind::AscribeUserType( | |
94222f64 | 485 | Box::new(( |
e74abb32 | 486 | place, |
dfeec247 | 487 | UserTypeProjection { base: annotation_index, projs: vec![] }, |
94222f64 | 488 | )), |
0bf4aa26 | 489 | Variance::Invariant, |
0bf4aa26 XL |
490 | ), |
491 | }, | |
492 | ); | |
493 | } | |
e74abb32 | 494 | block.and(place_builder) |
0bf4aa26 | 495 | } |
f2b60f7d | 496 | ExprKind::ValueTypeAscription { source, ref user_ty } => { |
17df50a5 | 497 | let source = &this.thir[source]; |
dfeec247 XL |
498 | let temp = |
499 | unpack!(block = this.as_temp(block, source.temp_lifetime, source, mutability)); | |
0bf4aa26 | 500 | if let Some(user_ty) = user_ty { |
dfeec247 XL |
501 | let annotation_index = |
502 | this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation { | |
9fa01778 | 503 | span: source_info.span, |
f2b60f7d | 504 | user_ty: user_ty.clone(), |
9fa01778 | 505 | inferred_ty: expr.ty, |
dfeec247 | 506 | }); |
0bf4aa26 XL |
507 | this.cfg.push( |
508 | block, | |
509 | Statement { | |
510 | source_info, | |
511 | kind: StatementKind::AscribeUserType( | |
94222f64 | 512 | Box::new(( |
dfeec247 XL |
513 | Place::from(temp), |
514 | UserTypeProjection { base: annotation_index, projs: vec![] }, | |
94222f64 | 515 | )), |
0bf4aa26 | 516 | Variance::Invariant, |
0bf4aa26 XL |
517 | ), |
518 | }, | |
519 | ); | |
520 | } | |
e74abb32 | 521 | block.and(PlaceBuilder::from(temp)) |
0bf4aa26 XL |
522 | } |
523 | ||
b7449926 XL |
524 | ExprKind::Array { .. } |
525 | | ExprKind::Tuple { .. } | |
526 | | ExprKind::Adt { .. } | |
527 | | ExprKind::Closure { .. } | |
528 | | ExprKind::Unary { .. } | |
529 | | ExprKind::Binary { .. } | |
530 | | ExprKind::LogicalOp { .. } | |
531 | | ExprKind::Box { .. } | |
532 | | ExprKind::Cast { .. } | |
533 | | ExprKind::Use { .. } | |
534 | | ExprKind::NeverToAny { .. } | |
fe692bf9 | 535 | | ExprKind::PointerCoercion { .. } |
b7449926 XL |
536 | | ExprKind::Repeat { .. } |
537 | | ExprKind::Borrow { .. } | |
dfeec247 | 538 | | ExprKind::AddressOf { .. } |
b7449926 | 539 | | ExprKind::Match { .. } |
5869c6ff | 540 | | ExprKind::If { .. } |
b7449926 XL |
541 | | ExprKind::Loop { .. } |
542 | | ExprKind::Block { .. } | |
94222f64 | 543 | | ExprKind::Let { .. } |
b7449926 XL |
544 | | ExprKind::Assign { .. } |
545 | | ExprKind::AssignOp { .. } | |
546 | | ExprKind::Break { .. } | |
547 | | ExprKind::Continue { .. } | |
548 | | ExprKind::Return { .. } | |
fe692bf9 | 549 | | ExprKind::Become { .. } |
b7449926 | 550 | | ExprKind::Literal { .. } |
5e7ed085 FG |
551 | | ExprKind::NamedConst { .. } |
552 | | ExprKind::NonHirLiteral { .. } | |
064997fb | 553 | | ExprKind::ZstLiteral { .. } |
5e7ed085 | 554 | | ExprKind::ConstParam { .. } |
29967ef6 | 555 | | ExprKind::ConstBlock { .. } |
60c5eb7d | 556 | | ExprKind::StaticRef { .. } |
f9f354fc | 557 | | ExprKind::InlineAsm { .. } |
49aad941 | 558 | | ExprKind::OffsetOf { .. } |
b7449926 | 559 | | ExprKind::Yield { .. } |
f9f354fc | 560 | | ExprKind::ThreadLocalRef(_) |
b7449926 | 561 | | ExprKind::Call { .. } => { |
ff7c6d11 | 562 | // these are not places, so we need to make a temporary. |
29967ef6 | 563 | debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place))); |
b7449926 XL |
564 | let temp = |
565 | unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability)); | |
e74abb32 | 566 | block.and(PlaceBuilder::from(temp)) |
e9174d1e SL |
567 | } |
568 | } | |
569 | } | |
60c5eb7d | 570 | |
fc512014 XL |
571 | /// Lower a captured upvar. Note we might not know the actual capture index, |
572 | /// so we create a place starting from `PlaceBase::Upvar`, which will be resolved | |
cdc7bbd5 | 573 | /// once all projections that allow us to identify a capture have been applied. |
fc512014 XL |
574 | fn lower_captured_upvar( |
575 | &mut self, | |
576 | block: BasicBlock, | |
064997fb | 577 | closure_def_id: LocalDefId, |
923072b8 | 578 | var_hir_id: LocalVarId, |
fc512014 | 579 | ) -> BlockAnd<PlaceBuilder<'tcx>> { |
f2b60f7d | 580 | block.and(PlaceBuilder::from(PlaceBase::Upvar { var_hir_id, closure_def_id })) |
fc512014 XL |
581 | } |
582 | ||
60c5eb7d XL |
583 | /// Lower an index expression |
584 | /// | |
585 | /// This has two complications; | |
586 | /// | |
587 | /// * We need to do a bounds check. | |
588 | /// * We need to ensure that the bounds check can't be invalidated using an | |
589 | /// expression like `x[1][{x = y; 2}]`. We use fake borrows here to ensure | |
590 | /// that this is the case. | |
591 | fn lower_index_expression( | |
592 | &mut self, | |
593 | mut block: BasicBlock, | |
17df50a5 XL |
594 | base: &Expr<'tcx>, |
595 | index: &Expr<'tcx>, | |
60c5eb7d XL |
596 | mutability: Mutability, |
597 | fake_borrow_temps: Option<&mut Vec<Local>>, | |
598 | temp_lifetime: Option<region::Scope>, | |
599 | expr_span: Span, | |
dfeec247 | 600 | source_info: SourceInfo, |
60c5eb7d | 601 | ) -> BlockAnd<PlaceBuilder<'tcx>> { |
60c5eb7d XL |
602 | let base_fake_borrow_temps = &mut Vec::new(); |
603 | let is_outermost_index = fake_borrow_temps.is_none(); | |
604 | let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps); | |
605 | ||
487cf647 | 606 | let base_place = |
6a06907d | 607 | unpack!(block = self.expr_as_place(block, base, mutability, Some(fake_borrow_temps),)); |
60c5eb7d XL |
608 | |
609 | // Making this a *fresh* temporary means we do not have to worry about | |
610 | // the index changing later: Nothing will ever change this temporary. | |
611 | // The "retagging" transformation (for Stacked Borrows) relies on this. | |
dfeec247 | 612 | let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not,)); |
60c5eb7d | 613 | |
487cf647 | 614 | block = self.bounds_check(block, &base_place, idx, expr_span, source_info); |
60c5eb7d XL |
615 | |
616 | if is_outermost_index { | |
617 | self.read_fake_borrows(block, fake_borrow_temps, source_info) | |
618 | } else { | |
619 | self.add_fake_borrows_of_base( | |
487cf647 | 620 | base_place.to_place(self), |
60c5eb7d XL |
621 | block, |
622 | fake_borrow_temps, | |
623 | expr_span, | |
624 | source_info, | |
625 | ); | |
626 | } | |
627 | ||
628 | block.and(base_place.index(idx)) | |
629 | } | |
630 | ||
631 | fn bounds_check( | |
632 | &mut self, | |
633 | block: BasicBlock, | |
487cf647 | 634 | slice: &PlaceBuilder<'tcx>, |
60c5eb7d XL |
635 | index: Local, |
636 | expr_span: Span, | |
637 | source_info: SourceInfo, | |
638 | ) -> BasicBlock { | |
6a06907d XL |
639 | let usize_ty = self.tcx.types.usize; |
640 | let bool_ty = self.tcx.types.bool; | |
60c5eb7d XL |
641 | // bounds check: |
642 | let len = self.temp(usize_ty, expr_span); | |
643 | let lt = self.temp(bool_ty, expr_span); | |
644 | ||
645 | // len = len(slice) | |
487cf647 | 646 | self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.to_place(self))); |
60c5eb7d XL |
647 | // lt = idx < len |
648 | self.cfg.push_assign( | |
649 | block, | |
650 | source_info, | |
ba9703b0 | 651 | lt, |
6a06907d XL |
652 | Rvalue::BinaryOp( |
653 | BinOp::Lt, | |
94222f64 | 654 | Box::new((Operand::Copy(Place::from(index)), Operand::Copy(len))), |
6a06907d | 655 | ), |
60c5eb7d | 656 | ); |
dfeec247 | 657 | let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) }; |
60c5eb7d XL |
658 | // assert!(lt, "...") |
659 | self.assert(block, Operand::Move(lt), true, msg, expr_span) | |
660 | } | |
661 | ||
662 | fn add_fake_borrows_of_base( | |
663 | &mut self, | |
487cf647 | 664 | base_place: Place<'tcx>, |
60c5eb7d XL |
665 | block: BasicBlock, |
666 | fake_borrow_temps: &mut Vec<Local>, | |
667 | expr_span: Span, | |
668 | source_info: SourceInfo, | |
669 | ) { | |
6a06907d | 670 | let tcx = self.tcx; |
fc512014 | 671 | |
487cf647 | 672 | let place_ty = base_place.ty(&self.local_decls, tcx); |
1b1a35ee | 673 | if let ty::Slice(_) = place_ty.ty.kind() { |
60c5eb7d XL |
674 | // We need to create fake borrows to ensure that the bounds |
675 | // check that we just did stays valid. Since we can't assign to | |
676 | // unsized values, we only need to ensure that none of the | |
677 | // pointers in the base place are modified. | |
fe692bf9 | 678 | for (base_place, elem) in base_place.iter_projections().rev() { |
60c5eb7d XL |
679 | match elem { |
680 | ProjectionElem::Deref => { | |
fe692bf9 | 681 | let fake_borrow_deref_ty = base_place.ty(&self.local_decls, tcx).ty; |
dfeec247 | 682 | let fake_borrow_ty = |
fe692bf9 | 683 | Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty); |
dfeec247 | 684 | let fake_borrow_temp = |
f9f354fc | 685 | self.local_decls.push(LocalDecl::new(fake_borrow_ty, expr_span)); |
fe692bf9 | 686 | let projection = tcx.mk_place_elems(&base_place.projection); |
60c5eb7d XL |
687 | self.cfg.push_assign( |
688 | block, | |
689 | source_info, | |
ba9703b0 | 690 | fake_borrow_temp.into(), |
60c5eb7d XL |
691 | Rvalue::Ref( |
692 | tcx.lifetimes.re_erased, | |
781aab86 | 693 | BorrowKind::Fake, |
487cf647 | 694 | Place { local: base_place.local, projection }, |
60c5eb7d XL |
695 | ), |
696 | ); | |
697 | fake_borrow_temps.push(fake_borrow_temp); | |
698 | } | |
699 | ProjectionElem::Index(_) => { | |
fe692bf9 | 700 | let index_ty = base_place.ty(&self.local_decls, tcx); |
1b1a35ee | 701 | match index_ty.ty.kind() { |
60c5eb7d XL |
702 | // The previous index expression has already |
703 | // done any index expressions needed here. | |
704 | ty::Slice(_) => break, | |
705 | ty::Array(..) => (), | |
706 | _ => bug!("unexpected index base"), | |
707 | } | |
708 | } | |
709 | ProjectionElem::Field(..) | |
710 | | ProjectionElem::Downcast(..) | |
2b03887a | 711 | | ProjectionElem::OpaqueCast(..) |
781aab86 | 712 | | ProjectionElem::Subtype(..) |
60c5eb7d XL |
713 | | ProjectionElem::ConstantIndex { .. } |
714 | | ProjectionElem::Subslice { .. } => (), | |
715 | } | |
716 | } | |
717 | } | |
718 | } | |
719 | ||
720 | fn read_fake_borrows( | |
721 | &mut self, | |
722 | bb: BasicBlock, | |
723 | fake_borrow_temps: &mut Vec<Local>, | |
724 | source_info: SourceInfo, | |
725 | ) { | |
726 | // All indexes have been evaluated now, read all of the | |
727 | // fake borrows so that they are live across those index | |
728 | // expressions. | |
729 | for temp in fake_borrow_temps { | |
730 | self.cfg.push_fake_read(bb, source_info, FakeReadCause::ForIndex, Place::from(*temp)); | |
731 | } | |
732 | } | |
e9174d1e | 733 | } |
136023e0 | 734 | |
9ffffee4 FG |
735 | /// Precise capture is enabled if user is using Rust Edition 2021 or higher. |
736 | fn enable_precise_capture(closure_span: Span) -> bool { | |
add651ee | 737 | closure_span.at_least_rust_2021() |
136023e0 | 738 | } |