1 //! See docs in build/expr/mod.rs
3 use crate::build::expr::category::Category;
4 use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
5 use crate::build::{BlockAnd, BlockAndExtension, Builder};
7 use rustc::middle::region;
8 use rustc::mir::interpret::PanicInfo::BoundsCheck;
10 use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
13 use rustc_index::vec::Idx;
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.
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`.
22 struct PlaceBuilder<'tcx> {
24 projection: Vec<PlaceElem<'tcx>>,
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) }
32 fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
33 self.project(PlaceElem::Field(f, ty))
36 fn deref(self) -> Self {
37 self.project(PlaceElem::Deref)
40 fn index(self, index: Local) -> Self {
41 self.project(PlaceElem::Index(index))
44 fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
45 self.projection.push(elem);
50 impl From<Local> for PlaceBuilder<'tcx> {
51 fn from(local: Local) -> Self {
52 Self { base: local.into(), projection: Vec::new() }
56 impl From<PlaceBase> for PlaceBuilder<'tcx> {
57 fn from(base: PlaceBase) -> Self {
58 Self { base, projection: Vec::new() }
62 impl<'a, 'tcx> Builder<'a, 'tcx> {
63 /// Compile `expr`, yielding a place that we can move from etc.
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.
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
75 pub fn as_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
77 M: Mirror<'tcx, Output = Expr<'tcx>>,
79 let place_builder = unpack!(block = self.as_place_builder(block, expr));
80 block.and(place_builder.into_place(self.hir.tcx()))
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>>
87 M: Mirror<'tcx, Output = Expr<'tcx>>,
89 let expr = self.hir.mirror(expr);
90 self.expr_as_place(block, expr, Mutability::Mut, None)
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>>
100 M: Mirror<'tcx, Output = Expr<'tcx>>,
102 let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
103 block.and(place_builder.into_place(self.hir.tcx()))
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>(
116 ) -> BlockAnd<PlaceBuilder<'tcx>>
118 M: Mirror<'tcx, Output = Expr<'tcx>>,
120 let expr = self.hir.mirror(expr);
121 self.expr_as_place(block, expr, Mutability::Not, None)
126 mut block: BasicBlock,
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);
134 let expr_span = expr.span;
135 let source_info = this.source_info(expr_span);
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)
143 ExprKind::Field { lhs, name } => {
144 let lhs = this.hir.mirror(lhs);
146 unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,));
147 block.and(place_builder.field(name, expr.ty))
149 ExprKind::Deref { arg } => {
150 let arg = this.hir.mirror(arg);
152 unpack!(block = this.expr_as_place(block, arg, mutability, fake_borrow_temps,));
153 block.and(place_builder.deref())
155 ExprKind::Index { lhs, index } => this.lower_index_expression(
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()
171 let index = this.var_local_id(id, OutsideGuard);
172 PlaceBuilder::from(index)
174 block.and(place_builder)
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,)
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,
187 inferred_ty: expr.ty,
190 let place = place_builder.clone().into_place(this.hir.tcx());
195 kind: StatementKind::AscribeUserType(
198 UserTypeProjection { base: annotation_index, projs: vec![] },
205 block.and(place_builder)
207 ExprKind::ValueTypeAscription { source, user_ty } => {
208 let source = this.hir.mirror(source);
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,
216 inferred_ty: expr.ty,
222 kind: StatementKind::AscribeUserType(
224 Place::from(temp.clone()),
225 UserTypeProjection { base: annotation_index, projs: vec![] },
232 block.and(PlaceBuilder::from(temp))
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,
269 unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
270 block.and(PlaceBuilder::from(temp))
275 /// Lower an index expression
277 /// This has two complications;
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(
285 mut block: BasicBlock,
287 index: ExprRef<'tcx>,
288 mutability: Mutability,
289 fake_borrow_temps: Option<&mut Vec<Local>>,
290 temp_lifetime: Option<region::Scope>,
292 source_info: SourceInfo,
293 ) -> BlockAnd<PlaceBuilder<'tcx>> {
294 let lhs = self.hir.mirror(base);
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);
301 unpack!(block = self.expr_as_place(block, lhs, mutability, Some(fake_borrow_temps),));
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,));
308 block = self.bounds_check(
310 base_place.clone().into_place(self.hir.tcx()),
316 if is_outermost_index {
317 self.read_fake_borrows(block, fake_borrow_temps, source_info)
319 self.add_fake_borrows_of_base(
328 block.and(base_place.index(idx))
337 source_info: SourceInfo,
339 let usize_ty = self.hir.usize_ty();
340 let bool_ty = self.hir.bool_ty();
342 let len = self.temp(usize_ty, expr_span);
343 let lt = self.temp(bool_ty, expr_span);
346 self.cfg.push_assign(block, source_info, &len, Rvalue::Len(slice));
348 self.cfg.push_assign(
354 Operand::Copy(Place::from(index)),
355 Operand::Copy(len.clone()),
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)
363 fn add_fake_borrows_of_base(
365 base_place: &PlaceBuilder<'tcx>,
367 fake_borrow_temps: &mut Vec<Local>,
369 source_info: SourceInfo,
371 let tcx = self.hir.tcx();
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() {
381 ProjectionElem::Deref => {
382 let fake_borrow_deref_ty = Place::ty_from(
384 &base_place.projection[..idx],
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(
397 &fake_borrow_temp.into(),
399 tcx.lifetimes.re_erased,
401 Place { base: base_place.base.clone(), projection },
404 fake_borrow_temps.push(fake_borrow_temp);
406 ProjectionElem::Index(_) => {
407 let index_ty = Place::ty_from(
409 &base_place.projection[..idx],
413 match index_ty.ty.kind {
414 // The previous index expression has already
415 // done any index expressions needed here.
416 ty::Slice(_) => break,
418 _ => bug!("unexpected index base"),
421 ProjectionElem::Field(..)
422 | ProjectionElem::Downcast(..)
423 | ProjectionElem::ConstantIndex { .. }
424 | ProjectionElem::Subslice { .. } => (),
430 fn read_fake_borrows(
433 fake_borrow_temps: &mut Vec<Local>,
434 source_info: SourceInfo,
436 // All indexes have been evaluated now, read all of the
437 // fake borrows so that they are live across those index
439 for temp in fake_borrow_temps {
440 self.cfg.push_fake_read(bb, source_info, FakeReadCause::ForIndex, Place::from(*temp));