]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/transform/const_prop.rs
Auto merge of #53002 - QuietMisdreavus:brother-may-i-have-some-loops, r=pnkfelix
[rust.git] / src / librustc_mir / transform / const_prop.rs
index 94f96d46996d831b34029b52db6d97557248d2e7..05e51c5430d7fe5aafd0f4b9741974e09722dc70 100644 (file)
@@ -17,7 +17,7 @@
 use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind};
 use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
 use rustc::mir::visit::{Visitor, PlaceContext};
-use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind};
+use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind, ScalarMaybeUndef};
 use rustc::ty::{TyCtxt, self, Instance};
 use rustc::mir::interpret::{Value, Scalar, GlobalId, EvalResult};
 use interpret::EvalContext;
@@ -65,7 +65,7 @@ fn run_pass<'a, 'tcx>(&self,
     }
 }
 
-type Const<'tcx> = (Value, ty::Ty<'tcx>, Span);
+type Const<'tcx> = (Value, TyLayout<'tcx>, Span);
 
 /// Finds optimization opportunities on the MIR.
 struct ConstPropagator<'b, 'a, 'tcx:'a+'b> {
@@ -144,18 +144,100 @@ fn use_ecx<F, T>(
         };
         let r = match f(self) {
             Ok(val) => Some(val),
-            Err(err) => {
-                match err.kind {
+            Err(error) => {
+                let (stacktrace, span) = self.ecx.generate_stacktrace(None);
+                let diagnostic = ConstEvalErr { span, error, stacktrace };
+                use rustc::mir::interpret::EvalErrorKind::*;
+                match diagnostic.error.kind {
                     // don't report these, they make no sense in a const prop context
-                    EvalErrorKind::MachineError(_) => {},
-                    _ => {
-                        let (frames, span) = self.ecx.generate_stacktrace(None);
-                        let err = ConstEvalErr {
-                            span,
-                            error: err,
-                            stacktrace: frames,
-                        };
-                        err.report_as_lint(
+                    | MachineError(_)
+                    // at runtime these transformations might make sense
+                    // FIXME: figure out the rules and start linting
+                    | FunctionPointerTyMismatch(..)
+                    // fine at runtime, might be a register address or sth
+                    | ReadBytesAsPointer
+                    // fine at runtime
+                    | ReadForeignStatic
+                    | Unimplemented(_)
+                    // don't report const evaluator limits
+                    | StackFrameLimitReached
+                    | NoMirFor(..)
+                    | InlineAsm
+                    => {},
+
+                    | InvalidMemoryAccess
+                    | DanglingPointerDeref
+                    | DoubleFree
+                    | InvalidFunctionPointer
+                    | InvalidBool
+                    | InvalidDiscriminant
+                    | PointerOutOfBounds { .. }
+                    | InvalidNullPointerUsage
+                    | MemoryLockViolation { .. }
+                    | MemoryAcquireConflict { .. }
+                    | ValidationFailure(..)
+                    | InvalidMemoryLockRelease { .. }
+                    | DeallocatedLockedMemory { .. }
+                    | InvalidPointerMath
+                    | ReadUndefBytes
+                    | DeadLocal
+                    | InvalidBoolOp(_)
+                    | DerefFunctionPointer
+                    | ExecuteMemory
+                    | Intrinsic(..)
+                    | InvalidChar(..)
+                    | AbiViolation(_)
+                    | AlignmentCheckFailed{..}
+                    | CalledClosureAsFunction
+                    | VtableForArgumentlessMethod
+                    | ModifiedConstantMemory
+                    | AssumptionNotHeld
+                    // FIXME: should probably be removed and turned into a bug! call
+                    | TypeNotPrimitive(_)
+                    | ReallocatedWrongMemoryKind(_, _)
+                    | DeallocatedWrongMemoryKind(_, _)
+                    | ReallocateNonBasePtr
+                    | DeallocateNonBasePtr
+                    | IncorrectAllocationInformation(..)
+                    | UnterminatedCString(_)
+                    | HeapAllocZeroBytes
+                    | HeapAllocNonPowerOfTwoAlignment(_)
+                    | Unreachable
+                    | ReadFromReturnPointer
+                    | GeneratorResumedAfterReturn
+                    | GeneratorResumedAfterPanic
+                    | ReferencedConstant(_)
+                    | InfiniteLoop
+                    => {
+                        // FIXME: report UB here
+                    },
+
+                    | OutOfTls
+                    | TlsOutOfBounds
+                    | PathNotFound(_)
+                    => bug!("these should not be in rustc, but in miri's machine errors"),
+
+                    | Layout(_)
+                    | UnimplementedTraitSelection
+                    | TypeckError
+                    | TooGeneric
+                    | CheckMatchError
+                    // these are just noise
+                    => {},
+
+                    // non deterministic
+                    | ReadPointerAsBytes
+                    // FIXME: implement
+                    => {},
+
+                    | Panic
+                    | BoundsCheck{..}
+                    | Overflow(_)
+                    | OverflowNeg
+                    | DivisionByZero
+                    | RemainderByZero
+                    => {
+                        diagnostic.report_as_lint(
                             self.ecx.tcx,
                             "this expression will panic at runtime",
                             lint_root,
@@ -176,7 +258,10 @@ fn eval_constant(
     ) -> Option<Const<'tcx>> {
         self.ecx.tcx.span = source_info.span;
         match self.ecx.const_to_value(c.literal.val) {
-            Ok(val) => Some((val, c.literal.ty, c.span)),
+            Ok(val) => {
+                let layout = self.tcx.layout_of(self.param_env.and(c.literal.ty)).ok()?;
+                Some((val, layout, c.span))
+            },
             Err(error) => {
                 let (stacktrace, span) = self.ecx.generate_stacktrace(None);
                 let err = ConstEvalErr {
@@ -199,11 +284,11 @@ fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option
             Place::Projection(ref proj) => match proj.elem {
                 ProjectionElem::Field(field, _) => {
                     trace!("field proj on {:?}", proj.base);
-                    let (base, ty, span) = self.eval_place(&proj.base, source_info)?;
+                    let (base, layout, span) = self.eval_place(&proj.base, source_info)?;
                     let valty = self.use_ecx(source_info, |this| {
-                        this.ecx.read_field(base, None, field, ty)
+                        this.ecx.read_field(base, None, field, layout)
                     })?;
-                    Some((valty.value, valty.ty, span))
+                    Some((valty.0, valty.1, span))
                 },
                 _ => None,
             },
@@ -243,14 +328,14 @@ fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Optio
     fn const_prop(
         &mut self,
         rvalue: &Rvalue<'tcx>,
-        place_ty: ty::Ty<'tcx>,
+        place_layout: TyLayout<'tcx>,
         source_info: SourceInfo,
     ) -> Option<Const<'tcx>> {
         let span = source_info.span;
         match *rvalue {
             // This branch exists for the sanity type check
             Rvalue::Use(Operand::Constant(ref c)) => {
-                assert_eq!(c.ty, place_ty);
+                assert_eq!(c.ty, place_layout.ty);
                 self.eval_constant(c, source_info)
             },
             Rvalue::Use(ref op) => {
@@ -263,15 +348,15 @@ fn const_prop(
             Rvalue::Discriminant(..) => None,
 
             Rvalue::Cast(kind, ref operand, _) => {
-                let (value, ty, span) = self.eval_operand(operand, source_info)?;
+                let (value, layout, span) = self.eval_operand(operand, source_info)?;
                 self.use_ecx(source_info, |this| {
-                    let dest_ptr = this.ecx.alloc_ptr(place_ty)?;
-                    let place_align = this.ecx.layout_of(place_ty)?.align;
+                    let dest_ptr = this.ecx.alloc_ptr(place_layout)?;
+                    let place_align = place_layout.align;
                     let dest = ::interpret::Place::from_ptr(dest_ptr, place_align);
-                    this.ecx.cast(ValTy { value, ty }, kind, place_ty, dest)?;
+                    this.ecx.cast(ValTy { value, ty: layout.ty }, kind, place_layout.ty, dest)?;
                     Ok((
                         Value::ByRef(dest_ptr.into(), place_align),
-                        place_ty,
+                        place_layout,
                         span,
                     ))
                 })
@@ -280,15 +365,14 @@ fn const_prop(
             // FIXME(oli-obk): evaluate static/constant slice lengths
             Rvalue::Len(_) => None,
             Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
-                let param_env = self.tcx.param_env(self.source.def_id);
-                type_size_of(self.tcx, param_env, ty).map(|n| (
+                type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some((
                     Value::Scalar(Scalar::Bits {
                         bits: n as u128,
-                        defined: self.tcx.data_layout.pointer_size.bits() as u8,
-                    }),
-                    self.tcx.types.usize,
+                        size: self.tcx.data_layout.pointer_size.bytes() as u8,
+                    }.into()),
+                    self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?,
                     span,
-                ))
+                )))
             }
             Rvalue::UnaryOp(op, ref arg) => {
                 let def_id = if self.tcx.is_closure(self.source.def_id) {
@@ -304,10 +388,10 @@ fn const_prop(
 
                 let val = self.eval_operand(arg, source_info)?;
                 let prim = self.use_ecx(source_info, |this| {
-                    this.ecx.value_to_scalar(ValTy { value: val.0, ty: val.1 })
+                    this.ecx.value_to_scalar(ValTy { value: val.0, ty: val.1.ty })
                 })?;
                 let val = self.use_ecx(source_info, |this| this.ecx.unary_op(op, prim, val.1))?;
-                Some((Value::Scalar(val), place_ty, span))
+                Some((Value::Scalar(val.into()), place_layout, span))
             }
             Rvalue::CheckedBinaryOp(op, ref left, ref right) |
             Rvalue::BinaryOp(op, ref left, ref right) => {
@@ -325,7 +409,7 @@ fn const_prop(
                 }
 
                 let r = self.use_ecx(source_info, |this| {
-                    this.ecx.value_to_scalar(ValTy { value: right.0, ty: right.1 })
+                    this.ecx.value_to_scalar(ValTy { value: right.0, ty: right.1.ty })
                 })?;
                 if op == BinOp::Shr || op == BinOp::Shl {
                     let left_ty = left.ty(self.mir, self.tcx);
@@ -335,7 +419,7 @@ fn const_prop(
                         .unwrap()
                         .size
                         .bits();
-                    let right_size = self.tcx.layout_of(self.param_env.and(right.1)).unwrap().size;
+                    let right_size = right.1.size;
                     if r.to_bits(right_size).ok().map_or(false, |b| b >= left_bits as u128) {
                         let source_scope_local_data = match self.mir.source_scope_local_data {
                             ClearCrossCrate::Set(ref data) => data,
@@ -357,16 +441,16 @@ fn const_prop(
                 }
                 let left = self.eval_operand(left, source_info)?;
                 let l = self.use_ecx(source_info, |this| {
-                    this.ecx.value_to_scalar(ValTy { value: left.0, ty: left.1 })
+                    this.ecx.value_to_scalar(ValTy { value: left.0, ty: left.1.ty })
                 })?;
                 trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
                 let (val, overflow) = self.use_ecx(source_info, |this| {
-                    this.ecx.binary_op(op, l, left.1, r, right.1)
+                    this.ecx.binary_op(op, l, left.1.ty, r, right.1.ty)
                 })?;
                 let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue {
                     Value::ScalarPair(
-                        val,
-                        Scalar::from_bool(overflow),
+                        val.into(),
+                        Scalar::from_bool(overflow).into(),
                     )
                 } else {
                     if overflow {
@@ -374,9 +458,9 @@ fn const_prop(
                         let _: Option<()> = self.use_ecx(source_info, |_| Err(err));
                         return None;
                     }
-                    Value::Scalar(val)
+                    Value::Scalar(val.into())
                 };
-                Some((val, place_ty, span))
+                Some((val, place_layout, span))
             },
         }
     }
@@ -462,16 +546,18 @@ fn visit_statement(
     ) {
         trace!("visit_statement: {:?}", statement);
         if let StatementKind::Assign(ref place, ref rval) = statement.kind {
-            let place_ty = place
+            let place_ty: ty::Ty<'tcx> = place
                 .ty(&self.mir.local_decls, self.tcx)
                 .to_ty(self.tcx);
-            if let Some(value) = self.const_prop(rval, place_ty, statement.source_info) {
-                if let Place::Local(local) = *place {
-                    trace!("checking whether {:?} can be stored to {:?}", value, local);
-                    if self.can_const_prop[local] {
-                        trace!("storing {:?} to {:?}", value, local);
-                        assert!(self.places[local].is_none());
-                        self.places[local] = Some(value);
+            if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
+                if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) {
+                    if let Place::Local(local) = *place {
+                        trace!("checking whether {:?} can be stored to {:?}", value, local);
+                        if self.can_const_prop[local] {
+                            trace!("storing {:?} to {:?}", value, local);
+                            assert!(self.places[local].is_none());
+                            self.places[local] = Some(value);
+                        }
                     }
                 }
             }
@@ -490,7 +576,7 @@ fn visit_terminator_kind(
         if let TerminatorKind::Assert { expected, msg, cond, .. } = kind {
             if let Some(value) = self.eval_operand(cond, source_info) {
                 trace!("assertion on {:?} should be {:?}", value, expected);
-                if Value::Scalar(Scalar::from_bool(*expected)) != value.0 {
+                if Value::Scalar(Scalar::from_bool(*expected).into()) != value.0 {
                     // poison all places this operand references so that further code
                     // doesn't use the invalid value
                     match cond {
@@ -527,14 +613,18 @@ fn visit_terminator_kind(
                                 .eval_operand(len, source_info)
                                 .expect("len must be const");
                             let len = match len.0 {
-                                Value::Scalar(Scalar::Bits { bits, ..}) => bits,
+                                Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits {
+                                    bits, ..
+                                })) => bits,
                                 _ => bug!("const len not primitive: {:?}", len),
                             };
                             let index = self
                                 .eval_operand(index, source_info)
                                 .expect("index must be const");
                             let index = match index.0 {
-                                Value::Scalar(Scalar::Bits { bits, .. }) => bits,
+                                Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits {
+                                    bits, ..
+                                })) => bits,
                                 _ => bug!("const index not primitive: {:?}", index),
                             };
                             format!(