+//! 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 {
}
}
-/// 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>,
}
}
+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,
);
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);
}
}
}
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 ||
}
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);
}
}
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(..) => {
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);
}
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(_) => {
}
}
}
-
-/// 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",
- );
- }
- }
-}