diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/src/librustc_mir/transform/check_consts/mod.rs index ce26af64741..3f26b4f0f45 100644 --- a/src/librustc_mir/transform/check_consts/mod.rs +++ b/src/librustc_mir/transform/check_consts/mod.rs @@ -4,8 +4,9 @@ use rustc::ty::{self, TyCtxt}; pub use self::qualifs::Qualif; -mod resolver; +pub mod ops; mod qualifs; +mod resolver; pub mod validation; /// Information about the item currently being validated, as well as a reference to the global diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs new file mode 100644 index 00000000000..31e981d2b5a --- /dev/null +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -0,0 +1,337 @@ +use rustc::hir::def_id::DefId; +use rustc::mir::BorrowKind; +use rustc::session::config::nightly_options; +use rustc::ty::TyCtxt; +use syntax::feature_gate::{emit_feature_err, GateIssue}; +use syntax::symbol::sym; +use syntax_pos::{Span, Symbol}; + +use super::Item; +use super::validation::Mode; + +/// 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(); + } +} + +/// 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 { + 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 { + const IS_SUPPORTED_IN_MIRI: bool = false; + + 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", + ); + } +} diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 6a8ac3f3085..c61fdf5b970 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -1,14 +1,12 @@ 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_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; @@ -19,6 +17,7 @@ use crate::dataflow as old_dataflow; use super::{Item, Qualif, is_lang_panic_fn}; use super::resolver::{QualifResolver, FlowSensitiveResolver}; use super::qualifs::{HasMutInterior, NeedsDrop}; +use super::ops::{self, NonConstOp}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CheckOpResult { @@ -91,43 +90,6 @@ impl fmt::Display for Mode { } } -/// 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>, @@ -622,298 +584,3 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { } } } - -/// 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 { - 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 { - const IS_SUPPORTED_IN_MIRI: bool = false; - - 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", - ); - } - } -} diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 1a09a2ddb34..d9854237eeb 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -34,7 +34,7 @@ use std::usize; use rustc::hir::HirId; use crate::transform::{MirPass, MirSource}; use super::promote_consts::{self, Candidate, TempState}; -use crate::transform::check_consts::validation::{ops, NonConstOp}; +use crate::transform::check_consts::ops::{self, NonConstOp}; /// What kind of item we are in. #[derive(Copy, Clone, Debug, PartialEq, Eq)]