]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/transform/check_consts/validation.rs
Pass attrs to `do_dataflow` in new const checker
[rust.git] / src / librustc_mir / transform / check_consts / validation.rs
index eee8f4856fd8d737a28268e6110da86cd2553d6e..2d7b215b13c45d97e4497993294b7257403a3b6a 100644 (file)
@@ -1,24 +1,24 @@
+//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
+
 use rustc::hir::{self, def_id::DefId};
 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
 use rustc::mir::*;
-use rustc::session::config::nightly_options;
 use rustc::ty::cast::CastTy;
 use rustc::ty::{self, TyCtxt};
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
 use rustc_target::spec::abi::Abi;
-use syntax::feature_gate::{emit_feature_err, GateIssue};
 use syntax::symbol::sym;
-use syntax_pos::{Span, Symbol};
+use syntax_pos::Span;
 
 use std::cell::RefCell;
 use std::fmt;
 use std::ops::Deref;
-use std::rc::Rc;
 
 use crate::dataflow as old_dataflow;
 use super::{Item, Qualif, is_lang_panic_fn};
-use super::resolver::{QualifResolver, FlowSensitiveResolver};
+use super::resolver::{FlowSensitiveResolver, IndirectlyMutableResults, QualifResolver};
 use super::qualifs::{HasMutInterior, NeedsDrop};
+use super::ops::{self, NonConstOp};
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum CheckOpResult {
@@ -91,43 +91,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-/// An operation that is not *always* allowed in a const context.
-pub trait NonConstOp {
-    /// Whether this operation can be evaluated by miri.
-    const IS_SUPPORTED_IN_MIRI: bool = true;
-
-    /// Returns a boolean indicating whether the feature gate that would allow this operation is
-    /// enabled, or `None` if such a feature gate does not exist.
-    fn feature_gate(_tcx: TyCtxt<'tcx>) -> Option<bool> {
-        None
-    }
-
-    /// Returns `true` if this operation is allowed in the given item.
-    ///
-    /// This check should assume that we are not in a non-const `fn`, where all operations are
-    /// legal.
-    fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
-        Self::feature_gate(item.tcx).unwrap_or(false)
-    }
-
-    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-        let mut err = struct_span_err!(
-            item.tcx.sess,
-            span,
-            E0019,
-            "{} contains unimplemented expression type",
-            item.mode
-        );
-        if item.tcx.sess.teach(&err.get_code().unwrap()) {
-            err.note("A function call isn't allowed in the const's initialization expression \
-                      because the expression's value must be known at compile-time.");
-            err.note("Remember: you can't use a function call inside a const's initialization \
-                      expression! However, you can use it anywhere else.");
-        }
-        err.emit();
-    }
-}
-
 pub struct Qualifs<'a, 'mir, 'tcx> {
     has_mut_interior: FlowSensitiveResolver<'a, 'mir, 'tcx, HasMutInterior>,
     needs_drop: FlowSensitiveResolver<'a, 'mir, 'tcx, NeedsDrop>,
@@ -165,37 +128,47 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
+pub fn compute_indirectly_mutable_locals<'mir, 'tcx>(
+    item: &Item<'mir, 'tcx>,
+) -> RefCell<IndirectlyMutableResults<'mir, 'tcx>> {
+    let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len());
+
+    let indirectly_mutable_locals = old_dataflow::do_dataflow(
+        item.tcx,
+        item.body,
+        item.def_id,
+        &item.tcx.get_attrs(item.def_id),
+        &dead_unwinds,
+        old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body, item.param_env),
+        |_, local| old_dataflow::DebugFormatted::new(&local),
+    );
+
+    let indirectly_mutable_locals = old_dataflow::DataflowResultsCursor::new(
+        indirectly_mutable_locals,
+        item.body,
+    );
+
+    RefCell::new(indirectly_mutable_locals)
+}
+
 impl Validator<'a, 'mir, 'tcx> {
