about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_mir/interpret/cast.rs146
-rw-r--r--src/librustc_mir/interpret/eval_context.rs133
-rw-r--r--src/librustc_mir/transform/const_prop.rs48
-rw-r--r--src/test/run-pass/cast-rfc0401.rs7
-rw-r--r--src/test/ui/const-eval/promoted_errors.rs3
-rw-r--r--src/test/ui/const-eval/promoted_errors.stderr18
6 files changed, 207 insertions, 148 deletions
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs
index e69e7a522ab..7bcf4ef6588 100644
--- a/src/librustc_mir/interpret/cast.rs
+++ b/src/librustc_mir/interpret/cast.rs
@@ -1,13 +1,153 @@
-use rustc::ty::Ty;
-use rustc::ty::layout::LayoutOf;
+use rustc::ty::{self, Ty};
+use rustc::ty::layout::{self, LayoutOf};
 use syntax::ast::{FloatTy, IntTy, UintTy};
 
 use rustc_apfloat::ieee::{Single, Double};
 use super::{EvalContext, Machine};
-use rustc::mir::interpret::{Scalar, EvalResult, Pointer, PointerArithmetic};
+use rustc::mir::interpret::{Scalar, EvalResult, Pointer, PointerArithmetic, Value, EvalErrorKind};
+use rustc::mir::CastKind;
 use rustc_apfloat::Float;
+use interpret::eval_context::ValTy;
+use interpret::Place;
 
 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
