use rustc_data_structures::bitvec::BitVector;
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
-use rustc::dep_graph::DepNode;
use rustc::hir;
use rustc::hir::map as hir_map;
use rustc::hir::def_id::DefId;
use rustc::traits::{self, Reveal};
use rustc::ty::{self, TyCtxt, Ty};
use rustc::ty::cast::CastTy;
-use rustc::mir::repr::*;
-use rustc::mir::mir_map::MirMap;
-use rustc::mir::traversal::{self, ReversePostorder};
-use rustc::mir::transform::{Pass, MirMapPass, MirPassHook, MirSource};
+use rustc::mir::*;
+use rustc::mir::traversal::ReversePostorder;
+use rustc::mir::transform::{Pass, MirPass, MirSource};
use rustc::mir::visit::{LvalueContext, Visitor};
use rustc::util::nodemap::DefIdMap;
use syntax::abi::Abi;
tcx: TyCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParameterEnvironment<'tcx>,
qualif_map: &'a mut DefIdMap<Qualif>,
- mir_map: Option<&'a MirMap<'tcx>>,
- temp_qualif: IndexVec<Temp, Option<Qualif>>,
+ temp_qualif: IndexVec<Local, Option<Qualif>>,
return_qualif: Option<Qualif>,
qualif: Qualif,
const_fn_arg_vars: BitVector,
- temp_promotion_state: IndexVec<Temp, TempState>,
+ temp_promotion_state: IndexVec<Local, TempState>,
promotion_candidates: Vec<Candidate>
}
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParameterEnvironment<'tcx>,
qualif_map: &'a mut DefIdMap<Qualif>,
- mir_map: Option<&'a MirMap<'tcx>>,
def_id: DefId,
mir: &'a Mir<'tcx>,
mode: Mode)
tcx: tcx,
param_env: param_env,
qualif_map: qualif_map,
- mir_map: mir_map,
- temp_qualif: IndexVec::from_elem(None, &mir.temp_decls),
+ temp_qualif: IndexVec::from_elem(None, &mir.local_decls),
return_qualif: None,
qualif: Qualif::empty(),
- const_fn_arg_vars: BitVector::new(mir.var_decls.len()),
+ const_fn_arg_vars: BitVector::new(mir.local_decls.len()),
temp_promotion_state: temps,
promotion_candidates: vec![]
}
// Only handle promotable temps in non-const functions.
if self.mode == Mode::Fn {
- if let Lvalue::Temp(index) = *dest {
- if self.temp_promotion_state[index].is_promotable() {
+ if let Lvalue::Local(index) = *dest {
+ if self.mir.local_kind(index) == LocalKind::Temp
+ && self.temp_promotion_state[index].is_promotable() {
+ debug!("store to promotable temp {:?}", index);
store(&mut self.temp_qualif[index]);
}
}
}
match *dest {
- Lvalue::Temp(index) => store(&mut self.temp_qualif[index]),
- Lvalue::ReturnPointer => store(&mut self.return_qualif),
+ Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
+ debug!("store to temp {:?}", index);
+ store(&mut self.temp_qualif[index])
+ }
+ Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
+ debug!("store to return pointer {:?}", index);
+ store(&mut self.return_qualif)
+ }
Lvalue::Projection(box Projection {
- base: Lvalue::Temp(index),
+ base: Lvalue::Local(index),
elem: ProjectionElem::Deref
- }) if self.mir.temp_decls[index].ty.is_unique()
+ }) if self.mir.local_kind(index) == LocalKind::Temp
+ && self.mir.local_decls[index].ty.is_unique()
&& self.temp_qualif[index].map_or(false, |qualif| {
qualif.intersects(Qualif::NOT_CONST)
}) => {
/// Qualify a whole const, static initializer or const fn.
fn qualify_const(&mut self) -> Qualif {
+ debug!("qualifying {} {}", self.mode, self.tcx.item_path_str(self.def_id));
+
let mir = self.mir;
let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
TerminatorKind::Return => {
// Check for unused values. This usually means
// there are extra statements in the AST.
- for temp in mir.temp_decls.indices() {
+ for temp in mir.temps_iter() {
if self.temp_qualif[temp].is_none() {
continue;
}
// Make sure there are no extra unassigned variables.
self.qualif = Qualif::NOT_CONST;
- for index in 0..mir.var_decls.len() {
- if !self.const_fn_arg_vars.contains(index) {
- self.assign(&Lvalue::Var(Var::new(index)), Location {
+ for index in mir.vars_iter() {
+ if !self.const_fn_arg_vars.contains(index.index()) {
+ debug!("unassigned variable {:?}", index);
+ self.assign(&Lvalue::Local(index), Location {
block: bb,
statement_index: usize::MAX,
});
context: LvalueContext<'tcx>,
location: Location) {
match *lvalue {
- Lvalue::Arg(_) => {
- self.add(Qualif::FN_ARGUMENT);
- }
- Lvalue::Var(_) => {
- self.add(Qualif::NOT_CONST);
- }
- Lvalue::Temp(index) => {
- if !self.temp_promotion_state[index].is_promotable() {
- self.add(Qualif::NOT_PROMOTABLE);
+ Lvalue::Local(local) => match self.mir.local_kind(local) {
+ LocalKind::ReturnPointer => {
+ self.not_const();
}
+ LocalKind::Arg => {
+ self.add(Qualif::FN_ARGUMENT);
+ }
+ LocalKind::Var => {
+ self.add(Qualif::NOT_CONST);
+ }
+ LocalKind::Temp => {
+ if !self.temp_promotion_state[local].is_promotable() {
+ self.add(Qualif::NOT_PROMOTABLE);
+ }
- if let Some(qualif) = self.temp_qualif[index] {
- self.add(qualif);
- } else {
- self.not_const();
+ if let Some(qualif) = self.temp_qualif[local] {
+ self.add(qualif);
+ } else {
+ self.not_const();
+ }
}
- }
+ },
Lvalue::Static(_) => {
self.add(Qualif::STATIC);
if self.mode == Mode::Const || self.mode == Mode::ConstFn {
a constant instead", self.mode);
}
}
- Lvalue::ReturnPointer => {
- self.not_const();
- }
Lvalue::Projection(ref proj) => {
self.nest(|this| {
this.super_lvalue(lvalue, context, location);
} else {
let qualif = qualify_const_item_cached(self.tcx,
self.qualif_map,
- self.mir_map,
def_id);
self.add(qualif);
}
if self.mode == Mode::Fn || self.mode == Mode::ConstFn {
if !self.qualif.intersects(Qualif::NEVER_PROMOTE) {
// We can only promote direct borrows of temps.
- if let Lvalue::Temp(_) = *lvalue {
- self.promotion_candidates.push(candidate);
+ if let Lvalue::Local(local) = *lvalue {
+ if self.mir.local_kind(local) == LocalKind::Temp {
+ self.promotion_candidates.push(candidate);
+ }
}
}
}
self.visit_rvalue(rvalue, location);
// Check the allowed const fn argument forms.
- if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) {
- if self.const_fn_arg_vars.insert(index.index()) {
+ if let (Mode::ConstFn, &Lvalue::Local(index)) = (self.mode, dest) {
+ if self.mir.local_kind(index) == LocalKind::Var &&
+ self.const_fn_arg_vars.insert(index.index()) {
+
// Direct use of an argument is permitted.
- if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue {
- return;
+ if let Rvalue::Use(Operand::Consume(Lvalue::Local(local))) = *rvalue {
+ if self.mir.local_kind(local) == LocalKind::Arg {
+ return;
+ }
}
// Avoid a generic error for other uses of arguments.
if self.qualif.intersects(Qualif::FN_ARGUMENT) {
- let decl = &self.mir.var_decls[index];
- span_err!(self.tcx.sess, decl.source_info.span, E0022,
+ let decl = &self.mir.local_decls[index];
+ span_err!(self.tcx.sess, decl.source_info.unwrap().span, E0022,
"arguments of constant functions can only \
be immutable by-value bindings");
return;
fn qualify_const_item_cached<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
qualif_map: &mut DefIdMap<Qualif>,
- mir_map: Option<&MirMap<'tcx>>,
def_id: DefId)
-> Qualif {
match qualif_map.entry(def_id) {
}
}
- let extern_mir;
- let param_env_and_mir = if def_id.is_local() {
- mir_map.and_then(|map| map.map.get(&def_id)).map(|mir| {
- let node_id = tcx.map.as_local_node_id(def_id).unwrap();
- (ty::ParameterEnvironment::for_item(tcx, node_id), mir)
- })
- } else if let Some(mir) = tcx.sess.cstore.maybe_get_item_mir(tcx, def_id) {
- // These should only be monomorphic constants.
- extern_mir = mir;
- Some((tcx.empty_parameter_environment(), &extern_mir))
+ let param_env = if def_id.is_local() {
+ let node_id = tcx.map.as_local_node_id(def_id).unwrap();
+ ty::ParameterEnvironment::for_item(tcx, node_id)
} else {
- None
+ // These should only be monomorphic constants.
+ tcx.empty_parameter_environment()
};
- let (param_env, mir) = param_env_and_mir.unwrap_or_else(|| {
- bug!("missing constant MIR for {}", tcx.item_path_str(def_id))
- });
-
- let mut qualifier = Qualifier::new(tcx, param_env, qualif_map, mir_map,
- def_id, mir, Mode::Const);
+ let mir = &tcx.item_mir(def_id);
+ let mut qualifier = Qualifier::new(tcx, param_env, qualif_map, def_id, mir, Mode::Const);
let qualif = qualifier.qualify_const();
qualifier.qualif_map.insert(def_id, qualif);
qualif
}
-pub struct QualifyAndPromoteConstants;
+#[derive(Default)]
+pub struct QualifyAndPromoteConstants {
+ qualif_map: DefIdMap<Qualif>
+}
impl Pass for QualifyAndPromoteConstants {}
-impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
- fn run_pass<'a>(&mut self,
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- map: &mut MirMap<'tcx>,
- hooks: &mut [Box<for<'s> MirPassHook<'s>>]) {
- let mut qualif_map = DefIdMap();
-
- // First, visit `const` items, potentially recursing, to get
- // accurate MUTABLE_INTERIOR and NEEDS_DROP qualifications.
- let keys = map.map.keys();
- for &def_id in &keys {
- let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id));
- let id = tcx.map.as_local_node_id(def_id).unwrap();
- let src = MirSource::from_node(tcx, id);
- if let MirSource::Const(_) = src {
- qualify_const_item_cached(tcx, &mut qualif_map, Some(map), def_id);
+impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants {
+ fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ src: MirSource, mir: &mut Mir<'tcx>) {
+ let id = src.item_id();
+ let def_id = tcx.map.local_def_id(id);
+ let mode = match src {
+ MirSource::Fn(_) => {
+ if is_const_fn(tcx, def_id) {
+ Mode::ConstFn
+ } else {
+ Mode::Fn
+ }
}
- }
-
- // Then, handle everything else, without recursing,
- // as the MIR map is not shared, since promotion
- // in functions (including `const fn`) mutates it.
- for &def_id in &keys {
- let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id));
- let id = tcx.map.as_local_node_id(def_id).unwrap();
- let src = MirSource::from_node(tcx, id);
- let mode = match src {
- MirSource::Fn(_) => {
- if is_const_fn(tcx, def_id) {
- Mode::ConstFn
- } else {
- Mode::Fn
+ MirSource::Const(_) => {
+ match self.qualif_map.entry(def_id) {
+ Entry::Occupied(_) => return,
+ Entry::Vacant(entry) => {
+ // Guard against `const` recursion.
+ entry.insert(Qualif::RECURSIVE);
}
}
- MirSource::Const(_) => continue,
- MirSource::Static(_, hir::MutImmutable) => Mode::Static,
- MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
- MirSource::Promoted(..) => bug!()
- };
- let param_env = ty::ParameterEnvironment::for_item(tcx, id);
-
- let mir = map.map.get_mut(&def_id).unwrap();
- for hook in &mut *hooks {
- hook.on_mir_pass(tcx, src, mir, self, false);
+ Mode::Const
}
-
- if mode == Mode::Fn || mode == Mode::ConstFn {
- // This is ugly because Qualifier holds onto mir,
- // which can't be mutated until its scope ends.
- let (temps, candidates) = {
- let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
- None, def_id, mir, mode);
- if mode == Mode::ConstFn {
- // Enforce a constant-like CFG for `const fn`.
- qualifier.qualify_const();
- } else {
- while let Some((bb, data)) = qualifier.rpo.next() {
- qualifier.visit_basic_block_data(bb, data);
- }
+ MirSource::Static(_, hir::MutImmutable) => Mode::Static,
+ MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
+ MirSource::Promoted(..) => return
+ };
+ let param_env = ty::ParameterEnvironment::for_item(tcx, id);
+
+ if mode == Mode::Fn || mode == Mode::ConstFn {
+ // This is ugly because Qualifier holds onto mir,
+ // which can't be mutated until its scope ends.
+ let (temps, candidates) = {
+ let mut qualifier = Qualifier::new(tcx, param_env,
+ &mut self.qualif_map,
+ def_id, mir, mode);
+ if mode == Mode::ConstFn {
+ // Enforce a constant-like CFG for `const fn`.
+ qualifier.qualify_const();
+ } else {
+ while let Some((bb, data)) = qualifier.rpo.next() {
+ qualifier.visit_basic_block_data(bb, data);
}
+ }
- (qualifier.temp_promotion_state,
- qualifier.promotion_candidates)
- };
+ (qualifier.temp_promotion_state, qualifier.promotion_candidates)
+ };
- // Do the actual promotion, now that we know what's viable.
- promote_consts::promote_candidates(mir, tcx, temps, candidates);
- } else {
- let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
- None, def_id, mir, mode);
- qualifier.qualify_const();
- }
+ // Do the actual promotion, now that we know what's viable.
+ promote_consts::promote_candidates(mir, tcx, temps, candidates);
+ } else {
+ let mut qualifier = Qualifier::new(tcx, param_env,
+ &mut self.qualif_map,
+ def_id, mir, mode);
+ let qualif = qualifier.qualify_const();
- for hook in &mut *hooks {
- hook.on_mir_pass(tcx, src, mir, self, true);
+ if mode == Mode::Const {
+ qualifier.qualif_map.insert(def_id, qualif);
}
+ }
- // Statics must be Sync.
- if mode == Mode::Static {
- let ty = mir.return_ty;
- tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| {
- let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
- let mut fulfillment_cx = traits::FulfillmentContext::new();
- fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
- if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
- infcx.report_fulfillment_errors(&err);
- }
- });
- }
+ // Statics must be Sync.
+ if mode == Mode::Static {
+ let ty = mir.return_ty;
+ tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| {
+ let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
+ let mut fulfillment_cx = traits::FulfillmentContext::new();
+ fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
+ if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
+ infcx.report_fulfillment_errors(&err);
+ }
+ });
}
}
}