-    pub fn new(item: &'a Item<'mir, 'tcx>) -> Self {
+    pub fn new(
+        item: &'a Item<'mir, 'tcx>,
+        indirectly_mutable_locals: &'a RefCell<IndirectlyMutableResults<'mir, 'tcx>>,
+    ) -> Self {
         let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len());
 
-        let indirectly_mutable_locals = old_dataflow::do_dataflow(
-            item.tcx,
-            item.body,
-            item.def_id,
-            &[],
-            &dead_unwinds,
-            old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body, item.param_env),
-            |_, local| old_dataflow::DebugFormatted::new(&local),
-        );
-
-        let indirectly_mutable_locals = old_dataflow::DataflowResultsCursor::new(
-            indirectly_mutable_locals,
-            item.body,
-        );
-        let indirectly_mutable_locals = Rc::new(RefCell::new(indirectly_mutable_locals));
-
         let needs_drop = FlowSensitiveResolver::new(
             NeedsDrop,
             item,
-            indirectly_mutable_locals.clone(),
+            indirectly_mutable_locals,
             &dead_unwinds,
         );
 
         let has_mut_interior = FlowSensitiveResolver::new(
             HasMutInterior,
             item,
-            indirectly_mutable_locals.clone(),
+            indirectly_mutable_locals,
             &dead_unwinds,
         );
 
