]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/build/expr/as_place.rs
Rollup merge of #67561 - euclio:remove-description, r=jonas-schievink
[rust.git] / src / librustc_mir / build / expr / as_place.rs
1 //! See docs in build/expr/mod.rs
2
3 use crate::build::expr::category::Category;
4 use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
5 use crate::build::{BlockAnd, BlockAndExtension, Builder};
6 use crate::hair::*;
7 use rustc::middle::region;
8 use rustc::mir::interpret::PanicInfo::BoundsCheck;
9 use rustc::mir::*;
10 use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
11 use syntax_pos::Span;
12
13 use rustc_index::vec::Idx;
14
15 /// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
16 /// place by pushing more and more projections onto the end, and then convert the final set into a
17 /// place using the `into_place` method.
18 ///
19 /// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
20 /// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
21 #[derive(Clone)]
22 struct PlaceBuilder<'tcx> {
23     base: PlaceBase<'tcx>,
24     projection: Vec<PlaceElem<'tcx>>,
25 }
26
27 impl PlaceBuilder<'tcx> {
28     fn into_place(self, tcx: TyCtxt<'tcx>) -> Place<'tcx> {
29         Place { base: self.base, projection: tcx.intern_place_elems(&self.projection) }
30     }
31
32     fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
33         self.project(PlaceElem::Field(f, ty))
34     }
35
36     fn deref(self) -> Self {
37         self.project(PlaceElem::Deref)
38     }
39
40     fn index(self, index: Local) -> Self {
41         self.project(PlaceElem::Index(index))
42     }
43
44     fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
45         self.projection.push(elem);
46         self
47     }
48 }
49
50 impl From<Local> for PlaceBuilder<'tcx> {
51     fn from(local: Local) -> Self {
52         Self { base: local.into(), projection: Vec::new() }
53     }
54 }
55
56 impl From<PlaceBase<'tcx>> for PlaceBuilder<'tcx> {
57     fn from(base: PlaceBase<'tcx>) -> Self {
58         Self { base, projection: Vec::new() }
59     }
60 }
61
62 impl<'a, 'tcx> Builder<'a, 'tcx> {
63     /// Compile `expr`, yielding a place that we can move from etc.
64     ///
65     /// WARNING: Any user code might:
66     /// * Invalidate any slice bounds checks performed.
67     /// * Change the address that this `Place` refers to.
68     /// * Modify the memory that this place refers to.
69     /// * Invalidate the memory that this place refers to, this will be caught
70     ///   by borrow checking.
71     ///
72     /// Extra care is needed if any user code is allowed to run between calling
73     /// this method and using it, as is the case for `match` and index
74     /// expressions.
75     pub fn as_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
76     where
77         M: Mirror<'tcx, Output = Expr<'tcx>>,
78     {
79         let place_builder = unpack!(block = self.as_place_builder(block, expr));
80         block.and(place_builder.into_place(self.hir.tcx()))
81     }
82
83     /// This is used when constructing a compound `Place`, so that we can avoid creating
84     /// intermediate `Place` values until we know the full set of projections.
85     fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
86     where
87         M: Mirror<'tcx, Output = Expr<'tcx>>,
88     {
89         let expr = self.hir.mirror(expr);
90         self.expr_as_place(block, expr, Mutability::Mut, None)
91     }
92
93     /// Compile `expr`, yielding a place that we can move from etc.
94     /// Mutability note: The caller of this method promises only to read from the resulting
95     /// place. The place itself may or may not be mutable:
96     /// * If this expr is a place expr like a.b, then we will return that place.
97     /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
98     pub fn as_read_only_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
99     where
100         M: Mirror<'tcx, Output = Expr<'tcx>>,
101     {
102         let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
103         block.and(place_builder.into_place(self.hir.tcx()))
104     }
105
106     /// This is used when constructing a compound `Place`, so that we can avoid creating
107     /// intermediate `Place` values until we know the full set of projections.
108     /// Mutability note: The caller of this method promises only to read from the resulting
109     /// place. The place itself may or may not be mutable:
110     /// * If this expr is a place expr like a.b, then we will return that place.
111     /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
112     fn as_read_only_place_builder<M>(
113         &mut self,
114         block: BasicBlock,
115         expr: M,
116     ) -> BlockAnd<PlaceBuilder<'tcx>>
117     where
118         M: Mirror<'tcx, Output = Expr<'tcx>>,
119     {
120         let expr = self.hir.mirror(expr);
121         self.expr_as_place(block, expr, Mutability::Not, None)
122     }
123
124     fn expr_as_place(
125         &mut self,
126         mut block: BasicBlock,
127         expr: Expr<'tcx>,
128         mutability: Mutability,
129         fake_borrow_temps: Option<&mut Vec<Local>>,
130     ) -> BlockAnd<PlaceBuilder<'tcx>> {
131         debug!("expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability);
132
133         let this = self;
134         let expr_span = expr.span;
135         let source_info = this.source_info(expr_span);
136         match expr.kind {
137             ExprKind::Scope { region_scope, lint_level, value } => {
138                 this.in_scope((region_scope, source_info), lint_level, |this| {
139                     let value = this.hir.mirror(value);
140                     this.expr_as_place(block, value, mutability, fake_borrow_temps)
141                 })
142             }
143             ExprKind::Field { lhs, name } => {
144                 let lhs = this.hir.mirror(lhs);
145                 let place_builder =
146                     unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,));
147                 block.and(place_builder.field(name, expr.ty))
148             }
149             ExprKind::Deref { arg } => {
150                 let arg = this.hir.mirror(arg);
151                 let place_builder =
152                     unpack!(block = this.expr_as_place(block, arg, mutability, fake_borrow_temps,));
153                 block.and(place_builder.deref())
154             }
155             ExprKind::Index { lhs, index } => this.lower_index_expression(
156                 block,
157                 lhs,
158                 index,
159                 mutability,
160                 fake_borrow_temps,
161                 expr.temp_lifetime,
162                 expr_span,
163                 source_info,
164             ),
165             ExprKind::SelfRef => block.and(PlaceBuilder::from(Local::new(1))),
166             ExprKind::VarRef { id } => {
167                 let place_builder = if this.is_bound_var_in_guard(id) {
168                     let index = this.var_local_id(id, RefWithinGuard);
169                     PlaceBuilder::from(index).deref()
170                 } else {
171                     let index = this.var_local_id(id, OutsideGuard);
172                     PlaceBuilder::from(index)
173                 };
174                 block.and(place_builder)
175             }
176
177             ExprKind::PlaceTypeAscription { source, user_ty } => {
178                 let source = this.hir.mirror(source);
179                 let place_builder = unpack!(
180                     block = this.expr_as_place(block, source, mutability, fake_borrow_temps,)
181                 );
182                 if let Some(user_ty) = user_ty {
183                     let annotation_index =
184                         this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
185                             span: source_info.span,
186                             user_ty,
187                             inferred_ty: expr.ty,
188                         });
189
190                     let place = place_builder.clone().into_place(this.hir.tcx());
191                     this.cfg.push(
192                         block,
193                         Statement {
194                             source_info,
195                             kind: StatementKind::AscribeUserType(
196                                 box (
197                                     place,
198                                     UserTypeProjection { base: annotation_index, projs: vec![] },
199                                 ),
200                                 Variance::Invariant,
201                             ),
202                         },
203                     );
204                 }
205                 block.and(place_builder)
206             }
207             ExprKind::ValueTypeAscription { source, user_ty } => {
208                 let source = this.hir.mirror(source);
209                 let temp =
210                     unpack!(block = this.as_temp(block, source.temp_lifetime, source, mutability));
211                 if let Some(user_ty) = user_ty {
212                     let annotation_index =
213                         this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
214                             span: source_info.span,
215                             user_ty,
216                             inferred_ty: expr.ty,
217                         });
218                     this.cfg.push(
219                         block,
220                         Statement {
221                             source_info,
222                             kind: StatementKind::AscribeUserType(
223                                 box (
224                                     Place::from(temp.clone()),
225                                     UserTypeProjection { base: annotation_index, projs: vec![] },
226                                 ),
227                                 Variance::Invariant,
228                             ),
229                         },
230                     );
231                 }
232                 block.and(PlaceBuilder::from(temp))
233             }
234
235             ExprKind::Array { .. }
236             | ExprKind::Tuple { .. }
237             | ExprKind::Adt { .. }
238             | ExprKind::Closure { .. }
239             | ExprKind::Unary { .. }
240             | ExprKind::Binary { .. }
241             | ExprKind::LogicalOp { .. }
242             | ExprKind::Box { .. }
243             | ExprKind::Cast { .. }
244             | ExprKind::Use { .. }
245             | ExprKind::NeverToAny { .. }
246             | ExprKind::Pointer { .. }
247             | ExprKind::Repeat { .. }
248             | ExprKind::Borrow { .. }
249             | ExprKind::AddressOf { .. }
250             | ExprKind::Match { .. }
251             | ExprKind::Loop { .. }
252             | ExprKind::Block { .. }
253             | ExprKind::Assign { .. }
254             | ExprKind::AssignOp { .. }
255             | ExprKind::Break { .. }
256             | ExprKind::Continue { .. }
257             | ExprKind::Return { .. }
258             | ExprKind::Literal { .. }
259             | ExprKind::StaticRef { .. }
260             | ExprKind::InlineAsm { .. }
261             | ExprKind::Yield { .. }
262             | ExprKind::Call { .. } => {
263                 // these are not places, so we need to make a temporary.
264                 debug_assert!(match Category::of(&expr.kind) {
265                     Some(Category::Place) => false,
266                     _ => true,
267                 });
268                 let temp =
269                     unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
270                 block.and(PlaceBuilder::from(temp))
271             }
272         }
273     }
274
275     /// Lower an index expression
276     ///
277     /// This has two complications;
278     ///
279     /// * We need to do a bounds check.
280     /// * We need to ensure that the bounds check can't be invalidated using an
281     ///   expression like `x[1][{x = y; 2}]`. We use fake borrows here to ensure
282     ///   that this is the case.
283     fn lower_index_expression(
284         &mut self,
285         mut block: BasicBlock,
286         base: ExprRef<'tcx>,
287         index: ExprRef<'tcx>,
288         mutability: Mutability,
289         fake_borrow_temps: Option<&mut Vec<Local>>,
290         temp_lifetime: Option<region::Scope>,
291         expr_span: Span,
292         source_info: SourceInfo,
293     ) -> BlockAnd<PlaceBuilder<'tcx>> {
294         let lhs = self.hir.mirror(base);
295
296         let base_fake_borrow_temps = &mut Vec::new();
297         let is_outermost_index = fake_borrow_temps.is_none();
298         let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
299
300         let base_place =
301             unpack!(block = self.expr_as_place(block, lhs, mutability, Some(fake_borrow_temps),));
302
303         // Making this a *fresh* temporary means we do not have to worry about
304         // the index changing later: Nothing will ever change this temporary.
305         // The "retagging" transformation (for Stacked Borrows) relies on this.
306         let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not,));
307
308         block = self.bounds_check(
309             block,
310             base_place.clone().into_place(self.hir.tcx()),
311             idx,
312             expr_span,
313             source_info,
314         );
315
316         if is_outermost_index {
317             self.read_fake_borrows(block, fake_borrow_temps, source_info)
318         } else {
319             self.add_fake_borrows_of_base(
320                 &base_place,
321                 block,
322                 fake_borrow_temps,
323                 expr_span,
324                 source_info,
325             );
326         }
327
328         block.and(base_place.index(idx))
329     }
330
331     fn bounds_check(
332         &mut self,
333         block: BasicBlock,
334         slice: Place<'tcx>,
335         index: Local,
336         expr_span: Span,
337         source_info: SourceInfo,
338     ) -> BasicBlock {
339         let usize_ty = self.hir.usize_ty();
340         let bool_ty = self.hir.bool_ty();
341         // bounds check:
342         let len = self.temp(usize_ty, expr_span);
343         let lt = self.temp(bool_ty, expr_span);
344
345         // len = len(slice)
346         self.cfg.push_assign(block, source_info, &len, Rvalue::Len(slice));
347         // lt = idx < len
348         self.cfg.push_assign(
349             block,
350             source_info,
351             &lt,
352             Rvalue::BinaryOp(
353                 BinOp::Lt,
354                 Operand::Copy(Place::from(index)),
355                 Operand::Copy(len.clone()),
356             ),
357         );
358         let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) };
359         // assert!(lt, "...")
360         self.assert(block, Operand::Move(lt), true, msg, expr_span)
361     }
362
363     fn add_fake_borrows_of_base(
364         &mut self,
365         base_place: &PlaceBuilder<'tcx>,
366         block: BasicBlock,
367         fake_borrow_temps: &mut Vec<Local>,
368         expr_span: Span,
369         source_info: SourceInfo,
370     ) {
371         let tcx = self.hir.tcx();
372         let place_ty =
373             Place::ty_from(&base_place.base, &base_place.projection, &self.local_decls, tcx);
374         if let ty::Slice(_) = place_ty.ty.kind {
375             // We need to create fake borrows to ensure that the bounds
376             // check that we just did stays valid. Since we can't assign to
377             // unsized values, we only need to ensure that none of the
378             // pointers in the base place are modified.
379             for (idx, elem) in base_place.projection.iter().enumerate().rev() {
380                 match elem {
381                     ProjectionElem::Deref => {
382                         let fake_borrow_deref_ty = Place::ty_from(
383                             &base_place.base,
384                             &base_place.projection[..idx],
385                             &self.local_decls,
386                             tcx,
387                         )
388                         .ty;
389                         let fake_borrow_ty =
390                             tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
391                         let fake_borrow_temp =
392                             self.local_decls.push(LocalDecl::new_temp(fake_borrow_ty, expr_span));
393                         let projection = tcx.intern_place_elems(&base_place.projection[..idx]);
394                         self.cfg.push_assign(
395                             block,
396                             source_info,
397                             &fake_borrow_temp.into(),
398                             Rvalue::Ref(
399                                 tcx.lifetimes.re_erased,
400                                 BorrowKind::Shallow,
401                                 Place { base: base_place.base.clone(), projection },
402                             ),
403                         );
404                         fake_borrow_temps.push(fake_borrow_temp);
405                     }
406                     ProjectionElem::Index(_) => {
407                         let index_ty = Place::ty_from(
408                             &base_place.base,
409                             &base_place.projection[..idx],
410                             &self.local_decls,
411                             tcx,
412                         );
413                         match index_ty.ty.kind {
414                             // The previous index expression has already
415                             // done any index expressions needed here.
416                             ty::Slice(_) => break,
417                             ty::Array(..) => (),
418                             _ => bug!("unexpected index base"),
419                         }
420                     }
421                     ProjectionElem::Field(..)
422                     | ProjectionElem::Downcast(..)
423                     | ProjectionElem::ConstantIndex { .. }
424                     | ProjectionElem::Subslice { .. } => (),
425                 }
426             }
427         }
428     }
429
430     fn read_fake_borrows(
431         &mut self,
432         bb: BasicBlock,
433         fake_borrow_temps: &mut Vec<Local>,
434         source_info: SourceInfo,
435     ) {
436         // All indexes have been evaluated now, read all of the
437         // fake borrows so that they are live across those index
438         // expressions.
439         for temp in fake_borrow_temps {
440             self.cfg.push_fake_read(bb, source_info, FakeReadCause::ForIndex, Place::from(*temp));
441         }
442     }
443 }