diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/src/librustc_mir/transform/check_consts/mod.rs index 3a959a86edd83..0c643f462432e 100644 --- a/src/librustc_mir/transform/check_consts/mod.rs +++ b/src/librustc_mir/transform/check_consts/mod.rs @@ -11,7 +11,7 @@ use rustc::ty::{self, TyCtxt}; pub use self::qualifs::Qualif; pub mod ops; -mod qualifs; +pub mod qualifs; mod resolver; pub mod validation; @@ -23,6 +23,7 @@ pub struct Item<'mir, 'tcx> { def_id: DefId, param_env: ty::ParamEnv<'tcx>, mode: validation::Mode, + for_promotion: bool, } impl Item<'mir, 'tcx> { @@ -41,6 +42,28 @@ impl Item<'mir, 'tcx> { def_id, param_env, mode, + for_promotion: false, + } + } + + // 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); + + Item { + body, + tcx, + def_id, + param_env, + mode, + for_promotion: true, } } } diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index c8605e22e1084..e666dd9571f14 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -3,7 +3,6 @@ use rustc::mir::*; use rustc::mir::interpret::ConstValue; use rustc::ty::{self, Ty}; -use rustc_index::bit_set::BitSet; use syntax_pos::DUMMY_SP; use super::Item as ConstCx; @@ -44,7 +43,7 @@ pub trait Qualif { fn in_projection_structurally( cx: &ConstCx<'_, 'tcx>, - per_local: &BitSet, + per_local: &impl Fn(Local) -> bool, place: PlaceRef<'_, 'tcx>, ) -> bool { if let [proj_base @ .., elem] = place.projection { @@ -65,7 +64,7 @@ pub trait Qualif { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) => qualif, - ProjectionElem::Index(local) => qualif || per_local.contains(*local), + ProjectionElem::Index(local) => qualif || per_local(*local), } } else { bug!("This should be called if projection is not empty"); @@ -74,7 +73,7 @@ pub trait Qualif { fn in_projection( cx: &ConstCx<'_, 'tcx>, - per_local: &BitSet, + per_local: &impl Fn(Local) -> bool, place: PlaceRef<'_, 'tcx>, ) -> bool { Self::in_projection_structurally(cx, per_local, place) @@ -82,14 +81,14 @@ pub trait Qualif { fn in_place( cx: &ConstCx<'_, 'tcx>, - per_local: &BitSet, + per_local: &impl Fn(Local) -> bool, place: PlaceRef<'_, 'tcx>, ) -> bool { match place { PlaceRef { base: PlaceBase::Local(local), projection: [], - } => per_local.contains(*local), + } => per_local(*local), PlaceRef { base: PlaceBase::Static(box Static { kind: StaticKind::Promoted(..), @@ -112,7 +111,7 @@ pub trait Qualif { fn in_operand( cx: &ConstCx<'_, 'tcx>, - per_local: &BitSet, + per_local: &impl Fn(Local) -> bool, operand: &Operand<'tcx>, ) -> bool { match *operand { @@ -143,7 +142,7 @@ pub trait Qualif { fn in_rvalue_structurally( cx: &ConstCx<'_, 'tcx>, - per_local: &BitSet, + per_local: &impl Fn(Local) -> bool, rvalue: &Rvalue<'tcx>, ) -> bool { match *rvalue { @@ -185,13 +184,17 @@ pub trait Qualif { } } - fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet, rvalue: &Rvalue<'tcx>) -> bool { + fn in_rvalue( + cx: &ConstCx<'_, 'tcx>, + per_local: &impl Fn(Local) -> bool, + rvalue: &Rvalue<'tcx>, + ) -> bool { Self::in_rvalue_structurally(cx, per_local, rvalue) } fn in_call( cx: &ConstCx<'_, 'tcx>, - _per_local: &BitSet, + _per_local: &impl Fn(Local) -> bool, _callee: &Operand<'tcx>, _args: &[Operand<'tcx>], return_ty: Ty<'tcx>, @@ -216,7 +219,11 @@ impl Qualif for HasMutInterior { !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) } - fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet, rvalue: &Rvalue<'tcx>) -> bool { + fn in_rvalue( + cx: &ConstCx<'_, 'tcx>, + per_local: &impl Fn(Local) -> bool, + rvalue: &Rvalue<'tcx>, + ) -> bool { match *rvalue { // Returning `true` for `Rvalue::Ref` indicates the borrow isn't // allowed in constants (and the `Checker` will error), and/or it @@ -231,12 +238,11 @@ impl Qualif for HasMutInterior { // Inside a `static mut`, &mut [...] is also allowed. ty::Array(..) | ty::Slice(_) if cx.mode == Mode::StaticMut => {}, - // FIXME(ecstaticmorse): uncomment the following match arm to stop marking - // `&mut []` as `HasMutInterior`. - /* - ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0) - => {}, - */ + // 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 => {}, _ => return true, } @@ -275,7 +281,11 @@ impl Qualif for NeedsDrop { ty.needs_drop(cx.tcx, cx.param_env) } - fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet, rvalue: &Rvalue<'tcx>) -> bool { + fn in_rvalue( + cx: &ConstCx<'_, 'tcx>, + per_local: &impl Fn(Local) -> bool, + rvalue: &Rvalue<'tcx>, + ) -> bool { if let Rvalue::Aggregate(ref kind, _) = *rvalue { if let AggregateKind::Adt(def, ..) = **kind { if def.has_dtor(cx.tcx) { diff --git a/src/librustc_mir/transform/check_consts/resolver.rs b/src/librustc_mir/transform/check_consts/resolver.rs index fc9290d638018..d3f1c760724f1 100644 --- a/src/librustc_mir/transform/check_consts/resolver.rs +++ b/src/librustc_mir/transform/check_consts/resolver.rs @@ -81,7 +81,13 @@ where return_place: &mir::Place<'tcx>, ) { let return_ty = return_place.ty(self.item.body, self.item.tcx).ty; - let qualif = Q::in_call(self.item, &mut self.qualifs_per_local, func, args, return_ty); + let qualif = Q::in_call( + self.item, + &|l| self.qualifs_per_local.contains(l), + func, + args, + return_ty, + ); if !return_place.is_indirect() { self.assign_qualif_direct(return_place, qualif); } @@ -114,7 +120,7 @@ where rvalue: &mir::Rvalue<'tcx>, location: Location, ) { - let qualif = Q::in_rvalue(self.item, self.qualifs_per_local, rvalue); + let qualif = Q::in_rvalue(self.item, &|l| self.qualifs_per_local.contains(l), rvalue); if !place.is_indirect() { self.assign_qualif_direct(place, qualif); } @@ -129,7 +135,7 @@ where // here; that occurs in `apply_call_return_effect`. if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind { - let qualif = Q::in_operand(self.item, self.qualifs_per_local, value); + let qualif = Q::in_operand(self.item, &|l| self.qualifs_per_local.contains(l), value); if !dest.is_indirect() { self.assign_qualif_direct(dest, qualif); } diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 76a73adf03836..fa74663973656 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -369,11 +369,10 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { // it depends on `HasMutInterior` being set for mutable borrows as well as values with // interior mutability. if let Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue { - let rvalue_has_mut_interior = HasMutInterior::in_rvalue( - &self.item, - self.qualifs.has_mut_interior.get(), - rvalue, - ); + let rvalue_has_mut_interior = { + let has_mut_interior = self.qualifs.has_mut_interior.get(); + HasMutInterior::in_rvalue(&self.item, &|l| has_mut_interior.contains(l), rvalue) + }; if rvalue_has_mut_interior { let is_derived_from_illegal_borrow = match borrowed_place.as_local() { diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 4d411ad4f537c..7aaff5735f696 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -25,12 +25,13 @@ use syntax::ast::LitKind; use syntax::symbol::sym; use syntax_pos::{Span, DUMMY_SP}; -use rustc_index::bit_set::BitSet; use rustc_index::vec::{IndexVec, Idx}; use rustc_target::spec::abi::Abi; use std::{iter, mem, usize}; +use crate::transform::check_consts::{qualifs, Item as ConstCx}; + /// State of a temporary during collection and promotion. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum TempState { @@ -231,9 +232,9 @@ struct Validator<'a, 'tcx> { is_static_mut: bool, is_non_const_fn: bool, temps: &'a IndexVec, - // FIXME(eddyb) compute these 2 on the fly. - has_mut_interior: &'a BitSet, - needs_drop: &'a BitSet, + + // FIXME(eddyb) deduplicate the data in this vs other fields. + const_cx: ConstCx<'a, 'tcx>, /// Explicit promotion happens e.g. for constant arguments declared via /// `rustc_args_required_const`. @@ -276,15 +277,17 @@ impl<'tcx> Validator<'_, 'tcx> { PlaceBase::Local(local) => local, _ => return Err(Unpromotable), }; + self.validate_local(base)?; + if place.projection.contains(&ProjectionElem::Deref) { return Err(Unpromotable); } - // FIXME(eddyb) compute this on the fly. - let mut has_mut_interior = self.has_mut_interior.contains(base); + let mut has_mut_interior = + self.qualif_local::(base); // HACK(eddyb) this should compute the same thing as // `::in_projection` from - // `qualify_consts` but without recursion. + // `check_consts::qualifs` but without recursion. if has_mut_interior { // This allows borrowing fields which don't have // `HasMutInterior`, from a type that does, e.g.: @@ -311,8 +314,7 @@ impl<'tcx> Validator<'_, 'tcx> { if has_mut_interior { return Err(Unpromotable); } - // FIXME(eddyb) compute this on the fly. - if self.needs_drop.contains(base) { + if self.qualif_local::(base) { return Err(Unpromotable); } if let BorrowKind::Mut { .. } = kind { @@ -339,7 +341,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } - self.validate_local(base) + Ok(()) } _ => bug!() } @@ -373,6 +375,42 @@ impl<'tcx> Validator<'_, 'tcx> { } } + // FIXME(eddyb) maybe cache this? + fn qualif_local(&self, local: Local) -> bool { + let per_local = &|l| self.qualif_local::(l); + + if let TempState::Defined { location: loc, .. } = self.temps[local] { + let num_stmts = self.body[loc.block].statements.len(); + + if loc.statement_index < num_stmts { + let statement = &self.body[loc.block].statements[loc.statement_index]; + match &statement.kind { + StatementKind::Assign(box(_, rhs)) => { + Q::in_rvalue(&self.const_cx, per_local, rhs) + } + _ => { + span_bug!(statement.source_info.span, "{:?} is not an assignment", + statement); + } + } + } else { + let terminator = self.body[loc.block].terminator(); + match &terminator.kind { + TerminatorKind::Call { func, args, .. } => { + let return_ty = self.body.local_decls[local].ty; + Q::in_call(&self.const_cx, per_local, func, args, return_ty) + } + kind => { + span_bug!(terminator.source_info.span, "{:?} not promotable", kind); + } + } + } + } else { + let span = self.body.local_decls[local].source_info.span; + span_bug!(span, "{:?} not promotable, qualif_local shouldn't have been called", local); + } + } + // FIXME(eddyb) maybe cache this? fn validate_local(&self, local: Local) -> Result<(), Unpromotable> { if let TempState::Defined { location: loc, .. } = self.temps[local] { @@ -593,13 +631,14 @@ impl<'tcx> Validator<'_, 'tcx> { } } + self.validate_place(place)?; + // HACK(eddyb) this should compute the same thing as // `::in_projection` from - // `qualify_consts` but without recursion. + // `check_consts::qualifs` but without recursion. let mut has_mut_interior = match place.base { PlaceBase::Local(local) => { - // FIXME(eddyb) compute this on the fly. - self.has_mut_interior.contains(*local) + self.qualif_local::(*local) } PlaceBase::Static(_) => false, }; @@ -624,7 +663,7 @@ impl<'tcx> Validator<'_, 'tcx> { return Err(Unpromotable); } - self.validate_place(place) + Ok(()) } Rvalue::Aggregate(_, ref operands) => { @@ -680,9 +719,6 @@ pub fn validate_candidates( body: &Body<'tcx>, def_id: DefId, temps: &IndexVec, - // FIXME(eddyb) compute these 2 on the fly. - has_mut_interior: &BitSet, - needs_drop: &BitSet, candidates: &[Candidate], ) -> Vec { let mut validator = Validator { @@ -693,9 +729,8 @@ pub fn validate_candidates( is_static_mut: false, is_non_const_fn: false, temps, - // FIXME(eddyb) compute these 2 on the fly. - has_mut_interior, - needs_drop, + + const_cx: ConstCx::for_promotion(tcx, def_id, body), explicit: false, }; diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 1c5c84c692364..21feeb1fad61d 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1118,8 +1118,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { self.body, self.def_id, &self.temp_promotion_state, - &self.per_local.0[HasMutInterior::IDX], - &self.per_local.0[NeedsDrop::IDX], &self.unchecked_promotion_candidates, );