@@ -274,7 +247,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
             if let box [proj_base @ .., elem] = &place.projection {
                 if *elem == ProjectionElem::Deref {
                     let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
-                    if let ty::Ref(..) = base_ty.sty {
+                    if let ty::Ref(..) = base_ty.kind {
                         reborrow_place = Some(proj_base);
                     }
                 }
@@ -329,7 +302,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
             }
 
             Rvalue::BinaryOp(op, ref lhs, _) => {
-                if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).sty {
+                if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
                     assert!(op == BinOp::Eq || op == BinOp::Ne ||
                             op == BinOp::Le || op == BinOp::Lt ||
                             op == BinOp::Ge || op == BinOp::Gt ||
@@ -458,7 +431,7 @@ fn visit_projection(
                 }
 
                 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
-                if let ty::RawPtr(_) = base_ty.sty {
+                if let ty::RawPtr(_) = base_ty.kind {
                     self.check_op(ops::RawPtrDeref);
                 }
             }
@@ -494,8 +467,6 @@ fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
 
         self.qualifs.needs_drop.visit_statement(statement, location);
         self.qualifs.has_mut_interior.visit_statement(statement, location);
-        debug!("needs_drop: {:?}", self.qualifs.needs_drop.get());
-        debug!("has_mut_interior: {:?}", self.qualifs.has_mut_interior.get());
 
         match statement.kind {
             StatementKind::Assign(..) => {
@@ -521,8 +492,6 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
 
         self.qualifs.needs_drop.visit_terminator(terminator, location);
         self.qualifs.has_mut_interior.visit_terminator(terminator, location);
-        debug!("needs_drop: {:?}", self.qualifs.needs_drop.get());
-        debug!("has_mut_interior: {:?}", self.qualifs.has_mut_interior.get());
 
         self.super_terminator(terminator, location);
     }
@@ -535,7 +504,7 @@ fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Locat
             TerminatorKind::Call { func, .. } => {
                 let fn_ty = func.ty(self.body, self.tcx);
 
-                let def_id = match fn_ty.sty {
+                let def_id = match fn_ty.kind {
                     ty::FnDef(def_id, _) => def_id,
 
                     ty::FnPtr(_) => {
@@ -622,298 +591,3 @@ fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Locat
         }
     }
 }
-
-/// All implementers of `NonConstOp`.
-pub mod ops {
-    use super::*;
-
-    /// A `Downcast` projection.
-    #[derive(Debug)]
-    pub struct Downcast;
-    impl NonConstOp for Downcast {}
-
-    /// A function call where the callee is a pointer.
-    #[derive(Debug)]
-    pub struct FnCallIndirect;
-    impl NonConstOp for FnCallIndirect {
-        const IS_SUPPORTED_IN_MIRI: bool = false;
-
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            let mut err = item.tcx.sess.struct_span_err(
-                span,
-                &format!("function pointers are not allowed in const fn"));
-            err.emit();
-        }
-    }
-
-    /// A function call where the callee is not marked as `const`.
-    #[derive(Debug)]
-    pub struct FnCallNonConst(pub DefId);
-    impl NonConstOp for FnCallNonConst {
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            let mut err = struct_span_err!(
-                item.tcx.sess,
-                span,
-                E0015,
-                "calls in {}s are limited to constant functions, \
-                 tuple structs and tuple variants",
-                item.mode,
-            );
-            err.emit();
-        }
-    }
-
-    /// A function call where the callee is not a function definition or function pointer, e.g. a
-    /// closure.
-    ///
-    /// This can be subdivided in the future to produce a better error message.
-    #[derive(Debug)]
-    pub struct FnCallOther;
-    impl NonConstOp for FnCallOther {
-        const IS_SUPPORTED_IN_MIRI: bool = false;
-    }
-
-    /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
-    ///
-    /// Contains the name of the feature that would allow the use of this function.
-    #[derive(Debug)]
-    pub struct FnCallUnstable(pub DefId, pub Symbol);
-    impl NonConstOp for FnCallUnstable {
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            let FnCallUnstable(def_id, feature) = *self;
-
-            let mut err = item.tcx.sess.struct_span_err(span,
-                &format!("`{}` is not yet stable as a const fn",
-                        item.tcx.def_path_str(def_id)));
-            if nightly_options::is_nightly_build() {
-                help!(&mut err,
-                      "add `#![feature({})]` to the \
-                       crate attributes to enable",
-                      feature);
-            }
-            err.emit();
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct HeapAllocation;
-    impl NonConstOp for HeapAllocation {
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            let mut err = struct_span_err!(item.tcx.sess, span, E0010,
-                                           "allocations are not allowed in {}s", item.mode);
-            err.span_label(span, format!("allocation not allowed in {}s", item.mode));
-            if item.tcx.sess.teach(&err.get_code().unwrap()) {
-                err.note(
-                    "The value of statics and constants must be known at compile time, \
-                     and they live for the entire lifetime of a program. Creating a boxed \
-                     value allocates memory on the heap at runtime, and therefore cannot \
-                     be done at compile time."
-                );
-            }
-            err.emit();
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct IfOrMatch;
-    impl NonConstOp for IfOrMatch {}
-
-    #[derive(Debug)]
-    pub struct LiveDrop;
-    impl NonConstOp for LiveDrop {
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            struct_span_err!(item.tcx.sess, span, E0493,
-                             "destructors cannot be evaluated at compile-time")
-                .span_label(span, format!("{}s cannot evaluate destructors",
-                                          item.mode))
-                .emit();
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct Loop;
-    impl NonConstOp for Loop {}
-
-    #[derive(Debug)]
-    pub struct MutBorrow(pub BorrowKind);
-    impl NonConstOp for MutBorrow {
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            let kind = self.0;
-            if let BorrowKind::Mut { .. } = kind {
-                let mut err = struct_span_err!(item.tcx.sess, span, E0017,
-                                               "references in {}s may only refer \
-                                                to immutable values", item.mode);
-                err.span_label(span, format!("{}s require immutable values",
-                                                    item.mode));
-                if item.tcx.sess.teach(&err.get_code().unwrap()) {
-                    err.note("References in statics and constants may only refer \
-                              to immutable values.\n\n\
-                              Statics are shared everywhere, and if they refer to \
-                              mutable data one might violate memory safety since \
-                              holding multiple mutable references to shared data \
-                              is not allowed.\n\n\
-                              If you really want global mutable state, try using \
-                              static mut or a global UnsafeCell.");
-                }
-                err.emit();
-            } else {
-                span_err!(item.tcx.sess, span, E0492,
-                          "cannot borrow a constant which may contain \
-                           interior mutability, create a static instead");
-            }
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct MutDeref;
-    impl NonConstOp for MutDeref {}
-
-    #[derive(Debug)]
-    pub struct Panic;
-    impl NonConstOp for Panic {
-        fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
-            Some(tcx.features().const_panic)
-        }
-
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            emit_feature_err(
-                &item.tcx.sess.parse_sess,
-                sym::const_panic,
-                span,
-                GateIssue::Language,
-                &format!("panicking in {}s is unstable", item.mode),
-            );
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct RawPtrComparison;
-    impl NonConstOp for RawPtrComparison {
-        fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
-            Some(tcx.features().const_compare_raw_pointers)
-        }
-
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            emit_feature_err(
-                &item.tcx.sess.parse_sess,
-                sym::const_compare_raw_pointers,
-                span,
-                GateIssue::Language,
-                &format!("comparing raw pointers inside {}", item.mode),
-            );
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct RawPtrDeref;
-    impl NonConstOp for RawPtrDeref {
-        fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
-            Some(tcx.features().const_raw_ptr_deref)
-        }
-
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            emit_feature_err(
-                &item.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
-                span, GateIssue::Language,
-                &format!(
-                    "dereferencing raw pointers in {}s is unstable",
-                    item.mode,
-                ),
-            );
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct RawPtrToIntCast;
-    impl NonConstOp for RawPtrToIntCast {
-        fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
-            Some(tcx.features().const_raw_ptr_to_usize_cast)
-        }
-
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            emit_feature_err(
-                &item.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
-                span, GateIssue::Language,
-                &format!(
-                    "casting pointers to integers in {}s is unstable",
-                    item.mode,
-                ),
-            );
-        }
-    }
-
-    /// An access to a (non-thread-local) `static`.
-    #[derive(Debug)]
-    pub struct StaticAccess;
-    impl NonConstOp for StaticAccess {
-        fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
-            item.mode.is_static()
-        }
-
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            let mut err = struct_span_err!(item.tcx.sess, span, E0013,
-                                            "{}s cannot refer to statics, use \
-                                            a constant instead", item.mode);
-            if item.tcx.sess.teach(&err.get_code().unwrap()) {
-                err.note(
-                    "Static and const variables can refer to other const variables. \
-                        But a const variable cannot refer to a static variable."
-                );
-                err.help(
-                    "To fix this, the value can be extracted as a const and then used."
-                );
-            }
-            err.emit();
-        }
-    }
-
-    /// An access to a thread-local `static`.
-    #[derive(Debug)]
-    pub struct ThreadLocalAccess;
-    impl NonConstOp for ThreadLocalAccess {
-        const IS_SUPPORTED_IN_MIRI: bool = false;
-
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            span_err!(item.tcx.sess, span, E0625,
-                "thread-local statics cannot be \
-                accessed at compile-time");
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct Transmute;
-    impl NonConstOp for Transmute {
-        fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
-            Some(tcx.features().const_transmute)
-        }
-
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            emit_feature_err(
-                &item.tcx.sess.parse_sess, sym::const_transmute,
-                span, GateIssue::Language,
-                &format!("The use of std::mem::transmute() \
-                is gated in {}s", item.mode));
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct UnionAccess;
-    impl NonConstOp for UnionAccess {
-        fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
-            // Union accesses are stable in all contexts except `const fn`.
-            item.mode != Mode::ConstFn || Self::feature_gate(item.tcx).unwrap()
-        }
-
-        fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
-            Some(tcx.features().const_fn_union)
-        }
-
-        fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
-            emit_feature_err(
-                &item.tcx.sess.parse_sess, sym::const_fn_union,
-                span, GateIssue::Language,
-                "unions in const fn are unstable",
-            );
-        }
-    }
-}