]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_mir/build/expr/as_place.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustc_mir / build / expr / as_place.rs
index 8d2bef39bed4269fd65b0c8ed90a625c2bafe674..ddacda72e1e656b133aa60c02475adf9029741cb 100644 (file)
@@ -4,9 +4,11 @@ use crate::build::expr::category::Category;
 use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
 use crate::build::{BlockAnd, BlockAndExtension, Builder};
 use crate::hair::*;
+use rustc::middle::region;
 use rustc::mir::interpret::{PanicInfo::BoundsCheck};
 use rustc::mir::*;
-use rustc::ty::{CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
+use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
+use syntax_pos::Span;
 
 use rustc_index::vec::Idx;
 
@@ -68,6 +70,17 @@ impl From<PlaceBase<'tcx>> for PlaceBuilder<'tcx> {
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Compile `expr`, yielding a place that we can move from etc.
+    ///
+    /// WARNING: Any user code might:
+    /// * Invalidate any slice bounds checks performed.
+    /// * Change the address that this `Place` refers to.
+    /// * Modify the memory that this place refers to.
+    /// * Invalidate the memory that this place refers to, this will be caught
+    ///   by borrow checking.
+    ///
+    /// Extra care is needed if any user code is allowed to run between calling
+    /// this method and using it, as is the case for `match` and index
+    /// expressions.
     pub fn as_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
     where
         M: Mirror<'tcx, Output = Expr<'tcx>>,
@@ -83,7 +96,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         M: Mirror<'tcx, Output = Expr<'tcx>>,
     {
         let expr = self.hir.mirror(expr);
-        self.expr_as_place(block, expr, Mutability::Mut)
+        self.expr_as_place(block, expr, Mutability::Mut, None)
     }
 
     /// Compile `expr`, yielding a place that we can move from etc.
@@ -114,7 +127,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         M: Mirror<'tcx, Output = Expr<'tcx>>,
     {
         let expr = self.hir.mirror(expr);
-        self.expr_as_place(block, expr, Mutability::Not)
+        self.expr_as_place(block, expr, Mutability::Not, None)
     }
 
     fn expr_as_place(
@@ -122,6 +135,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         mut block: BasicBlock,
         expr: Expr<'tcx>,
         mutability: Mutability,
+        fake_borrow_temps: Option<&mut Vec<Local>>,
     ) -> BlockAnd<PlaceBuilder<'tcx>> {
         debug!(
             "expr_as_place(block={:?}, expr={:?}, mutability={:?})",
@@ -137,63 +151,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 lint_level,
                 value,
             } => this.in_scope((region_scope, source_info), lint_level, |this| {
-                if mutability == Mutability::Not {
-                    this.as_read_only_place_builder(block, value)
-                } else {
-                    this.as_place_builder(block, value)
-                }
+                let value = this.hir.mirror(value);
+                this.expr_as_place(block, value, mutability, fake_borrow_temps)
             }),
             ExprKind::Field { lhs, name } => {
-                let place_builder = unpack!(block = this.as_place_builder(block, lhs));
+                let lhs = this.hir.mirror(lhs);
+                let place_builder = unpack!(block = this.expr_as_place(
+                    block,
+                    lhs,
+                    mutability,
+                    fake_borrow_temps,
+                ));
                 block.and(place_builder.field(name, expr.ty))
             }
             ExprKind::Deref { arg } => {
-                let place_builder = unpack!(block = this.as_place_builder(block, arg));
+                let arg = this.hir.mirror(arg);
+                let place_builder = unpack!(block = this.expr_as_place(
+                    block,
+                    arg,
+                    mutability,
+                    fake_borrow_temps,
+                ));
                 block.and(place_builder.deref())
             }
             ExprKind::Index { lhs, index } => {
-                let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
-
-                let place_builder = unpack!(block = this.as_place_builder(block, lhs));
-                // Making this a *fresh* temporary also means we do not have to worry about
-                // the index changing later: Nothing will ever change this temporary.
-                // The "retagging" transformation (for Stacked Borrows) relies on this.
-                let idx = unpack!(block = this.as_temp(
+                this.lower_index_expression(
                     block,
-                    expr.temp_lifetime,
+                    lhs,
                     index,
-                    Mutability::Not,
-                ));
-
-                let slice = place_builder.clone().into_place(this.hir.tcx());
-                // bounds check:
-                let (len, lt) = (
-                    this.temp(usize_ty.clone(), expr_span),
-                    this.temp(bool_ty, expr_span),
-                );
-                this.cfg.push_assign(
-                    block,
-                    source_info, // len = len(slice)
-                    &len,
-                    Rvalue::Len(slice),
-                );
-                this.cfg.push_assign(
-                    block,
-                    source_info, // lt = idx < len
-                    &lt,
-                    Rvalue::BinaryOp(
-                        BinOp::Lt,
-                        Operand::Copy(Place::from(idx)),
-                        Operand::Copy(len.clone()),
-                    ),
-                );
-
-                let msg = BoundsCheck {
-                    len: Operand::Move(len),
-                    index: Operand::Copy(Place::from(idx)),
-                };
-                let success = this.assert(block, Operand::Move(lt), true, msg, expr_span);
-                success.and(place_builder.index(idx))
+                    mutability,
+                    fake_borrow_temps,
+                    expr.temp_lifetime,
+                    expr_span,
+                    source_info,
+                )
             }
             ExprKind::SelfRef => block.and(PlaceBuilder::from(Local::new(1))),
             ExprKind::VarRef { id } => {
@@ -206,16 +197,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 };
                 block.and(place_builder)
             }
-            ExprKind::StaticRef { id } => block.and(PlaceBuilder::from(
-                PlaceBase::Static(Box::new(Static {
-                    ty: expr.ty,
-                    kind: StaticKind::Static,
-                    def_id: id,
-                }))
-            )),
 
             ExprKind::PlaceTypeAscription { source, user_ty } => {
-                let place_builder = unpack!(block = this.as_place_builder(block, source));
+                let source = this.hir.mirror(source);
+                let place_builder = unpack!(block = this.expr_as_place(
+                    block,
+                    source,
+                    mutability,
+                    fake_borrow_temps,
+                ));
                 if let Some(user_ty) = user_ty {
                     let annotation_index = this.canonical_user_type_annotations.push(
                         CanonicalUserTypeAnnotation {
@@ -295,6 +285,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             | ExprKind::Continue { .. }
             | ExprKind::Return { .. }
             | ExprKind::Literal { .. }
+            | ExprKind::StaticRef { .. }
             | ExprKind::InlineAsm { .. }
             | ExprKind::Yield { .. }
             | ExprKind::Call { .. } => {
@@ -309,4 +300,199 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
         }
     }
+
+    /// Lower an index expression
+    ///
+    /// This has two complications;
+    ///
+    /// * We need to do a bounds check.
+    /// * We need to ensure that the bounds check can't be invalidated using an
+    ///   expression like `x[1][{x = y; 2}]`. We use fake borrows here to ensure
+    ///   that this is the case.
+    fn lower_index_expression(
+        &mut self,
+        mut block: BasicBlock,
+        base: ExprRef<'tcx>,
+        index: ExprRef<'tcx>,
+        mutability: Mutability,
+        fake_borrow_temps: Option<&mut Vec<Local>>,
+        temp_lifetime: Option<region::Scope>,
+        expr_span: Span,
+        source_info: SourceInfo
+    ) -> BlockAnd<PlaceBuilder<'tcx>> {
+        let lhs = self.hir.mirror(base);
+
+        let base_fake_borrow_temps = &mut Vec::new();
+        let is_outermost_index = fake_borrow_temps.is_none();
+        let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
+
+        let base_place = unpack!(block = self.expr_as_place(
+            block,
+            lhs,
+            mutability,
+            Some(fake_borrow_temps),
+        ));
+
+        // Making this a *fresh* temporary means we do not have to worry about
+        // the index changing later: Nothing will ever change this temporary.
+        // The "retagging" transformation (for Stacked Borrows) relies on this.
+        let idx = unpack!(block = self.as_temp(
+            block,
+            temp_lifetime,
+            index,
+            Mutability::Not,
+        ));
+
+        block = self.bounds_check(
+            block,
+            base_place.clone().into_place(self.hir.tcx()),
+            idx,
+            expr_span,
+            source_info,
+        );
+
+        if is_outermost_index {
+            self.read_fake_borrows(block, fake_borrow_temps, source_info)
+        } else {
+            self.add_fake_borrows_of_base(
+                &base_place,
+                block,
+                fake_borrow_temps,
+                expr_span,
+                source_info,
+            );
+        }
+
+        block.and(base_place.index(idx))
+    }
+
+    fn bounds_check(
+        &mut self,
+        block: BasicBlock,
+        slice: Place<'tcx>,
+        index: Local,
+        expr_span: Span,
+        source_info: SourceInfo,
+    ) -> BasicBlock {
+        let usize_ty = self.hir.usize_ty();
+        let bool_ty = self.hir.bool_ty();
+        // bounds check:
+        let len = self.temp(usize_ty, expr_span);
+        let lt = self.temp(bool_ty, expr_span);
+
+        // len = len(slice)
+        self.cfg.push_assign(
+            block,
+            source_info,
+            &len,
+            Rvalue::Len(slice),
+        );
+        // lt = idx < len
+        self.cfg.push_assign(
+            block,
+            source_info,
+            &lt,
+            Rvalue::BinaryOp(
+                BinOp::Lt,
+                Operand::Copy(Place::from(index)),
+                Operand::Copy(len.clone()),
+            ),
+        );
+        let msg = BoundsCheck {
+            len: Operand::Move(len),
+            index: Operand::Copy(Place::from(index)),
+        };
+        // assert!(lt, "...")
+        self.assert(block, Operand::Move(lt), true, msg, expr_span)
+    }
+
+    fn add_fake_borrows_of_base(
+        &mut self,
+        base_place: &PlaceBuilder<'tcx>,
+        block: BasicBlock,
+        fake_borrow_temps: &mut Vec<Local>,
+        expr_span: Span,
+        source_info: SourceInfo,
+    ) {
+        let tcx = self.hir.tcx();
+        let place_ty = Place::ty_from(
+            &base_place.base,
+            &base_place.projection,
+            &self.local_decls,
+            tcx,
+        );
+        if let ty::Slice(_) = place_ty.ty.kind {
+            // We need to create fake borrows to ensure that the bounds
+            // check that we just did stays valid. Since we can't assign to
+            // unsized values, we only need to ensure that none of the
+            // pointers in the base place are modified.
+            for (idx, elem) in base_place.projection.iter().enumerate().rev() {
+                match elem {
+                    ProjectionElem::Deref => {
+                        let fake_borrow_deref_ty = Place::ty_from(
+                            &base_place.base,
+                            &base_place.projection[..idx],
+                            &self.local_decls,
+                            tcx,
+                        ).ty;
+                        let fake_borrow_ty = tcx.mk_imm_ref(
+                            tcx.lifetimes.re_erased,
+                            fake_borrow_deref_ty,
+                        );
+                        let fake_borrow_temp = self.local_decls.push(
+                            LocalDecl::new_temp(fake_borrow_ty, expr_span)
+                        );
+                        let projection = tcx.intern_place_elems(&base_place.projection[..idx]);
+                        self.cfg.push_assign(
+                            block,
+                            source_info,
+                            &fake_borrow_temp.into(),
+                            Rvalue::Ref(
+                                tcx.lifetimes.re_erased,
+                                BorrowKind::Shallow,
+                                Place {
+                                    base: base_place.base.clone(),
+                                    projection,
+                                }
+                            ),
+                        );
+                        fake_borrow_temps.push(fake_borrow_temp);
+                    }
+                    ProjectionElem::Index(_) => {
+                        let index_ty = Place::ty_from(
+                            &base_place.base,
+                            &base_place.projection[..idx],
+                            &self.local_decls,
+                            tcx,
+                        );
+                        match index_ty.ty.kind {
+                            // The previous index expression has already
+                            // done any index expressions needed here.
+                            ty::Slice(_) => break,
+                            ty::Array(..) => (),
+                            _ => bug!("unexpected index base"),
+                        }
+                    }
+                    ProjectionElem::Field(..)
+                    | ProjectionElem::Downcast(..)
+                    | ProjectionElem::ConstantIndex { .. }
+                    | ProjectionElem::Subslice { .. } => (),
+                }
+            }
+        }
+    }
+
+    fn read_fake_borrows(
+        &mut self,
+        bb: BasicBlock,
+        fake_borrow_temps: &mut Vec<Local>,
+        source_info: SourceInfo,
+    ) {
+        // All indexes have been evaluated now, read all of the
+        // fake borrows so that they are live across those index
+        // expressions.
+        for temp in fake_borrow_temps {
+            self.cfg.push_fake_read(bb, source_info, FakeReadCause::ForIndex, Place::from(*temp));
+        }
+    }
 }