]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_mir/transform/qualify_consts.rs
New upstream version 1.14.0+dfsg1
[rustc.git] / src / librustc_mir / transform / qualify_consts.rs
index 2c03af2c8e97a93d67fa655683e662e0c38afa80..b33a7060e37537df665b2fca2c26d30a74537ba3 100644 (file)
@@ -16,7 +16,6 @@
 
 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;
@@ -25,10 +24,9 @@ use rustc::hir::map::blocks::FnLikeNode;
 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;
@@ -142,12 +140,11 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     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>
 }
 
@@ -155,7 +152,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
     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)
@@ -172,11 +168,10 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
             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![]
         }
@@ -332,8 +327,10 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
 
         // 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]);
                 }
             }
@@ -341,13 +338,20 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
         }
 
         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)
                }) => {
@@ -366,6 +370,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
 
     /// 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());
@@ -399,7 +405,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                 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;
                         }
@@ -424,9 +430,10 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
 
                     // 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,
                             });
@@ -480,23 +487,28 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                     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 {
@@ -505,9 +517,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                                a constant instead", self.mode);
                 }
             }
-            Lvalue::ReturnPointer => {
-                self.not_const();
-            }
             Lvalue::Projection(ref proj) => {
                 self.nest(|this| {
                     this.super_lvalue(lvalue, context, location);
@@ -581,7 +590,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                     } else {
                         let qualif = qualify_const_item_cached(self.tcx,
                                                                self.qualif_map,
-                                                               self.mir_map,
                                                                def_id);
                         self.add(qualif);
                     }
@@ -685,8 +693,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 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);
+                            }
                         }
                     }
                 }
@@ -879,17 +889,21 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
         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;
@@ -929,7 +943,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
 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) {
@@ -940,124 +953,100 @@ fn qualify_const_item_cached<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
     }
 
-    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);
+                }
+            });
         }
     }
 }