]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_lint/builtin.rs
Sanity-check all constants
[rust.git] / src / librustc_lint / builtin.rs
index 8df6df155112ec4365d0a4093166d2d99033d50d..7cb4f7d38609871bdcd81427191543ebdc2d5687 100644 (file)
@@ -40,6 +40,7 @@
 use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext};
 
 use std::collections::HashSet;
+use rustc::util::nodemap::FxHashSet;
 
 use syntax::tokenstream::{TokenTree, TokenStream};
 use syntax::ast;
@@ -1576,6 +1577,57 @@ fn get_lints(&self) -> LintArray {
     }
 }
 
+fn validate_const<'a, 'tcx>(
+    tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
+    constant: &ty::Const<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    gid: ::rustc::mir::interpret::GlobalId<'tcx>,
+    what: &str,
+) {
+    let mut ecx = ::rustc_mir::interpret::mk_eval_cx(tcx, gid.instance, param_env).unwrap();
+    let result = (|| {
+        let val = ecx.const_to_value(constant.val)?;
+        use rustc_target::abi::LayoutOf;
+        let layout = ecx.layout_of(constant.ty)?;
+        let place = ecx.allocate_place_for_value(val, layout, None)?;
+        let ptr = place.to_ptr()?;
+        let mut todo = vec![(ptr, layout.ty, String::new())];
+        let mut seen = FxHashSet();
+        seen.insert((ptr, layout.ty));
+        while let Some((ptr, ty, path)) = todo.pop() {
+            let layout = ecx.layout_of(ty)?;
+            ecx.validate_ptr_target(
+                ptr,
+                layout.align,
+                layout,
+                path,
+                &mut seen,
+                &mut todo,
+            )?;
+        }
+        Ok(())
+    })();
+    if let Err(err) = result {
+        let (trace, span) = ecx.generate_stacktrace(None);
+        let err = ::rustc::mir::interpret::ConstEvalErr {
+            error: err,
+            stacktrace: trace,
+            span,
+        };
+        let err = err.struct_error(
+            tcx.at(span),
+            &format!("this {} likely exhibits undefined behavior", what),
+        );
+        if let Some(mut err) = err {
+            err.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",
+            );
+            err.emit();
+        }
+    }
+}
+
 fn check_const(cx: &LateContext, body_id: hir::BodyId, what: &str) {
     let def_id = cx.tcx.hir.body_owner_def_id(body_id);
     let param_env = cx.tcx.param_env(def_id);
@@ -1583,13 +1635,19 @@ fn check_const(cx: &LateContext, body_id: hir::BodyId, what: &str) {
         instance: ty::Instance::mono(cx.tcx, def_id),
         promoted: None
     };
-    if let Err(err) = cx.tcx.const_eval(param_env.and(cid)) {
-        let span = cx.tcx.def_span(def_id);
-        err.report_as_lint(
-            cx.tcx.at(span),
-            &format!("this {} cannot be used", what),
-            cx.current_lint_root(),
-        );
+    match cx.tcx.const_eval(param_env.and(cid)) {
+        Ok(val) => validate_const(cx.tcx, val, param_env, cid, what),
+        Err(err) => {
+            // errors for statics are already reported directly in the query
+            if cx.tcx.is_static(def_id).is_none() {
+                let span = cx.tcx.def_span(def_id);
+                err.report_as_lint(
+                    cx.tcx.at(span),
+                    &format!("this {} cannot be used", what),
+                    cx.current_lint_root(),
+                );
+            }
+        },
     }
 }
 
@@ -1610,6 +1668,9 @@ fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
             hir::ItemKind::Const(_, body_id) => {
                 check_const(cx, body_id, "constant");
             },
+            hir::ItemKind::Static(_, _, body_id) => {
+                check_const(cx, body_id, "static");
+            },
             hir::ItemKind::Ty(ref ty, _) => hir::intravisit::walk_ty(
                 &mut UnusedBrokenConstVisitor(cx),
                 ty