about summary refs log tree commit diff
path: root/src/librustc_mir
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc_mir')
-rw-r--r--src/librustc_mir/const_eval.rs144
-rw-r--r--src/librustc_mir/hair/cx/expr.rs7
-rw-r--r--src/librustc_mir/hair/pattern/mod.rs8
-rw-r--r--src/librustc_mir/interpret/eval_context.rs9
-rw-r--r--src/librustc_mir/interpret/memory.rs6
-rw-r--r--src/librustc_mir/lib.rs1
-rw-r--r--src/librustc_mir/monomorphize/collector.rs41
-rw-r--r--src/librustc_mir/transform/const_prop.rs39
8 files changed, 160 insertions, 95 deletions
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index bc917140bbd..6ad1536a630 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -17,13 +17,16 @@ use std::hash::Hash;
 use std::collections::hash_map::Entry;
 
 use rustc::hir::{self, def_id::DefId};
-use rustc::mir::interpret::ConstEvalErr;
+use rustc::hir::def::Def;
+use rustc::mir::interpret::{ConstEvalErr, ErrorHandled};
 use rustc::mir;
 use rustc::ty::{self, Ty, TyCtxt, Instance, query::TyCtxtAt};
 use rustc::ty::layout::{self, Size, LayoutOf, TyLayout};
 use rustc::ty::subst::Subst;
+use rustc::util::nodemap::FxHashSet;
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_data_structures::fx::FxHashMap;
+use rustc::util::common::ErrorReported;
 
 use syntax::ast::Mutability;
 use syntax::source_map::{Span, DUMMY_SP};
@@ -509,13 +512,11 @@ pub fn const_field<'a, 'tcx>(
         // this is not called for statics.
         op_to_const(&ecx, field, true)
     })();
-    result.map_err(|err| {
-        let (trace, span) = ecx.generate_stacktrace(None);
-        ConstEvalErr {
-            error: err,
-            stacktrace: trace,
-            span,
-        }.into()
+    result.map_err(|error| {
+        let stacktrace = ecx.generate_stacktrace(None);
+        let err = ::rustc::mir::interpret::ConstEvalErr { error, stacktrace, span: ecx.tcx.span };
+        err.report_as_error(ecx.tcx, "could not access field of constant");
+        ErrorHandled::Reported
     })
 }
 
@@ -531,41 +532,69 @@ pub fn const_variant_index<'a, 'tcx>(
     Ok(ecx.read_discriminant(op)?.1)
 }
 
-pub fn const_to_allocation_provider<'a, 'tcx>(
-    _tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    val: &'tcx ty::Const<'tcx>,
-) -> &'tcx Allocation {
-    // FIXME: This really does not need to be a query.  Instead, we should have a query for statics
-    // that returns an allocation directly (or an `AllocId`?), after doing a sanity check of the
-    // value and centralizing error reporting.
-    match val.val {
-        ConstValue::ByRef(_, alloc, offset) => {
-            assert_eq!(offset.bytes(), 0);
-            return alloc;
-        },
-        _ => bug!("const_to_allocation called on non-static"),
-    }
+fn validate_const<'a, 'tcx>(
+    tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
+    constant: &'tcx ty::Const<'tcx>,
+    key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
+) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
+    let cid = key.value;
+    let ecx = mk_eval_cx(tcx, cid.instance, key.param_env).unwrap();
+    let val = (|| {
+        let op = ecx.const_to_op(constant)?;
+        let mut todo = vec![(op, Vec::new())];
+        let mut seen = FxHashSet();
+        seen.insert(op);
+        while let Some((op, mut path)) = todo.pop() {
+            ecx.validate_operand(
+                op,
+                &mut path,
+                &mut seen,
+                &mut todo,
+            )?;
+        }
+        Ok(constant)
+    })();
+
+    val.map_err(|error| {
+        let stacktrace = ecx.generate_stacktrace(None);
+        let err = ::rustc::mir::interpret::ConstEvalErr { error, stacktrace, span: ecx.tcx.span };
+        match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
+            Ok(mut diag) => {
+                diag.note("The rules on what exactly is undefined behavior aren't clear, \
+                    so this check might be overzealous. Please open an issue on the rust compiler \
+                    repository if you believe it should not be considered undefined behavior",
+                );
+                diag.emit();
+                ErrorHandled::Reported
+            }
+            Err(err) => err,
+        }
+    })
 }
 
 pub fn const_eval_provider<'a, 'tcx>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
