rustc_mir: use the new validator's Qualif in promotion.
This commit is contained in:
parent
6c55fb8227
commit
f2c8628920
6 changed files with 120 additions and 49 deletions
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Local>,
|
||||
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<Local>,
|
||||
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<Local>,
|
||||
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<Local>,
|
||||
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<Local>,
|
||||
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<Local>, 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<Local>,
|
||||
_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<Local>, 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<Local>, 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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<Local, TempState>,
|
||||
// FIXME(eddyb) compute these 2 on the fly.
|
||||
has_mut_interior: &'a BitSet<Local>,
|
||||
needs_drop: &'a BitSet<Local>,
|
||||
|
||||
// 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::<qualifs::HasMutInterior>(base);
|
||||
// HACK(eddyb) this should compute the same thing as
|
||||
// `<HasMutInterior as Qualif>::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::<qualifs::NeedsDrop>(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<Q: qualifs::Qualif>(&self, local: Local) -> bool {
|
||||
let per_local = &|l| self.qualif_local::<Q>(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
|
||||
// `<HasMutInterior as Qualif>::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::<qualifs::HasMutInterior>(*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<Local, TempState>,
|
||||
// FIXME(eddyb) compute these 2 on the fly.
|
||||
has_mut_interior: &BitSet<Local>,
|
||||
needs_drop: &BitSet<Local>,
|
||||
candidates: &[Candidate],
|
||||
) -> Vec<Candidate> {
|
||||
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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue