]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/transform/const_prop.rs
Remove PlaceBase enum and make Place base field be local: Local
[rust.git] / src / librustc_mir / transform / const_prop.rs
index c36f7935115536b22d53c7529b39ebe8928b324a..79506f3f22a67ccde5ce2428266d0e8b22f31268 100644 (file)
@@ -4,28 +4,28 @@
 use std::borrow::Cow;
 use std::cell::Cell;
 
-use rustc::hir::def::DefKind;
-use rustc::hir::def_id::DefId;
-use rustc::hir::HirId;
 use rustc::mir::interpret::{InterpResult, PanicInfo, Scalar};
 use rustc::mir::visit::{
     MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
 };
 use rustc::mir::{
     read_only, AggregateKind, BasicBlock, BinOp, Body, BodyAndCache, ClearCrossCrate, Constant,
-    Local, LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, ReadOnlyBodyAndCache, Rvalue,
+    Local, LocalDecl, LocalKind, Location, Operand, Place, ReadOnlyBodyAndCache, Rvalue,
     SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind,
     UnOp, RETURN_PLACE,
 };
 use rustc::ty::layout::{
     HasDataLayout, HasTyCtxt, LayoutError, LayoutOf, Size, TargetDataLayout, TyLayout,
 };
-use rustc::ty::subst::InternalSubsts;
+use rustc::ty::subst::{InternalSubsts, Subst};
 use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable};
 use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_hir::HirId;
 use rustc_index::vec::IndexVec;
+use rustc_span::{Span, DUMMY_SP};
 use syntax::ast::Mutability;
-use syntax_pos::{Span, DUMMY_SP};
 
 use crate::const_eval::error_to_const_error;
 use crate::interpret::{
@@ -33,7 +33,6 @@
     LocalState, LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer,
     ScalarMaybeUndef, StackPopCleanup,
 };
-use crate::rustc::ty::subst::Subst;
 use crate::transform::{MirPass, MirSource};
 
 /// The maximum number of bytes that we'll allocate space for a return value.
@@ -125,6 +124,7 @@ fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
 
     fn find_mir_or_eval_fn(
         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _span: Span,
         _instance: ty::Instance<'tcx>,
         _args: &[OpTy<'tcx>],
         _ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
@@ -264,6 +264,7 @@ struct ConstPropagator<'mir, 'tcx> {
     // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
     // the last known `SourceInfo` here and just keep revisiting it.
     source_info: Option<SourceInfo>,
+    lint_root: Option<HirId>,
 }
 
 impl<'mir, 'tcx> LayoutOf for ConstPropagator<'mir, 'tcx> {
@@ -343,6 +344,7 @@ fn new(
             local_decls: body.local_decls.clone(),
             ret: ret.map(Into::into),
             source_info: None,
+            lint_root: None,
         }
     }
 
@@ -376,10 +378,6 @@ fn use_ecx<F, T>(&mut self, source_info: SourceInfo, f: F) -> Option<T>
         F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
     {
         self.ecx.tcx.span = source_info.span;
-        // FIXME(eddyb) move this to the `Panic(_)` error case, so that
-        // `f(self)` is always called, and that the only difference when the
-        // scope's `local_data` is missing, is that the lint isn't emitted.
-        let lint_root = self.lint_root(source_info)?;
         let r = match f(self) {
             Ok(val) => Some(val),
             Err(error) => {
@@ -413,7 +411,7 @@ fn use_ecx<F, T>(&mut self, source_info: SourceInfo, f: F) -> Option<T>
                         diagnostic.report_as_lint(
                             self.ecx.tcx,
                             "this expression will panic at runtime",
-                            lint_root,
+                            self.lint_root?,
                             None,
                         );
                     }
@@ -425,17 +423,19 @@ fn use_ecx<F, T>(&mut self, source_info: SourceInfo, f: F) -> Option<T>
         r
     }
 
-    fn eval_constant(
-        &mut self,
-        c: &Constant<'tcx>,
-        source_info: SourceInfo,
-    ) -> Option<Const<'tcx>> {
+    fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<Const<'tcx>> {
         self.ecx.tcx.span = c.span;
+
+        // FIXME we need to revisit this for #67176
+        if c.needs_subst() {
+            return None;
+        }
+
         match self.ecx.eval_const_to_op(c.literal, None) {
             Ok(op) => Some(op),
             Err(error) => {
                 let err = error_to_const_error(&self.ecx, error);
-                match self.lint_root(source_info) {
+                match self.lint_root {
                     Some(lint_root) if c.literal.needs_subst() => {
                         // Out of backwards compatibility we cannot report hard errors in unused
                         // generic functions using associated constants of the generic parameters.
@@ -462,13 +462,83 @@ fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option
 
     fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
         match *op {
-            Operand::Constant(ref c) => self.eval_constant(c, source_info),
+            Operand::Constant(ref c) => self.eval_constant(c),
             Operand::Move(ref place) | Operand::Copy(ref place) => {
                 self.eval_place(place, source_info)
             }
         }
     }
 
+    fn check_unary_op(&mut self, arg: &Operand<'tcx>, source_info: SourceInfo) -> Option<()> {
+        self.use_ecx(source_info, |this| {
+            let ty = arg.ty(&this.local_decls, this.tcx);
+
+            if ty.is_integral() {
+                let arg = this.ecx.eval_operand(arg, None)?;
+                let prim = this.ecx.read_immediate(arg)?;
+                // Need to do overflow check here: For actual CTFE, MIR
+                // generation emits code that does this before calling the op.
+                if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
+                    throw_panic!(OverflowNeg)
+                }
+            }
+
+            Ok(())
+        })?;
+
+        Some(())
+    }
+
+    fn check_binary_op(
+        &mut self,
+        op: BinOp,
+        left: &Operand<'tcx>,
+        right: &Operand<'tcx>,
+        source_info: SourceInfo,
+        place_layout: TyLayout<'tcx>,
+        overflow_check: bool,
+    ) -> Option<()> {
+        let r = self.use_ecx(source_info, |this| {
+            this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
+        })?;
+        if op == BinOp::Shr || op == BinOp::Shl {
+            let left_bits = place_layout.size.bits();
+            let right_size = r.layout.size;
+            let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
+            if r_bits.map_or(false, |b| b >= left_bits as u128) {
+                let lint_root = self.lint_root(source_info)?;
+                let dir = if op == BinOp::Shr { "right" } else { "left" };
+                self.tcx.lint_hir(
+                    ::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
+                    lint_root,
+                    source_info.span,
+                    &format!("attempt to shift {} with overflow", dir),
+                );
+                return None;
+            }
+        }
+
+        // If overflow checking is enabled (like in debug mode by default),
+        // then we'll already catch overflow when we evaluate the `Assert` statement
+        // in MIR. However, if overflow checking is disabled, then there won't be any
+        // `Assert` statement and so we have to do additional checking here.
+        if !overflow_check {
+            self.use_ecx(source_info, |this| {
+                let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
+                let (_, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
+
+                if overflow {
+                    let err = err_panic!(Overflow(op)).into();
+                    return Err(err);
+                }
+
+                Ok(())
+            })?;
+        }
+
+        Some(())
+    }
+
     fn const_prop(
         &mut self,
         rvalue: &Rvalue<'tcx>,
@@ -476,13 +546,16 @@ fn const_prop(
         source_info: SourceInfo,
         place: &Place<'tcx>,
     ) -> Option<()> {
-        let span = source_info.span;
-
         // #66397: Don't try to eval into large places as that can cause an OOM
         if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
             return None;
         }
 
+        // FIXME we need to revisit this for #67176
+        if rvalue.needs_subst() {
+            return None;
+        }
+
         let overflow_check = self.tcx.sess.overflow_checks();
 
         // Perform any special handling for specific Rvalue types.
@@ -498,66 +571,14 @@ fn const_prop(
             // if an overflow would occur.
             Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => {
                 trace!("checking UnaryOp(op = Neg, arg = {:?})", arg);
-
-                self.use_ecx(source_info, |this| {
-                    let ty = arg.ty(&this.local_decls, this.tcx);
-
-                    if ty.is_integral() {
-                        let arg = this.ecx.eval_operand(arg, None)?;
-                        let prim = this.ecx.read_immediate(arg)?;
-                        // Need to do overflow check here: For actual CTFE, MIR
-                        // generation emits code that does this before calling the op.
-                        if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
-                            throw_panic!(OverflowNeg)
-                        }
-                    }
-
-                    Ok(())
-                })?;
+                self.check_unary_op(arg, source_info)?;
             }
 
             // Additional checking: check for overflows on integer binary operations and report
             // them to the user as lints.
             Rvalue::BinaryOp(op, left, right) => {
                 trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
-
-                let r = self.use_ecx(source_info, |this| {
-                    this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
-                })?;
-                if *op == BinOp::Shr || *op == BinOp::Shl {
-                    let left_bits = place_layout.size.bits();
-                    let right_size = r.layout.size;
-                    let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
-                    if r_bits.map_or(false, |b| b >= left_bits as u128) {
-                        let lint_root = self.lint_root(source_info)?;
-                        let dir = if *op == BinOp::Shr { "right" } else { "left" };
-                        self.tcx.lint_hir(
-                            ::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
-                            lint_root,
-                            span,
-                            &format!("attempt to shift {} with overflow", dir),
-                        );
-                        return None;
-                    }
-                }
-
-                // If overflow checking is enabled (like in debug mode by default),
-                // then we'll already catch overflow when we evaluate the `Assert` statement
-                // in MIR. However, if overflow checking is disabled, then there won't be any
-                // `Assert` statement and so we have to do additional checking here.
-                if !overflow_check {
-                    self.use_ecx(source_info, |this| {
-                        let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
-                        let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?;
-
-                        if overflow {
-                            let err = err_panic!(Overflow(*op)).into();
-                            return Err(err);
-                        }
-
-                        Ok(())
-                    })?;
-                }
+                self.check_binary_op(*op, left, right, source_info, place_layout, overflow_check)?;
             }
 
             // Work around: avoid ICE in miri. FIXME(wesleywiser)
@@ -690,7 +711,8 @@ fn should_const_prop(&mut self, op: OpTy<'tcx>) -> bool {
                 ScalarMaybeUndef::Scalar(r),
             )) => l.is_bits() && r.is_bits(),
             interpret::Operand::Indirect(_) if mir_opt_level >= 2 => {
-                intern_const_alloc_recursive(&mut self.ecx, None, op.assert_mem_place())
+                let mplace = op.assert_mem_place(&self.ecx);
+                intern_const_alloc_recursive(&mut self.ecx, None, mplace, false)
                     .expect("failed to intern alloc");
                 true
             }
@@ -779,13 +801,14 @@ fn tcx(&self) -> TyCtxt<'tcx> {
     fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
         trace!("visit_constant: {:?}", constant);
         self.super_constant(constant, location);
-        self.eval_constant(constant, self.source_info.unwrap());
+        self.eval_constant(constant);
     }
 
     fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
         trace!("visit_statement: {:?}", statement);
         let source_info = statement.source_info;
         self.source_info = Some(source_info);
+        self.lint_root = self.lint_root(source_info);
         if let StatementKind::Assign(box (ref place, ref mut rval)) = statement.kind {
             let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
             if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
@@ -837,6 +860,7 @@ fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Loca
         let source_info = terminator.source_info;
         self.source_info = Some(source_info);
         self.super_terminator(terminator, location);
+        self.lint_root = self.lint_root(source_info);
         match &mut terminator.kind {
             TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => {
                 if let Some(value) = self.eval_operand(&cond, source_info) {
@@ -848,9 +872,7 @@ fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Loca
                         // doesn't use the invalid value
                         match cond {
                             Operand::Move(ref place) | Operand::Copy(ref place) => {
-                                if let PlaceBase::Local(local) = place.base {
-                                    self.remove_const(local);
-                                }
+                                self.remove_const(place.local);
                             }
                             Operand::Constant(_) => {}
                         }