+    tcx.const_eval_raw(key).and_then(|val| {
+        validate_const(tcx, val, key)
+    })
+}
+
+pub fn const_eval_raw_provider<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
+) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
     trace!("const eval: {:?}", key);
     let cid = key.value;
     let def_id = cid.instance.def.def_id();
 
     if let Some(id) = tcx.hir.as_local_node_id(def_id) {
         let tables = tcx.typeck_tables_of(def_id);
-        let span = tcx.def_span(def_id);
 
         // Do match-check before building MIR
-        if tcx.check_match(def_id).is_err() {
-            return Err(ConstEvalErr {
-                error: EvalErrorKind::CheckMatchError.into(),
-                stacktrace: vec![],
-                span,
-            }.into());
+        if let Err(ErrorReported) = tcx.check_match(def_id) {
+            return Err(ErrorHandled::Reported)
         }
 
         if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
@@ -574,11 +603,7 @@ pub fn const_eval_provider<'a, 'tcx>(
 
         // Do not continue into miri if typeck errors occurred; it will fail horribly
         if tables.tainted_by_errors {
-            return Err(ConstEvalErr {
-                error: EvalErrorKind::CheckMatchError.into(),
-                stacktrace: vec![],
-                span,
-            }.into());
+            return Err(ErrorHandled::Reported)
         }
     };
 
@@ -593,19 +618,50 @@ pub fn const_eval_provider<'a, 'tcx>(
             }
         }
         op_to_const(&ecx, op, normalize)
-    }).map_err(|err| {
-        let (trace, span) = ecx.generate_stacktrace(None);
-        let err = ConstEvalErr {
-            error: err,
-            stacktrace: trace,
-            span,
-        };
+    }).map_err(|error| {
+        let stacktrace = ecx.generate_stacktrace(None);
+        let err = ConstEvalErr { error, stacktrace, span: ecx.tcx.span };
         if tcx.is_static(def_id).is_some() {
-            err.report_as_error(ecx.tcx, "could not evaluate static initializer");
+            let err = err.report_as_error(ecx.tcx, "could not evaluate static initializer");
             if tcx.sess.err_count() == 0 {
-                span_bug!(span, "static eval failure didn't emit an error: {:#?}", err);
+                span_bug!(ecx.tcx.span, "static eval failure didn't emit an error: {:#?}", err);
+            }
+            err
+        } else if def_id.is_local() {
+            // constant defined in this crate, we can figure out a lint level!
+            match tcx.describe_def(def_id) {
+                Some(Def::Const(_)) | Some(Def::AssociatedConst(_)) => {
+                    let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
+                    err.report_as_lint(
+                        tcx.at(tcx.def_span(def_id)),
+                        "any use of this value will cause an error",
+                        node_id,
+                    )
+                },
+                _ => if let Some(p) = cid.promoted {
+                    let span = tcx.optimized_mir(def_id).promoted[p].span;
+                    if let EvalErrorKind::ReferencedConstant = err.error.kind {
+                        err.report_as_error(
+                            tcx.at(span),
+                            "evaluation of constant expression failed",
+                        )
+                    } else {
+                        err.report_as_lint(
+                            tcx.at(span),
+                            "reaching this expression at runtime will panic or abort",
+                            tcx.hir.as_local_node_id(def_id).unwrap(),
+                        )
+                    }
+                } else {
+                    err.report_as_error(
+                        ecx.tcx,
+                        "evaluation of constant value failed",
+                    )
+                },
             }
+        } else {
+            // use of constant from other crate
+            err.report_as_error(ecx.tcx, "could not evaluate constant")
         }
-        err.into()
     })
 }
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 1df5f789751..48fcdd42ff5 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -15,7 +15,7 @@ use hair::cx::block;
 use hair::cx::to_ref::ToRef;
 use hair::util::UserAnnotatedTyHelpers;
 use rustc::hir::def::{Def, CtorKind};
-use rustc::mir::interpret::GlobalId;
+use rustc::mir::interpret::{GlobalId, ErrorHandled};
 use rustc::ty::{self, AdtKind, Ty};
 use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability};
 use rustc::ty::cast::CastKind as TyCastKind;
