diff options
| author | Dylan MacKenzie <ecstaticmorse@gmail.com> | 2019-10-22 14:30:31 -0700 |
|---|---|---|
| committer | Dylan MacKenzie <ecstaticmorse@gmail.com> | 2019-10-25 20:56:56 -0700 |
| commit | b2d021aa2b7c701406c10a3110dd31f990368c97 (patch) | |
| tree | 81b4bb0a966b00743d73b2260c80879ff9b7699f | |
| parent | 084edc426f2e7e4bbedb5c6afa7fc422a52ee379 (diff) | |
| download | rust-b2d021aa2b7c701406c10a3110dd31f990368c97.tar.gz rust-b2d021aa2b7c701406c10a3110dd31f990368c97.zip | |
Make `check_consts::Item` work on non-const fns
This was originally only needed for validation, which is never run on non-const `fn`s. The new promotion pass wants to use it, however.
| -rw-r--r-- | src/librustc_mir/transform/check_consts/mod.rs | 98 | ||||
| -rw-r--r-- | src/librustc_mir/transform/check_consts/ops.rs | 33 | ||||
| -rw-r--r-- | src/librustc_mir/transform/check_consts/qualifs.rs | 21 | ||||
| -rw-r--r-- | src/librustc_mir/transform/check_consts/validation.rs | 71 | ||||
| -rw-r--r-- | src/librustc_mir/transform/promote_consts.rs | 2 |
5 files changed, 105 insertions, 120 deletions
diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/src/librustc_mir/transform/check_consts/mod.rs index 0c643f46243..61496ebb442 100644 --- a/src/librustc_mir/transform/check_consts/mod.rs +++ b/src/librustc_mir/transform/check_consts/mod.rs @@ -4,10 +4,12 @@ //! has interior mutability or needs to be dropped, as well as the visitor that emits errors when //! it finds operations that are invalid in a certain context. -use rustc::hir::def_id::DefId; +use rustc::hir::{self, def_id::DefId}; use rustc::mir; use rustc::ty::{self, TyCtxt}; +use std::fmt; + pub use self::qualifs::Qualif; pub mod ops; @@ -15,15 +17,14 @@ pub mod qualifs; mod resolver; pub mod validation; -/// Information about the item currently being validated, as well as a reference to the global +/// Information about the item currently being const-checked, as well as a reference to the global /// context. pub struct Item<'mir, 'tcx> { body: &'mir mir::Body<'tcx>, tcx: TyCtxt<'tcx>, def_id: DefId, param_env: ty::ParamEnv<'tcx>, - mode: validation::Mode, - for_promotion: bool, + const_kind: Option<ConstKind>, } impl Item<'mir, 'tcx> { @@ -33,41 +34,88 @@ impl Item<'mir, 'tcx> { body: &'mir mir::Body<'tcx>, ) -> Self { let param_env = tcx.param_env(def_id); - let mode = validation::Mode::for_item(tcx, def_id) - .expect("const validation must only be run inside a const context"); + let const_kind = ConstKind::for_item(tcx, def_id); Item { body, tcx, def_id, param_env, - mode, - for_promotion: false, + const_kind, } } - // HACK(eddyb) this is to get around the panic for a runtime fn from `Item::new`. - // Also, it allows promoting `&mut []`. - pub fn for_promotion( - tcx: TyCtxt<'tcx>, - def_id: DefId, - body: &'mir mir::Body<'tcx>, - ) -> Self { - let param_env = tcx.param_env(def_id); - let mode = validation::Mode::for_item(tcx, def_id) - .unwrap_or(validation::Mode::ConstFn); + /// Returns the kind of const context this `Item` represents (`const`, `static`, etc.). + /// + /// Panics if this `Item` is not const. + pub fn const_kind(&self) -> ConstKind { + self.const_kind.expect("`const_kind` must not be called on a non-const fn") + } +} - Item { - body, - tcx, - def_id, - param_env, - mode, - for_promotion: true, +/// The kinds of items which require compile-time evaluation. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ConstKind { + /// A `static` item. + Static, + /// A `static mut` item. + StaticMut, + /// A `const fn` item. + ConstFn, + /// A `const` item or an anonymous constant (e.g. in array lengths). + Const, +} + +impl ConstKind { + /// Returns the validation mode for the item with the given `DefId`, or `None` if this item + /// does not require validation (e.g. a non-const `fn`). + pub fn for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Self> { + use hir::BodyOwnerKind as HirKind; + + let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + + let mode = match tcx.hir().body_owner_kind(hir_id) { + HirKind::Closure => return None, + + HirKind::Fn if tcx.is_const_fn(def_id) => ConstKind::ConstFn, + HirKind::Fn => return None, + + HirKind::Const => ConstKind::Const, + + HirKind::Static(hir::MutImmutable) => ConstKind::Static, + HirKind::Static(hir::MutMutable) => ConstKind::StaticMut, + }; + + Some(mode) + } + + pub fn is_static(self) -> bool { + match self { + ConstKind::Static | ConstKind::StaticMut => true, + ConstKind::ConstFn | ConstKind::Const => false, + } + } + + /// Returns `true` if the value returned by this item must be `Sync`. + /// + /// This returns false for `StaticMut` since all accesses to one are `unsafe` anyway. + pub fn requires_sync(self) -> bool { + match self { + ConstKind::Static => true, + ConstKind::ConstFn | ConstKind::Const | ConstKind::StaticMut => false, } } } +impl fmt::Display for ConstKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ConstKind::Const => write!(f, "constant"), + ConstKind::Static | ConstKind::StaticMut => write!(f, "static"), + ConstKind::ConstFn => write!(f, "constant function"), + } + } +} fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { Some(def_id) == tcx.lang_items().panic_fn() || diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs index f457b739949..4b374cff809 100644 --- a/src/librustc_mir/transform/check_consts/ops.rs +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -8,8 +8,7 @@ use syntax::feature_gate::{emit_feature_err, GateIssue}; use syntax::symbol::sym; use syntax_pos::{Span, Symbol}; -use super::Item; -use super::validation::Mode; +use super::{ConstKind, Item}; /// An operation that is not *always* allowed in a const context. pub trait NonConstOp: std::fmt::Debug { @@ -36,7 +35,7 @@ pub trait NonConstOp: std::fmt::Debug { span, E0019, "{} contains unimplemented expression type", - item.mode + item.const_kind() ); if item.tcx.sess.teach(&err.get_code().unwrap()) { err.note("A function call isn't allowed in the const's initialization expression \ @@ -76,7 +75,7 @@ impl NonConstOp for FnCallNonConst { E0015, "calls in {}s are limited to constant functions, \ tuple structs and tuple variants", - item.mode, + item.const_kind(), ); err.emit(); } @@ -121,8 +120,8 @@ 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)); + "allocations are not allowed in {}s", item.const_kind()); + err.span_label(span, format!("allocation not allowed in {}s", item.const_kind())); if item.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "The value of statics and constants must be known at compile time, \ @@ -146,7 +145,7 @@ impl NonConstOp for LiveDrop { 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)) + item.const_kind())) .emit(); } } @@ -163,9 +162,9 @@ impl NonConstOp for MutBorrow { 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); + to immutable values", item.const_kind()); err.span_label(span, format!("{}s require immutable values", - item.mode)); + item.const_kind())); if item.tcx.sess.teach(&err.get_code().unwrap()) { err.note("References in statics and constants may only refer \ to immutable values.\n\n\ @@ -202,7 +201,7 @@ impl NonConstOp for Panic { sym::const_panic, span, GateIssue::Language, - &format!("panicking in {}s is unstable", item.mode), + &format!("panicking in {}s is unstable", item.const_kind()), ); } } @@ -220,7 +219,7 @@ impl NonConstOp for RawPtrComparison { sym::const_compare_raw_pointers, span, GateIssue::Language, - &format!("comparing raw pointers inside {}", item.mode), + &format!("comparing raw pointers inside {}", item.const_kind()), ); } } @@ -238,7 +237,7 @@ impl NonConstOp for RawPtrDeref { span, GateIssue::Language, &format!( "dereferencing raw pointers in {}s is unstable", - item.mode, + item.const_kind(), ), ); } @@ -257,7 +256,7 @@ impl NonConstOp for RawPtrToIntCast { span, GateIssue::Language, &format!( "casting pointers to integers in {}s is unstable", - item.mode, + item.const_kind(), ), ); } @@ -268,13 +267,13 @@ impl NonConstOp for RawPtrToIntCast { pub struct StaticAccess; impl NonConstOp for StaticAccess { fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool { - item.mode.is_static() + item.const_kind().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); + a constant instead", item.const_kind()); if item.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "Static and const variables can refer to other const variables. \ @@ -313,7 +312,7 @@ impl NonConstOp for Transmute { &item.tcx.sess.parse_sess, sym::const_transmute, span, GateIssue::Language, &format!("The use of std::mem::transmute() \ - is gated in {}s", item.mode)); + is gated in {}s", item.const_kind())); } } @@ -322,7 +321,7 @@ 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() + item.const_kind() != ConstKind::ConstFn || Self::feature_gate(item.tcx).unwrap() } fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> { diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index e666dd9571f..840ad303016 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -5,8 +5,7 @@ use rustc::mir::interpret::ConstValue; use rustc::ty::{self, Ty}; use syntax_pos::DUMMY_SP; -use super::Item as ConstCx; -use super::validation::Mode; +use super::{ConstKind, Item as ConstCx}; #[derive(Clone, Copy)] pub struct QualifSet(u8); @@ -236,13 +235,17 @@ impl Qualif for HasMutInterior { // mutably without consequences. match ty.kind { // Inside a `static mut`, &mut [...] is also allowed. - ty::Array(..) | ty::Slice(_) if cx.mode == Mode::StaticMut => {}, - - // FIXME(eddyb) the `cx.for_promotion` condition - // seems unnecessary, given that this is merely a ZST. - ty::Array(_, len) - if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0) - && cx.for_promotion => {}, + | ty::Array(..) + | ty::Slice(_) + if cx.const_kind == Some(ConstKind::StaticMut) + => {}, + + // FIXME(eddyb): We only return false for `&mut []` outside a const + // context which seems unnecessary given that this is merely a ZST. + | ty::Array(_, len) + if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0) + && cx.const_kind == None + => {}, _ => return true, } diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index fa746639736..c145fb41c96 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -1,10 +1,9 @@ //! 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::ty::cast::CastTy; -use rustc::ty::{self, TyCtxt}; +use rustc::ty; use rustc_index::bit_set::BitSet; use rustc_target::spec::abi::Abi; use syntax::symbol::sym; @@ -15,7 +14,7 @@ use std::fmt; use std::ops::Deref; use crate::dataflow as old_dataflow; -use super::{Item, Qualif, is_lang_panic_fn}; +use super::{ConstKind, Item, Qualif, is_lang_panic_fn}; use super::resolver::{FlowSensitiveResolver, IndirectlyMutableResults, QualifResolver}; use super::qualifs::{HasMutInterior, NeedsDrop}; use super::ops::{self, NonConstOp}; @@ -27,70 +26,6 @@ pub enum CheckOpResult { Allowed, } -/// What kind of item we are in. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Mode { - /// A `static` item. - Static, - /// A `static mut` item. - StaticMut, - /// A `const fn` item. - ConstFn, - /// A `const` item or an anonymous constant (e.g. in array lengths). - Const, -} - -impl Mode { - /// Returns the validation mode for the item with the given `DefId`, or `None` if this item - /// does not require validation (e.g. a non-const `fn`). - pub fn for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Self> { - use hir::BodyOwnerKind as HirKind; - - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - - let mode = match tcx.hir().body_owner_kind(hir_id) { - HirKind::Closure => return None, - - HirKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn, - HirKind::Fn => return None, - - HirKind::Const => Mode::Const, - - HirKind::Static(hir::MutImmutable) => Mode::Static, - HirKind::Static(hir::MutMutable) => Mode::StaticMut, - }; - - Some(mode) - } - - pub fn is_static(self) -> bool { - match self { - Mode::Static | Mode::StaticMut => true, - Mode::ConstFn | Mode::Const => false, - } - } - - /// Returns `true` if the value returned by this item must be `Sync`. - /// - /// This returns false for `StaticMut` since all accesses to one are `unsafe` anyway. - pub fn requires_sync(self) -> bool { - match self { - Mode::Static => true, - Mode::ConstFn | Mode::Const | Mode::StaticMut => false, - } - } -} - -impl fmt::Display for Mode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Mode::Const => write!(f, "constant"), - Mode::Static | Mode::StaticMut => write!(f, "static"), - Mode::ConstFn => write!(f, "constant function"), - } - } -} - pub struct Qualifs<'a, 'mir, 'tcx> { has_mut_interior: FlowSensitiveResolver<'a, 'mir, 'tcx, HasMutInterior>, needs_drop: FlowSensitiveResolver<'a, 'mir, 'tcx, NeedsDrop>, @@ -343,7 +278,7 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { let is_thread_local = self.tcx.has_attr(*def_id, sym::thread_local); if is_thread_local { self.check_op(ops::ThreadLocalAccess); - } else if self.mode == Mode::Static && context.is_mutating_use() { + } else if self.const_kind() == ConstKind::Static && context.is_mutating_use() { // this is not strictly necessary as miri will also bail out // For interior mutability we can't really catch this statically as that // goes through raw pointers and intermediate temporaries, so miri has diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 7aaff5735f6..4bf1fc60fe7 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -730,7 +730,7 @@ pub fn validate_candidates( is_non_const_fn: false, temps, - const_cx: ConstCx::for_promotion(tcx, def_id, body), + const_cx: ConstCx::new(tcx, def_id, body), explicit: false, }; |
