X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc_mir%2Ftransform%2Fcheck_consts%2Fvalidation.rs;h=2d7b215b13c45d97e4497993294b7257403a3b6a;hb=a1ad38f99cfc4ffa0f943f22834cc56afcef06e1;hp=eee8f4856fd8d737a28268e6110da86cd2553d6e;hpb=fc92d3b8202874c52328fbc3b7816f999586045a;p=rust.git diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index eee8f4856fd..2d7b215b13c 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -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 { - 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> { + 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>, + ) -> 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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", - ); - } - } -}