@@ -571,8 +571,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
             let span = cx.tcx.def_span(def_id);
             let count = match cx.tcx.at(span).const_eval(cx.param_env.and(global_id)) {
                 Ok(cv) => cv.unwrap_usize(cx.tcx),
-                Err(e) => {
-                    e.report_as_error(cx.tcx.at(span), "could not evaluate array length");
+                Err(ErrorHandled::Reported) => 0,
+                Err(ErrorHandled::TooGeneric) => {
+                    cx.tcx.sess.span_err(span, "array lengths can't depend on generic parameters");
                     0
                 },
             };
diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs
index cb974366a30..4649c28aff5 100644
--- a/src/librustc_mir/hair/pattern/mod.rs
+++ b/src/librustc_mir/hair/pattern/mod.rs
@@ -732,13 +732,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
                             Ok(value) => {
                                 return self.const_to_pat(instance, value, id, span)
                             },
-                            Err(err) => {
-                                err.report_as_error(
-                                    self.tcx.at(span),
+                            Err(_) => {
+                                self.tcx.sess.span_err(
+                                    span,
                                     "could not evaluate constant pattern",
                                 );
                                 PatternKind::Wild
-                            },
+                            }
                         }
                     },
                     None => {
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 18938892165..ce991c50330 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -611,8 +611,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
         } else {
             self.param_env
         };
-        self.tcx.const_eval(param_env.and(gid))
-            .map_err(|err| EvalErrorKind::ReferencedConstant(err).into())
+        self.tcx.const_eval(param_env.and(gid)).map_err(|_| {
+            EvalErrorKind::ReferencedConstant.into()
+        })
     }
 
     pub fn dump_place(&self, place: Place<M::PointerTag>) {
@@ -679,7 +680,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
         }
     }
 
-    pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> (Vec<FrameInfo>, Span) {
+    pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo> {
         let mut last_span = None;
         let mut frames = Vec::new();
         // skip 1 because the last frame is just the environment of the constant
@@ -716,7 +717,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
             frames.push(FrameInfo { span, location, lint_root });
         }
         trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span);
-        (frames, self.tcx.span)
+        frames
     }
 
     #[inline(always)]
diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index 9adca6c4297..b46e914133a 100644
--- a/src/librustc_mir/interpret/memory.rs
+++ b/src/librustc_mir/interpret/memory.rs
@@ -368,10 +368,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
             instance,
             promoted: None,
         };