+    crate fn cast(
+        &mut self,
+        src: ValTy<'tcx>,
+        kind: CastKind,
+        dest_ty: Ty<'tcx>,
+        dest: Place,
+    ) -> EvalResult<'tcx> {
+        use rustc::mir::CastKind::*;
+        match kind {
+            Unsize => {
+                let src_layout = self.layout_of(src.ty)?;
+                let dst_layout = self.layout_of(dest_ty)?;
+                self.unsize_into(src.value, src_layout, dest, dst_layout)?;
+            }
+
+            Misc => {
+                if self.type_is_fat_ptr(src.ty) {
+                    match (src.value, self.type_is_fat_ptr(dest_ty)) {
+                        (Value::ByRef { .. }, _) |
+                        // pointers to extern types
+                        (Value::Scalar(_),_) |
+                        // slices and trait objects to other slices/trait objects
+                        (Value::ScalarPair(..), true) => {
+                            let valty = ValTy {
+                                value: src.value,
+                                ty: dest_ty,
+                            };
+                            self.write_value(valty, dest)?;
+                        }
+                        // slices and trait objects to thin pointers (dropping the metadata)
+                        (Value::ScalarPair(data, _), false) => {
+                            let valty = ValTy {
+                                value: Value::Scalar(data),
+                                ty: dest_ty,
+                            };
+                            self.write_value(valty, dest)?;
+                        }
+                    }
+                } else {
+                    let src_layout = self.layout_of(src.ty)?;
+                    match src_layout.variants {
+                        layout::Variants::Single { index } => {
+                            if let Some(def) = src.ty.ty_adt_def() {
+                                let discr_val = def
+                                    .discriminant_for_variant(*self.tcx, index)
+                                    .val;
+                                let defined = self
+                                    .layout_of(dest_ty)
+                                    .unwrap()
+                                    .size
+                                    .bits() as u8;
+                                return self.write_scalar(
+                                    dest,
+                                    Scalar::Bits {
+                                        bits: discr_val,
+                                        defined,
+                                    },
+                                    dest_ty);
+                            }
+                        }
+                        layout::Variants::Tagged { .. } |
+                        layout::Variants::NicheFilling { .. } => {},
+                    }
+
+                    let src_val = self.value_to_scalar(src)?;
+                    let dest_val = self.cast_scalar(src_val, src.ty, dest_ty)?;
+                    let valty = ValTy {
+                        value: Value::Scalar(dest_val),
+                        ty: dest_ty,
+                    };
+                    self.write_value(valty, dest)?;
+                }
+            }
+
+            ReifyFnPointer => {
+                match src.ty.sty {
+                    ty::TyFnDef(def_id, substs) => {
+                        if self.tcx.has_attr(def_id, "rustc_args_required_const") {
+                            bug!("reifying a fn ptr that requires \
+                                    const arguments");
+                        }
+                        let instance: EvalResult<'tcx, _> = ty::Instance::resolve(
+                            *self.tcx,
+                            self.param_env,
+                            def_id,
+                            substs,
+                        ).ok_or_else(|| EvalErrorKind::TooGeneric.into());
+                        let fn_ptr = self.memory.create_fn_alloc(instance?);
+                        let valty = ValTy {
+                            value: Value::Scalar(fn_ptr.into()),
+                            ty: dest_ty,
+                        };
+                        self.write_value(valty, dest)?;
+                    }
+                    ref other => bug!("reify fn pointer on {:?}", other),
+                }
+            }
+
+            UnsafeFnPointer => {
+                match dest_ty.sty {
+                    ty::TyFnPtr(_) => {
+                        let mut src = src;
+                        src.ty = dest_ty;
+                        self.write_value(src, dest)?;
+                    }
+                    ref other => bug!("fn to unsafe fn cast on {:?}", other),
+                }
+            }
+
+            ClosureFnPointer => {
+                match src.ty.sty {
+                    ty::TyClosure(def_id, substs) => {
+                        let substs = self.tcx.subst_and_normalize_erasing_regions(
+                            self.substs(),
+                            ty::ParamEnv::reveal_all(),
+                            &substs,
+                        );
+                        let instance = ty::Instance::resolve_closure(
+                            *self.tcx,
+                            def_id,
+                            substs,
+                            ty::ClosureKind::FnOnce,
+                        );
+                        let fn_ptr = self.memory.create_fn_alloc(instance);
+                        let valty = ValTy {
+                            value: Value::Scalar(fn_ptr.into()),
+                            ty: dest_ty,
+                        };
+                        self.write_value(valty, dest)?;
+                    }
+                    ref other => bug!("closure fn pointer on {:?}", other),
+                }
+            }
+        }
+        Ok(())
+    }
+
     pub(super) fn cast_scalar(
         &self,
         val: Scalar,
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index a92c81e046a..fac6e8b69ef 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -770,135 +770,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
 
             Cast(kind, ref operand, cast_ty) => {
                 debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty);
-                use rustc::mir::CastKind::*;
-                match kind {
-                    Unsize => {
-                        let src = self.eval_operand(operand)?;
-                        let src_layout = self.layout_of(src.ty)?;
-                        let dst_layout = self.layout_of(dest_ty)?;
-                        self.unsize_into(src.value, src_layout, dest, dst_layout)?;
-                    }
-
-                    Misc => {
-                        let src = self.eval_operand(operand)?;
-                        if self.type_is_fat_ptr(src.ty) {
-                            match (src.value, self.type_is_fat_ptr(dest_ty)) {
-                                (Value::ByRef { .. }, _) |
-                                // pointers to extern types
-                                (Value::Scalar(_),_) |
-                                // slices and trait objects to other slices/trait objects
-                                (Value::ScalarPair(..), true) => {
-                                    let valty = ValTy {
-                                        value: src.value,
-                                        ty: dest_ty,
-                                    };
-                                    self.write_value(valty, dest)?;
-                                }
-                                // slices and trait objects to thin pointers (dropping the metadata)
-                                (Value::ScalarPair(data, _), false) => {
-                                    let valty = ValTy {
-                                        value: Value::Scalar(data),
-                                        ty: dest_ty,
-                                    };
-                                    self.write_value(valty, dest)?;
-                                }
-                            }
-                        } else {
-                            let src_layout = self.layout_of(src.ty)?;
-                            match src_layout.variants {
-                                layout::Variants::Single { index } => {
-                                    if let Some(def) = src.ty.ty_adt_def() {
-                                        let discr_val = def
-                                            .discriminant_for_variant(*self.tcx, index)
-                                            .val;
-                                        let defined = self
-                                            .layout_of(dest_ty)
-                                            .unwrap()
-                                            .size
-                                            .bits() as u8;
-                                        return self.write_scalar(
-                                            dest,
-                                            Scalar::Bits {
-                                                bits: discr_val,
-                                                defined,
-                                            },
-                                            dest_ty);
-                                    }
-                                }
-                                layout::Variants::Tagged { .. } |
-                                layout::Variants::NicheFilling { .. } => {},
-                            }
-
-                            let src_val = self.value_to_scalar(src)?;
-                            let dest_val = self.cast_scalar(src_val, src.ty, dest_ty)?;
-                            let valty = ValTy {
-                                value: Value::Scalar(dest_val),
-                                ty: dest_ty,
-                            };
-                            self.write_value(valty, dest)?;
-                        }
-                    }
-
-                    ReifyFnPointer => {
-                        match self.eval_operand(operand)?.ty.sty {
-                            ty::TyFnDef(def_id, substs) => {
-                                if self.tcx.has_attr(def_id, "rustc_args_required_const") {
-                                    bug!("reifying a fn ptr that requires \
-                                          const arguments");
-                                }
-                                let instance: EvalResult<'tcx, _> = ty::Instance::resolve(
-                                    *self.tcx,
-                                    self.param_env,
-                                    def_id,
-                                    substs,
-                                ).ok_or_else(|| EvalErrorKind::TooGeneric.into());
-                                let fn_ptr = self.memory.create_fn_alloc(instance?);
-                                let valty = ValTy {
-                                    value: Value::Scalar(fn_ptr.into()),
-                                    ty: dest_ty,
-                                };
-                                self.write_value(valty, dest)?;
-                            }
-                            ref other => bug!("reify fn pointer on {:?}", other),
-                        }
-                    }
-
-                    UnsafeFnPointer => {
-                        match dest_ty.sty {
-                            ty::TyFnPtr(_) => {
-                                let mut src = self.eval_operand(operand)?;
-                                src.ty = dest_ty;
-                                self.write_value(src, dest)?;
-                            }
-                            ref other => bug!("fn to unsafe fn cast on {:?}", other),
-                        }
-                    }
-
-                    ClosureFnPointer => {
-                        match self.eval_operand(operand)?.ty.sty {
-                            ty::TyClosure(def_id, substs) => {
-                                let substs = self.tcx.subst_and_normalize_erasing_regions(
-                                    self.substs(),
-                                    ty::ParamEnv::reveal_all(),
-                                    &substs,
-                                );
-                                let instance = ty::Instance::resolve_closure(
-                                    *self.tcx,
-                                    def_id,
-                                    substs,
-                                    ty::ClosureKind::FnOnce,
-                                );
-                                let fn_ptr = self.memory.create_fn_alloc(instance);
-                                let valty = ValTy {
-                                    value: Value::Scalar(fn_ptr.into()),
-                                    ty: dest_ty,
-                                };
-                                self.write_value(valty, dest)?;
-                            }
-                            ref other => bug!("closure fn pointer on {:?}", other),
-                        }
-                    }
-                }
+                let src = self.eval_operand(operand)?;
+                self.cast(src, kind, dest_ty, dest)?;
             }
 
             Discriminant(ref place) => {
@@ -1565,7 +1438,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
         }
     }
 
