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