-        tcx.const_eval(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| {
+        // use the raw query here to break validation cycles. Later uses of the static will call the
+        // full query anyway
+        tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|_| {
             // no need to report anything, the const_eval call takes care of that for statics
             assert!(tcx.is_static(def_id).is_some());
-            EvalErrorKind::ReferencedConstant(err).into()
+            EvalErrorKind::ReferencedConstant.into()
         }).map(|const_val| {
             if let ConstValue::ByRef(_, allocation, _) = const_val.val {
                 // We got tcx memory. Let the machine figure out whether and how to
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index 2f44dff2e22..d4495d3085d 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -94,6 +94,7 @@ pub fn provide(providers: &mut Providers) {
     shim::provide(providers);
     transform::provide(providers);
     providers.const_eval = const_eval::const_eval_provider;
+    providers.const_eval_raw = const_eval::const_eval_raw_provider;
     providers.check_match = hair::pattern::check_match;
 }
 
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index 6b60b5340ee..e8c482e836f 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -202,7 +202,7 @@ use rustc::session::config;
 use rustc::mir::{self, Location, Promoted};
 use rustc::mir::visit::Visitor as MirVisitor;
 use rustc::mir::mono::MonoItem;
-use rustc::mir::interpret::{Scalar, GlobalId, AllocType};
+use rustc::mir::interpret::{Scalar, GlobalId, AllocType, ErrorHandled};
 
 use monomorphize::{self, Instance};
 use rustc::util::nodemap::{FxHashSet, FxHashMap, DefIdMap};
@@ -988,6 +988,20 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
             hir::ItemKind::Const(..) => {
                 // const items only generate mono items if they are
                 // actually used somewhere. Just declaring them is insufficient.
+
+                // but even just declaring them must collect the items they refer to
+                let def_id = self.tcx.hir.local_def_id(item.id);
+
+                let instance = Instance::mono(self.tcx, def_id);
+                let cid = GlobalId {
+                    instance,
+                    promoted: None,
+                };
+                let param_env = ty::ParamEnv::reveal_all();
+
+                if let Ok(val) = self.tcx.const_eval(param_env.and(cid)) {
+                    collect_const(self.tcx, val, instance.substs, &mut self.output);
+                }
             }
             hir::ItemKind::Fn(..) => {
                 let def_id = self.tcx.hir.local_def_id(item.id);
@@ -1198,15 +1212,10 @@ fn collect_neighbours<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         };
         match tcx.const_eval(param_env.and(cid)) {
             Ok(val) => collect_const(tcx, val, instance.substs, output),
-            Err(err) => {
-                use rustc::mir::interpret::EvalErrorKind;
-                if let EvalErrorKind::ReferencedConstant(_) = err.error.kind {
-                    err.report_as_error(
-                        tcx.at(mir.promoted[i].span),
-                        "erroneous constant used",
-                    );
-                }
-            },
+            Err(ErrorHandled::Reported) => {},
+            Err(ErrorHandled::TooGeneric) => span_bug!(
+                mir.promoted[i].span, "collection encountered polymorphic constant",
+            ),
         }
     }
 }
@@ -1247,14 +1256,10 @@ fn collect_const<'a, 'tcx>(
             };
             match tcx.const_eval(param_env.and(cid)) {
                 Ok(val) => val.val,
-                Err(err) => {
-                    let span = tcx.def_span(def_id);
-                    err.report_as_error(
-                        tcx.at(span),
-                        "constant evaluation error",
-                    );
-                    return;
-                }
+                Err(ErrorHandled::Reported) => return,
+                Err(ErrorHandled::TooGeneric) => span_bug!(
+                    tcx.def_span(def_id), "collection encountered polymorphic constant",
+                ),
             }
         },
         _ => constant.val,
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 626baf207ee..6da40aa4a11 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -18,7 +18,7 @@ use rustc::mir::{NullOp, UnOp, StatementKind, Statement, BasicBlock, LocalKind};
 use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
 use rustc::mir::visit::{Visitor, PlaceContext};
 use rustc::mir::interpret::{
-    ConstEvalErr, EvalErrorKind, Scalar, GlobalId, EvalResult
+    ConstEvalErr, EvalErrorKind, Scalar, GlobalId, EvalResult,
 };
 use rustc::ty::{TyCtxt, self, Instance};
 use interpret::{self, EvalContext, Value, OpTy, MemoryKind, ScalarMaybeUndef};
@@ -45,12 +45,17 @@ impl MirPass for ConstProp {
             return;
         }
         match tcx.describe_def(source.def_id) {
-            // skip statics/consts because they'll be evaluated by miri anyway
-            Some(Def::Const(..)) |
-            Some(Def::Static(..)) => return,
-            // we still run on associated constants, because they might not get evaluated
-            // within the current crate
-            _ => {},
+            // Only run const prop on functions, methods, closures and associated constants
+            | Some(Def::Fn(_))
+            | Some(Def::Method(_))
+            | Some(Def::AssociatedConst(_))
+            | Some(Def::Closure(_))
+            => {}
+            // skip anon_const/statics/consts because they'll be evaluated by miri anyway
+            def => {
+                trace!("ConstProp skipped for {:?} ({:?})", source.def_id, def);
+                return
+            },
         }
         trace!("ConstProp starting for {:?}", source.def_id);
 
@@ -144,8 +149,8 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
         let r = match f(self) {
             Ok(val) => Some(val),
             Err(error) => {
-                let (stacktrace, span) = self.ecx.generate_stacktrace(None);
-                let diagnostic = ConstEvalErr { span, error, stacktrace };
+                let stacktrace = self.ecx.generate_stacktrace(None);
+                let diagnostic = ConstEvalErr { span: source_info.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
@@ -208,7 +213,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                     | ReadFromReturnPointer
                     | GeneratorResumedAfterReturn
                     | GeneratorResumedAfterPanic
-                    | ReferencedConstant(_)
+                    | ReferencedConstant
                     | InfiniteLoop
                     => {
                         // FIXME: report UB here
@@ -223,7 +228,6 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                     | UnimplementedTraitSelection
                     | TypeckError
                     | TooGeneric
-                    | CheckMatchError
                     // these are just noise
                     => {},
 
@@ -264,16 +268,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                 Some((op, c.span))
             },
             Err(error) => {
-                let (stacktrace, span) = self.ecx.generate_stacktrace(None);
-                let err = ConstEvalErr {
-                    span,
-                    error,
-                    stacktrace,
+                let stacktrace = self.ecx.generate_stacktrace(None);
+                let err = ::rustc::mir::interpret::ConstEvalErr {
+                    error, stacktrace, span: source_info.span,
                 };
-                err.report_as_error(
-                    self.tcx.at(source_info.span),
-                    "could not evaluate constant",
-                );
+                err.report_as_error(self.ecx.tcx, "erroneous constant used");
                 None
             },
         }