-    fn unsize_into(
+    crate fn unsize_into(
         &mut self,
         src: Value,
         src_layout: TyLayout<'tcx>,
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 3b3c28f6ae2..d15dd14084a 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -17,7 +17,7 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local
 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;
+use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind};
 use rustc::ty::{TyCtxt, self, Instance};
 use rustc::mir::interpret::{Value, Scalar, GlobalId, EvalResult};
 use interpret::EvalContext;
@@ -145,17 +145,23 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
         let r = match f(self) {
             Ok(val) => Some(val),
             Err(err) => {
-                let (frames, span) = self.ecx.generate_stacktrace(None);
-                let err = ConstEvalErr {
-                    span,
-                    error: err,
-                    stacktrace: frames,
-                };
-                err.report_as_lint(
-                    self.ecx.tcx,
-                    "this expression will panic at runtime",
-                    lint_root,
-                );
+                match err.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(
+                            self.ecx.tcx,
+                            "this expression will panic at runtime",
+                            lint_root,
+                        );
+                    }
+                }
                 None
             },
         };
@@ -257,10 +263,25 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
             },
             Rvalue::Repeat(..) |
             Rvalue::Ref(..) |
-            Rvalue::Cast(..) |
             Rvalue::Aggregate(..) |
             Rvalue::NullaryOp(NullOp::Box, _) |
             Rvalue::Discriminant(..) => None,
+
+            Rvalue::Cast(kind, ref operand, _) => {
+                let (value, ty, 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 = ::interpret::Place::from_ptr(dest_ptr, place_align);
+                    this.ecx.cast(ValTy { value, ty }, kind, place_ty, dest)?;
+                    Ok((
+                        Value::ByRef(dest_ptr.into(), place_align),
+                        place_ty,
+                        span,
+                    ))
+                })
+            }
+
             // FIXME(oli-obk): evaluate static/constant slice lengths
             Rvalue::Len(_) => None,
             Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
@@ -354,7 +375,6 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
                     )
                 } else {
                     if overflow {
-                        use rustc::mir::interpret::EvalErrorKind;
                         let err = EvalErrorKind::Overflow(op).into();
                         let _: Option<()> = self.use_ecx(source_info, |_| Err(err));
                         return None;
diff --git a/src/test/run-pass/cast-rfc0401.rs b/src/test/run-pass/cast-rfc0401.rs
index 5b6f6ccc627..022ed6c28c3 100644
--- a/src/test/run-pass/cast-rfc0401.rs
+++ b/src/test/run-pass/cast-rfc0401.rs
@@ -166,7 +166,12 @@ fn main()
 
     assert!(foo as usize != bar as usize);
 
-    assert_eq!(foo as i16, foo as usize as i16);
+    // Taking a few bits of a function's address is totally pointless and we detect that
+    // Disabling the lint to ensure that the assertion can still be run
+    #[allow(const_err)]
+    {
+        assert_eq!(foo as i16, foo as usize as i16);
+    }
 
     // fptr-ptr-cast
 
diff --git a/src/test/ui/const-eval/promoted_errors.rs b/src/test/ui/const-eval/promoted_errors.rs
index a39afb9bdd4..04e9bfbc03e 100644
--- a/src/test/ui/const-eval/promoted_errors.rs
+++ b/src/test/ui/const-eval/promoted_errors.rs
@@ -24,5 +24,8 @@ fn main() {
     //~| WARN const_err
     println!("{}", 1/(false as u32));
     //~^ WARN const_err
+    //~| WARN const_err
     let _x = 1/(false as u32);
+    //~^ WARN const_err
+    //~| WARN const_err
 }
diff --git a/src/test/ui/const-eval/promoted_errors.stderr b/src/test/ui/const-eval/promoted_errors.stderr
index a4c1c48a03d..9afe5f20af3 100644
--- a/src/test/ui/const-eval/promoted_errors.stderr
+++ b/src/test/ui/const-eval/promoted_errors.stderr
@@ -34,9 +34,27 @@ warning: this expression will panic at runtime
 LL |     let _x = 1/(1-1);
    |              ^^^^^^^ attempt to divide by zero
 
+warning: attempt to divide by zero
+  --> $DIR/promoted_errors.rs:25:20
+   |
+LL |     println!("{}", 1/(false as u32));
+   |                    ^^^^^^^^^^^^^^^^
+
 warning: this expression will panic at runtime
   --> $DIR/promoted_errors.rs:25:20
    |
 LL |     println!("{}", 1/(false as u32));
    |                    ^^^^^^^^^^^^^^^^ attempt to divide by zero
 
+warning: attempt to divide by zero
+  --> $DIR/promoted_errors.rs:28:14
+   |
+LL |     let _x = 1/(false as u32);
+   |              ^^^^^^^^^^^^^^^^
+
+warning: this expression will panic at runtime
+  --> $DIR/promoted_errors.rs:28:14
+   |
+LL |     let _x = 1/(false as u32);
+   |              ^^^^^^^^^^^^^^^^ attempt to divide by zero
+