diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs index d138ffe6226..a6ffe7cea55 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc/hir/map/collector.rs @@ -11,7 +11,6 @@ use super::*; use hir::intravisit::{Visitor, NestedVisitorMap}; -use middle::cstore::InlinedItem; use std::iter::repeat; use syntax::ast::{NodeId, CRATE_NODE_ID}; use syntax_pos::Span; @@ -21,7 +20,7 @@ pub struct NodeCollector<'ast> { /// The crate pub krate: &'ast Crate, /// The node map - pub map: Vec>, + pub(super) map: Vec>, /// The parent of this node pub parent_node: NodeId, /// If true, completely ignore nested items. We set this when loading @@ -43,11 +42,11 @@ impl<'ast> NodeCollector<'ast> { collector } - pub fn extend(krate: &'ast Crate, - parent: &'ast InlinedItem, - parent_node: NodeId, - map: Vec>) - -> NodeCollector<'ast> { + pub(super) fn extend(krate: &'ast Crate, + parent: &'ast InlinedItem, + parent_node: NodeId, + map: Vec>) + -> NodeCollector<'ast> { let mut collector = NodeCollector { krate: krate, map: map, diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 330583c0d88..2f28f1f3f55 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -17,7 +17,6 @@ pub use self::definitions::{Definitions, DefKey, DefPath, DefPathData, use dep_graph::{DepGraph, DepNode}; -use middle::cstore::InlinedItem; use hir::def_id::{CRATE_DEF_INDEX, DefId, DefIndex}; use syntax::abi::Abi; @@ -26,6 +25,7 @@ use syntax::codemap::Spanned; use syntax_pos::Span; use hir::*; +use hir::intravisit::Visitor; use hir::print as pprust; use arena::TypedArena; @@ -38,6 +38,15 @@ mod collector; mod def_collector; pub mod definitions; +/// The data we save and restore about an inlined item or method. This is not +/// part of the AST that we parse from a file, but it becomes part of the tree +/// that we trans. +#[derive(Debug)] +struct InlinedItem { + def_id: DefId, + body: Body, +} + #[derive(Copy, Clone, Debug)] pub enum Node<'ast> { NodeItem(&'ast Item), @@ -60,14 +69,12 @@ pub enum Node<'ast> { NodeLifetime(&'ast Lifetime), NodeTyParam(&'ast TyParam), NodeVisibility(&'ast Visibility), - - NodeInlinedItem(&'ast InlinedItem), } /// Represents an entry and its parent NodeID. /// The odd layout is to bring down the total size. #[derive(Copy, Debug)] -pub enum MapEntry<'ast> { +enum MapEntry<'ast> { /// Placeholder for holes in the map. NotPresent, @@ -121,8 +128,6 @@ impl<'ast> MapEntry<'ast> { NodeLifetime(n) => EntryLifetime(p, n), NodeTyParam(n) => EntryTyParam(p, n), NodeVisibility(n) => EntryVisibility(p, n), - - NodeInlinedItem(n) => RootInlinedParent(n), } } @@ -171,10 +176,49 @@ impl<'ast> MapEntry<'ast> { EntryLifetime(_, n) => NodeLifetime(n), EntryTyParam(_, n) => NodeTyParam(n), EntryVisibility(_, n) => NodeVisibility(n), - RootInlinedParent(n) => NodeInlinedItem(n), _ => return None }) } + + fn is_body_owner(self, node_id: NodeId) -> bool { + match self { + EntryItem(_, item) => { + match item.node { + ItemConst(_, body) | + ItemStatic(.., body) | + ItemFn(_, _, _, _, _, body) => body.node_id == node_id, + _ => false + } + } + + EntryTraitItem(_, item) => { + match item.node { + TraitItemKind::Const(_, Some(body)) | + TraitItemKind::Method(_, TraitMethod::Provided(body)) => { + body.node_id == node_id + } + _ => false + } + } + + EntryImplItem(_, item) => { + match item.node { + ImplItemKind::Const(_, body) | + ImplItemKind::Method(_, body) => body.node_id == node_id, + _ => false + } + } + + EntryExpr(_, expr) => { + match expr.node { + ExprClosure(.., body, _) => body.node_id == node_id, + _ => false + } + } + + _ => false + } + } } /// Stores a crate and any number of inlined items from other crates. @@ -250,42 +294,19 @@ impl<'ast> Map<'ast> { if !self.is_inlined_node_id(id) { let mut last_expr = None; loop { - match map[id.as_usize()] { - EntryItem(_, item) => { - assert_eq!(id, item.id); - let def_id = self.local_def_id(id); - + let entry = map[id.as_usize()]; + match entry { + EntryItem(..) | + EntryTraitItem(..) | + EntryImplItem(..) => { if let Some(last_id) = last_expr { - // The body of the item may have a separate dep node - if self.is_item_body(last_id, item) { + // The body may have a separate dep node + if entry.is_body_owner(last_id) { + let def_id = self.local_def_id(id); return DepNode::HirBody(def_id); } } - return DepNode::Hir(def_id); - } - - EntryTraitItem(_, item) => { - let def_id = self.local_def_id(id); - - if let Some(last_id) = last_expr { - // The body of the item may have a separate dep node - if self.is_trait_item_body(last_id, item) { - return DepNode::HirBody(def_id); - } - } - return DepNode::Hir(def_id); - } - - EntryImplItem(_, item) => { - let def_id = self.local_def_id(id); - - if let Some(last_id) = last_expr { - // The body of the item may have a separate dep node - if self.is_impl_item_body(last_id, item) { - return DepNode::HirBody(def_id); - } - } - return DepNode::Hir(def_id); + return DepNode::Hir(self.local_def_id(id)); } EntryVariant(p, v) => { @@ -377,33 +398,6 @@ impl<'ast> Map<'ast> { } } - fn is_item_body(&self, node_id: NodeId, item: &Item) -> bool { - match item.node { - ItemConst(_, body) | - ItemStatic(.., body) | - ItemFn(_, _, _, _, _, body) => body.node_id == node_id, - _ => false - } - } - - fn is_trait_item_body(&self, node_id: NodeId, item: &TraitItem) -> bool { - match item.node { - TraitItemKind::Const(_, Some(body)) | - TraitItemKind::Method(_, TraitMethod::Provided(body)) => { - body.node_id == node_id - } - _ => false - } - } - - fn is_impl_item_body(&self, node_id: NodeId, item: &ImplItem) -> bool { - match item.node { - ImplItemKind::Const(_, body) | - ImplItemKind::Method(_, body) => body.node_id == node_id, - _ => false - } - } - pub fn num_local_def_ids(&self) -> usize { self.definitions.len() } @@ -483,6 +477,23 @@ impl<'ast> Map<'ast> { self.forest.krate.body(id) } + /// Returns the `NodeId` that corresponds to the definition of + /// which this is the body of, i.e. a `fn`, `const` or `static` + /// item (possibly associated), or a closure, or the body itself + /// for embedded constant expressions (e.g. `N` in `[T; N]`). + pub fn body_owner(&self, BodyId { node_id }: BodyId) -> NodeId { + let parent = self.get_parent_node(node_id); + if self.map.borrow()[parent.as_usize()].is_body_owner(node_id) { + parent + } else { + node_id + } + } + + pub fn body_owner_def_id(&self, id: BodyId) -> DefId { + self.local_def_id(self.body_owner(id)) + } + /// Get the attributes on the krate. This is preferable to /// invoking `krate.attrs` because it registers a tighter /// dep-graph access. @@ -726,9 +737,9 @@ impl<'ast> Map<'ast> { } } - pub fn expect_inlined_item(&self, id: NodeId) -> &'ast InlinedItem { + pub fn expect_inlined_body(&self, id: NodeId) -> &'ast Body { match self.find_entry(id) { - Some(RootInlinedParent(inlined_item)) => inlined_item, + Some(RootInlinedParent(inlined_item)) => &inlined_item.body, _ => bug!("expected inlined item, found {}", self.node_to_string(id)), } } @@ -969,24 +980,28 @@ pub fn map_crate<'ast>(forest: &'ast mut Forest, } } -/// Used for items loaded from external crate that are being inlined into this +/// Used for bodies loaded from external crate that are being inlined into this /// crate. -pub fn map_decoded_item<'ast>(map: &Map<'ast>, - ii: InlinedItem, - ii_parent_id: NodeId) - -> &'ast InlinedItem { +pub fn map_decoded_body<'ast>(map: &Map<'ast>, + def_id: DefId, + body: Body, + parent_id: NodeId) + -> &'ast Body { let _ignore = map.forest.dep_graph.in_ignore(); - let ii = map.forest.inlined_items.alloc(ii); + let ii = map.forest.inlined_items.alloc(InlinedItem { + def_id: def_id, + body: body + }); let mut collector = NodeCollector::extend(map.krate(), ii, - ii_parent_id, + parent_id, mem::replace(&mut *map.map.borrow_mut(), vec![])); - ii.visit(&mut collector); + collector.visit_body(&ii.body); *map.map.borrow_mut() = collector.map; - ii + &ii.body } pub trait NodePrinter { @@ -1016,8 +1031,6 @@ impl<'a> NodePrinter for pprust::State<'a> { // printing. NodeLocal(_) => bug!("cannot print isolated Local"), NodeStructCtor(_) => bug!("cannot print isolated StructCtor"), - - NodeInlinedItem(_) => bug!("cannot print inlined item"), } } } @@ -1131,9 +1144,6 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String { Some(NodeVisibility(ref vis)) => { format!("visibility {:?}{}", vis, id_str) } - Some(NodeInlinedItem(_)) => { - format!("inlined item {}", id_str) - } None => { format!("unknown node{}", id_str) } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 17cc34fcd83..ff508d2d819 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -33,6 +33,7 @@ #![cfg_attr(stage0, feature(item_like_imports))] #![feature(libc)] #![feature(nonzero)] +#![feature(pub_restricted)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] @@ -80,9 +81,8 @@ pub mod lint; pub mod middle { pub mod astconv_util; - pub mod expr_use_visitor; // STAGE0: increase glitch immunity + pub mod expr_use_visitor; pub mod const_val; - pub mod const_qualif; pub mod cstore; pub mod dataflow; pub mod dead; diff --git a/src/librustc/middle/const_qualif.rs b/src/librustc/middle/const_qualif.rs deleted file mode 100644 index ec98637922e..00000000000 --- a/src/librustc/middle/const_qualif.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Const qualification, from partial to completely promotable. -bitflags! { - #[derive(RustcEncodable, RustcDecodable)] - flags ConstQualif: u8 { - // Inner mutability (can not be placed behind a reference) or behind - // &mut in a non-global expression. Can be copied from static memory. - const MUTABLE_MEM = 1 << 0, - // Constant value with a type that implements Drop. Can be copied - // from static memory, similar to MUTABLE_MEM. - const NEEDS_DROP = 1 << 1, - // Even if the value can be placed in static memory, copying it from - // there is more expensive than in-place instantiation, and/or it may - // be too large. This applies to [T; N] and everything containing it. - // N.B.: references need to clear this flag to not end up on the stack. - const PREFER_IN_PLACE = 1 << 2, - // May use more than 0 bytes of memory, doesn't impact the constness - // directly, but is not allowed to be borrowed mutably in a constant. - const NON_ZERO_SIZED = 1 << 3, - // Actually borrowed, has to always be in static memory. Does not - // propagate, and requires the expression to behave like a 'static - // lvalue. The set of expressions with this flag is the minimum - // that have to be promoted. - const HAS_STATIC_BORROWS = 1 << 4, - // Invalid const for miscellaneous reasons (e.g. not implemented). - const NOT_CONST = 1 << 5, - - // Borrowing the expression won't produce &'static T if any of these - // bits are set, though the value could be copied from static memory - // if `NOT_CONST` isn't set. - const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits | - ConstQualif::NEEDS_DROP.bits | - ConstQualif::NOT_CONST.bits - } -} diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 7dcc5aae8e7..ffd1ad8a0fa 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -42,7 +42,6 @@ use syntax::symbol::Symbol; use syntax_pos::Span; use rustc_back::target::Target; use hir; -use hir::intravisit::Visitor; use rustc_back::PanicStrategy; pub use self::NativeLibraryKind::{NativeStatic, NativeFramework, NativeUnknown}; @@ -133,86 +132,6 @@ pub struct NativeLibrary { pub foreign_items: Vec, } -/// The data we save and restore about an inlined item or method. This is not -/// part of the AST that we parse from a file, but it becomes part of the tree -/// that we trans. -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] -pub struct InlinedItem { - pub def_id: DefId, - pub body: hir::Body, -} - -/// A borrowed version of `hir::InlinedItem`. This is what's encoded when saving -/// a crate; it then gets read as an InlinedItem. -#[derive(Clone, PartialEq, Eq, RustcEncodable, Hash, Debug)] -pub struct InlinedItemRef<'a> { - pub def_id: DefId, - pub body: &'a hir::Body, -} - -impl<'a, 'tcx> InlinedItemRef<'tcx> { - pub fn from_item(def_id: DefId, - item: &hir::Item, - tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> InlinedItemRef<'tcx> { - let body_id = match item.node { - hir::ItemFn(.., body_id) | - hir::ItemConst(_, body_id) => body_id, - _ => bug!("InlinedItemRef::from_item wrong kind") - }; - InlinedItemRef { - def_id: def_id, - body: tcx.map.body(body_id), - } - } - - pub fn from_trait_item(def_id: DefId, - item: &hir::TraitItem, - tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> InlinedItemRef<'tcx> { - let body_id = match item.node { - hir::TraitItemKind::Const(_, Some(body_id)) => body_id, - hir::TraitItemKind::Const(_, None) => { - bug!("InlinedItemRef::from_trait_item called for const without body") - }, - _ => bug!("InlinedItemRef::from_trait_item wrong kind") - }; - InlinedItemRef { - def_id: def_id, - body: tcx.map.body(body_id), - } - } - - pub fn from_impl_item(def_id: DefId, - item: &hir::ImplItem, - tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> InlinedItemRef<'tcx> { - let body_id = match item.node { - hir::ImplItemKind::Method(_, body_id) | - hir::ImplItemKind::Const(_, body_id) => body_id, - _ => bug!("InlinedItemRef::from_impl_item wrong kind") - }; - InlinedItemRef { - def_id: def_id, - body: tcx.map.body(body_id), - } - } - - pub fn visit(&self, visitor: &mut V) - where V: Visitor<'tcx> - { - visitor.visit_body(self.body); - } -} - -impl InlinedItem { - pub fn visit<'ast,V>(&'ast self, visitor: &mut V) - where V: Visitor<'ast> - { - visitor.visit_body(&self.body); - } -} - pub enum LoadedMacro { MacroRules(ast::MacroDef), ProcMacro(Rc), @@ -329,10 +248,9 @@ pub trait CrateStore<'tcx> { fn load_macro(&self, did: DefId, sess: &Session) -> LoadedMacro; // misc. metadata - fn maybe_get_item_ast<'a>(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> Option<(&'tcx InlinedItem, ast::NodeId)>; - fn local_node_for_inlined_defid(&'tcx self, def_id: DefId) -> Option; - fn defid_for_inlined_node(&'tcx self, node_id: ast::NodeId) -> Option; + fn maybe_get_item_body<'a>(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) + -> Option<&'tcx hir::Body>; + fn const_is_rvalue_promotable_to_static(&self, def: DefId) -> bool; fn get_item_mir<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> Mir<'tcx>; fn is_item_mir_available(&self, def: DefId) -> bool; @@ -499,15 +417,12 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn load_macro(&self, did: DefId, sess: &Session) -> LoadedMacro { bug!("load_macro") } // misc. metadata - fn maybe_get_item_ast<'a>(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> Option<(&'tcx InlinedItem, ast::NodeId)> { - bug!("maybe_get_item_ast") + fn maybe_get_item_body<'a>(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) + -> Option<&'tcx hir::Body> { + bug!("maybe_get_item_body") } - fn local_node_for_inlined_defid(&'tcx self, def_id: DefId) -> Option { - bug!("local_node_for_inlined_defid") - } - fn defid_for_inlined_node(&'tcx self, node_id: ast::NodeId) -> Option { - bug!("defid_for_inlined_node") + fn const_is_rvalue_promotable_to_static(&self, def: DefId) -> bool { + bug!("const_is_rvalue_promotable_to_static") } fn get_item_mir<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 9ba26a0bf1d..a3a49c91633 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -287,7 +287,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { } } - pub fn walk_fn(&mut self, body: &hir::Body) { + pub fn consume_body(&mut self, body: &hir::Body) { for arg in &body.arguments { let arg_ty = return_if_err!(self.mc.infcx.node_ty(arg.pat.id)); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index fdc5087ce9b..2d88567b8b8 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -73,7 +73,6 @@ use self::Aliasability::*; use hir::def_id::DefId; use hir::map as ast_map; use infer::InferCtxt; -use middle::const_qualif::ConstQualif; use hir::def::{Def, CtorKind}; use ty::adjustment; use ty::{self, Ty, TyCtxt}; @@ -773,23 +772,23 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { span: Span, expr_ty: Ty<'tcx>) -> cmt<'tcx> { - let qualif = self.tcx().const_qualif_map.borrow().get(&id).cloned() - .unwrap_or(ConstQualif::NOT_CONST); + let promotable = self.tcx().rvalue_promotable_to_static.borrow().get(&id).cloned() + .unwrap_or(false); // Only promote `[T; 0]` before an RFC for rvalue promotions // is accepted. - let qualif = match expr_ty.sty { - ty::TyArray(_, 0) => qualif, - _ => ConstQualif::NOT_CONST + let promotable = match expr_ty.sty { + ty::TyArray(_, 0) => true, + _ => promotable & false }; // Compute maximum lifetime of this rvalue. This is 'static if // we can promote to a constant, otherwise equal to enclosing temp // lifetime. - let re = if qualif.intersects(ConstQualif::NON_STATIC_BORROWS) { - self.temporary_scope(id) - } else { + let re = if promotable { self.tcx().mk_region(ty::ReStatic) + } else { + self.temporary_scope(id) }; let ret = self.cat_rvalue(id, span, re, expr_ty); debug!("cat_rvalue_node ret {:?}", ret); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index f24ff980355..cb1fc15c5f6 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -508,14 +508,6 @@ pub struct GlobalCtxt<'tcx> { /// FIXME(arielb1): why is this separate from populated_external_types? pub populated_external_primitive_impls: RefCell, - /// Cache used by const_eval when decoding external constants. - /// Contains `None` when the constant has been fetched but doesn't exist. - /// Constains `Some(expr_id, type)` otherwise. - /// `type` is `None` in case it's not a primitive type - pub extern_const_statics: RefCell>)>>>, - /// Cache used by const_eval when decoding extern const fns - pub extern_const_fns: RefCell>, - /// Maps any item's def-id to its stability index. pub stability: RefCell>, @@ -537,8 +529,8 @@ pub struct GlobalCtxt<'tcx> { /// Caches the representation hints for struct definitions. repr_hint_cache: RefCell>>, - /// Maps Expr NodeId's to their constant qualification. - pub const_qualif_map: RefCell>, + /// Maps Expr NodeId's to `true` iff `&expr` can have 'static lifetime. + pub rvalue_promotable_to_static: RefCell>, /// Caches CoerceUnsized kinds for impls on custom types. pub custom_coerce_unsized_kinds: RefCell>, @@ -787,13 +779,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { used_trait_imports: RefCell::new(NodeSet()), populated_external_types: RefCell::new(DefIdSet()), populated_external_primitive_impls: RefCell::new(DefIdSet()), - extern_const_statics: RefCell::new(DefIdMap()), - extern_const_fns: RefCell::new(DefIdMap()), stability: RefCell::new(stability), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), repr_hint_cache: RefCell::new(DepTrackingMap::new(dep_graph.clone())), - const_qualif_map: RefCell::new(NodeMap()), + rvalue_promotable_to_static: RefCell::new(NodeMap()), custom_coerce_unsized_kinds: RefCell::new(DefIdMap()), cast_kinds: RefCell::new(NodeMap()), fragment_infos: RefCell::new(DefIdMap()), diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index f7249784dba..dc2214dd34e 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -201,7 +201,7 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, all_loans: all_loans, param_env: &infcx.parameter_environment }; - euv::ExprUseVisitor::new(&mut clcx, &infcx).walk_fn(body); + euv::ExprUseVisitor::new(&mut clcx, &infcx).consume_body(body); } #[derive(PartialEq)] diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index c9e526202fa..34f1ad57c62 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -54,7 +54,7 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); let infcx = bccx.tcx.borrowck_fake_infer_ctxt(param_env); - euv::ExprUseVisitor::new(&mut glcx, &infcx).walk_fn(body); + euv::ExprUseVisitor::new(&mut glcx, &infcx).consume_body(body); glcx.report_potential_errors(); let GatherLoanCtxt { all_loans, move_data, .. } = glcx; diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index e27a0476635..f65a3b46e5a 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -17,7 +17,6 @@ use self::EvalHint::*; use rustc::hir::map as ast_map; use rustc::hir::map::blocks::FnLikeNode; -use rustc::middle::cstore::InlinedItem; use rustc::traits; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; @@ -139,21 +138,10 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, Some(_) => None } } else { - match tcx.extern_const_statics.borrow().get(&def_id) { - Some(&None) => return None, - Some(&Some((expr_id, ty))) => { - return Some((tcx.map.expect_expr(expr_id), ty)); - } - None => {} - } - let mut used_substs = false; - let expr_ty = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) { - Some((&InlinedItem { ref body, .. }, _)) => { - Some((&body.value, Some(tcx.sess.cstore.item_type(tcx, def_id)))) - } - _ => None - }; - let expr_ty = match tcx.sess.cstore.describe_def(def_id) { + let expr_ty = tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| { + (&body.value, Some(tcx.sess.cstore.item_type(tcx, def_id))) + }); + match tcx.sess.cstore.describe_def(def_id) { Some(Def::AssociatedConst(_)) => { let trait_id = tcx.sess.cstore.trait_of_item(def_id); // As mentioned in the comments above for in-crate @@ -161,8 +149,6 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // trait-associated const if the caller gives us the // substitutions for the reference to it. if let Some(trait_id) = trait_id { - used_substs = true; - if let Some(substs) = substs { resolve_trait_associated_const(tcx, def_id, expr_ty, trait_id, substs) } else { @@ -174,70 +160,27 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }, Some(Def::Const(..)) => expr_ty, _ => None - }; - // If we used the substitutions, particularly to choose an impl - // of a trait-associated const, don't cache that, because the next - // lookup with the same def_id may yield a different result. - if !used_substs { - tcx.extern_const_statics - .borrow_mut() - .insert(def_id, expr_ty.map(|(e, t)| (e.id, t))); } - expr_ty } } -fn inline_const_fn_from_external_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> Option { - match tcx.extern_const_fns.borrow().get(&def_id) { - Some(&ast::DUMMY_NODE_ID) => return None, - Some(&fn_id) => return Some(fn_id), - None => {} - } - - if !tcx.sess.cstore.is_const_fn(def_id) { - tcx.extern_const_fns.borrow_mut().insert(def_id, ast::DUMMY_NODE_ID); - return None; - } - - let fn_id = tcx.sess.cstore.maybe_get_item_ast(tcx, def_id).map(|t| t.1); - tcx.extern_const_fns.borrow_mut().insert(def_id, - fn_id.unwrap_or(ast::DUMMY_NODE_ID)); - fn_id -} - -pub enum ConstFnNode<'tcx> { - Local(FnLikeNode<'tcx>), - Inlined(&'tcx InlinedItem) -} - -pub fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) - -> Option> +fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) + -> Option<&'tcx hir::Body> { - let fn_id = if let Some(node_id) = tcx.map.as_local_node_id(def_id) { - node_id - } else { - if let Some(fn_id) = inline_const_fn_from_external_crate(tcx, def_id) { - if let ast_map::NodeInlinedItem(ii) = tcx.map.get(fn_id) { - return Some(ConstFnNode::Inlined(ii)); + if let Some(node_id) = tcx.map.as_local_node_id(def_id) { + FnLikeNode::from_node(tcx.map.get(node_id)).and_then(|fn_like| { + if fn_like.constness() == hir::Constness::Const { + Some(tcx.map.body(fn_like.body())) } else { - bug!("Got const fn from external crate, but it's not inlined") + None } - } else { - return None; - } - }; - - let fn_like = match FnLikeNode::from_node(tcx.map.get(fn_id)) { - Some(fn_like) => fn_like, - None => return None - }; - - if fn_like.constness() == hir::Constness::Const { - Some(ConstFnNode::Local(fn_like)) + }) } else { - None + if tcx.sess.cstore.is_const_fn(def_id) { + tcx.sess.cstore.maybe_get_item_body(tcx, def_id) + } else { + None + } } } @@ -871,8 +814,7 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, callee => signal!(e, CallOn(callee)), }; let body = match lookup_const_fn_by_id(tcx, did) { - Some(ConstFnNode::Inlined(ii)) => &ii.body, - Some(ConstFnNode::Local(fn_like)) => tcx.map.body(fn_like.body()), + Some(body) => body, None => signal!(e, NonConstPath), }; diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index 926c44824ce..54824fc51d6 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -16,8 +16,7 @@ use cstore::CrateMetadata; use encoder::EncodeContext; use schema::*; -use rustc::middle::cstore::{InlinedItem, InlinedItemRef}; -use rustc::middle::const_qualif::ConstQualif; +use rustc::hir; use rustc::hir::def::Def; use rustc::hir::def_id::DefId; use rustc::ty::{self, TyCtxt, Ty}; @@ -29,8 +28,9 @@ use rustc_serialize::Encodable; #[derive(RustcEncodable, RustcDecodable)] pub struct Ast<'tcx> { id_range: IdRange, - item: Lazy, + body: Lazy, side_tables: LazySeq<(ast::NodeId, TableEntry<'tcx>)>, + pub rvalue_promotable_to_static: bool, } #[derive(RustcEncodable, RustcDecodable)] @@ -39,16 +39,17 @@ enum TableEntry<'tcx> { NodeType(Ty<'tcx>), ItemSubsts(ty::ItemSubsts<'tcx>), Adjustment(ty::adjustment::Adjustment<'tcx>), - ConstQualif(ConstQualif), } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { - pub fn encode_inlined_item(&mut self, ii: InlinedItemRef<'tcx>) -> Lazy> { - let mut id_visitor = IdRangeComputingVisitor::new(&self.tcx.map); - ii.visit(&mut id_visitor); + pub fn encode_body(&mut self, body: hir::BodyId) -> Lazy> { + let body = self.tcx.map.body(body); - let ii_pos = self.position(); - ii.encode(self).unwrap(); + let mut id_visitor = IdRangeComputingVisitor::new(&self.tcx.map); + id_visitor.visit_body(body); + + let body_pos = self.position(); + body.encode(self).unwrap(); let tables_pos = self.position(); let tables_count = { @@ -56,14 +57,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ecx: self, count: 0, }; - ii.visit(&mut visitor); + visitor.visit_body(body); visitor.count }; + let rvalue_promotable_to_static = + self.tcx.rvalue_promotable_to_static.borrow()[&body.value.id]; + self.lazy(&Ast { id_range: id_visitor.result(), - item: Lazy::with_position(ii_pos), + body: Lazy::with_position(body_pos), side_tables: LazySeq::with_position_and_length(tables_pos, tables_count), + rvalue_promotable_to_static: rvalue_promotable_to_static }) } } @@ -94,18 +99,17 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for SideTableEncodingIdVisitor<'a, 'b, 'tcx> { encode(tcx.tables().node_types.get(&id).cloned().map(TableEntry::NodeType)); encode(tcx.tables().item_substs.get(&id).cloned().map(TableEntry::ItemSubsts)); encode(tcx.tables().adjustments.get(&id).cloned().map(TableEntry::Adjustment)); - encode(tcx.const_qualif_map.borrow().get(&id).cloned().map(TableEntry::ConstQualif)); } } -/// Decodes an item from its AST in the cdata's metadata and adds it to the +/// Decodes an item's body from its AST in the cdata's metadata and adds it to the /// ast-map. -pub fn decode_inlined_item<'a, 'tcx>(cdata: &CrateMetadata, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - ast: Ast<'tcx>, - orig_did: DefId) - -> &'tcx InlinedItem { - debug!("> Decoding inlined fn: {:?}", tcx.item_path_str(orig_did)); +pub fn decode_body<'a, 'tcx>(cdata: &CrateMetadata, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + ast: Ast<'tcx>) + -> &'tcx hir::Body { + debug!("> Decoding inlined fn: {}", tcx.item_path_str(def_id)); let cnt = ast.id_range.max.as_usize() - ast.id_range.min.as_usize(); let start = tcx.sess.reserve_node_ids(cnt); @@ -115,12 +119,6 @@ pub fn decode_inlined_item<'a, 'tcx>(cdata: &CrateMetadata, max: ast::NodeId::new(start.as_usize() + cnt), }]; - let ii = ast.item.decode((cdata, tcx, id_ranges)); - let item_node_id = tcx.sess.next_node_id(); - let ii = ast_map::map_decoded_item(&tcx.map, - ii, - item_node_id); - for (id, entry) in ast.side_tables.decode((cdata, tcx, id_ranges)) { match entry { TableEntry::TypeRelativeDef(def) => { @@ -135,11 +133,9 @@ pub fn decode_inlined_item<'a, 'tcx>(cdata: &CrateMetadata, TableEntry::Adjustment(adj) => { tcx.tables.borrow_mut().adjustments.insert(id, adj); } - TableEntry::ConstQualif(qualif) => { - tcx.const_qualif_map.borrow_mut().insert(id, qualif); - } } } - ii + let body = ast.body.decode((cdata, tcx, id_ranges)); + ast_map::map_decoded_body(&tcx.map, def_id, body, tcx.sess.next_node_id()) } diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 7ec847d24cf..aab4034b770 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -88,13 +88,6 @@ pub struct CrateMetadata { pub dllimport_foreign_items: FxHashSet, } -pub struct CachedInlinedItem { - /// The NodeId of the RootInlinedParent HIR map entry - pub inlined_root: ast::NodeId, - /// The local NodeId of the inlined entity - pub item_id: ast::NodeId, -} - pub struct CStore { pub dep_graph: DepGraph, metas: RefCell>>, @@ -104,8 +97,7 @@ pub struct CStore { used_link_args: RefCell>, statically_included_foreign_items: RefCell>, pub dllimport_foreign_items: RefCell>, - pub inlined_item_cache: RefCell>>, - pub defid_for_inlined_node: RefCell>, + pub inlined_item_cache: RefCell>>, pub visible_parent_map: RefCell>, } @@ -121,7 +113,6 @@ impl CStore { dllimport_foreign_items: RefCell::new(FxHashSet()), visible_parent_map: RefCell::new(FxHashMap()), inlined_item_cache: RefCell::new(FxHashMap()), - defid_for_inlined_node: RefCell::new(FxHashMap()), } } diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 6a626e9942c..a5173e00c72 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -13,7 +13,7 @@ use encoder; use locator; use schema; -use rustc::middle::cstore::{InlinedItem, CrateStore, CrateSource, LibSource, DepKind, ExternCrate}; +use rustc::middle::cstore::{CrateStore, CrateSource, LibSource, DepKind, ExternCrate}; use rustc::middle::cstore::{NativeLibrary, LinkMeta, LinkagePreference, LoadedMacro}; use rustc::hir::def::{self, Def}; use rustc::middle::lang_items; @@ -427,94 +427,37 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { }) } - fn maybe_get_item_ast<'a>(&'tcx self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> Option<(&'tcx InlinedItem, ast::NodeId)> + fn maybe_get_item_body<'a>(&'tcx self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> Option<&'tcx hir::Body> { self.dep_graph.read(DepNode::MetaData(def_id)); - match self.inlined_item_cache.borrow().get(&def_id) { - Some(&None) => { - return None; // Not inlinable - } - Some(&Some(ref cached_inlined_item)) => { + if let Some(&cached) = self.inlined_item_cache.borrow().get(&def_id) { + return cached.map(|root_id| { // Already inline - debug!("maybe_get_item_ast({}): already inline as node id {}", - tcx.item_path_str(def_id), cached_inlined_item.item_id); - return Some((tcx.map.expect_inlined_item(cached_inlined_item.inlined_root), - cached_inlined_item.item_id)); - } - None => { - // Not seen yet - } + debug!("maybe_get_item_body({}): already inline", tcx.item_path_str(def_id)); + tcx.map.expect_inlined_body(root_id) + }); } - debug!("maybe_get_item_ast({}): inlining item", tcx.item_path_str(def_id)); + debug!("maybe_get_item_body({}): inlining item", tcx.item_path_str(def_id)); - let inlined = self.get_crate_data(def_id.krate).maybe_get_item_ast(tcx, def_id.index); + let inlined = self.get_crate_data(def_id.krate).maybe_get_item_body(tcx, def_id.index); - let cache_inlined_item = |original_def_id, inlined_item_id, inlined_root_node_id| { - let cache_entry = cstore::CachedInlinedItem { - inlined_root: inlined_root_node_id, - item_id: inlined_item_id, - }; - self.inlined_item_cache - .borrow_mut() - .insert(original_def_id, Some(cache_entry)); - self.defid_for_inlined_node - .borrow_mut() - .insert(inlined_item_id, original_def_id); - }; + self.inlined_item_cache.borrow_mut().insert(def_id, inlined.map(|body| { + let root_id = tcx.map.get_parent_node(body.value.id); + assert_eq!(tcx.map.get_parent_node(root_id), root_id); + root_id + })); - let find_inlined_item_root = |inlined_item_id| { - let mut node = inlined_item_id; - - // If we can't find the inline root after a thousand hops, we can - // be pretty sure there's something wrong with the HIR map. - for _ in 0 .. 1000 { - let parent_node = tcx.map.get_parent_node(node); - if parent_node == node { - return node; - } - node = parent_node; - } - bug!("cycle in HIR map parent chain") - }; - - match inlined { - None => { - self.inlined_item_cache - .borrow_mut() - .insert(def_id, None); - } - Some(&InlinedItem { ref body, .. }) => { - let inlined_root_node_id = find_inlined_item_root(body.value.id); - cache_inlined_item(def_id, inlined_root_node_id, inlined_root_node_id); - } - } - - // We can be sure to hit the cache now - return self.maybe_get_item_ast(tcx, def_id); + inlined } - fn local_node_for_inlined_defid(&'tcx self, def_id: DefId) -> Option { - assert!(!def_id.is_local()); - match self.inlined_item_cache.borrow().get(&def_id) { - Some(&Some(ref cached_inlined_item)) => { - Some(cached_inlined_item.item_id) - } - Some(&None) => { - None - } - _ => { - bug!("Trying to lookup inlined NodeId for unexpected item"); - } - } - } - - fn defid_for_inlined_node(&'tcx self, node_id: ast::NodeId) -> Option { - self.defid_for_inlined_node.borrow().get(&node_id).map(|x| *x) + fn const_is_rvalue_promotable_to_static(&self, def: DefId) -> bool { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).const_is_rvalue_promotable_to_static(def.index) } fn get_item_mir<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> Mir<'tcx> { diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 400a3ac0e3e..81f63825919 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -10,7 +10,7 @@ // Decoding metadata from a single crate's metadata -use astencode::decode_inlined_item; +use astencode::decode_body; use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary}; use schema::*; @@ -18,7 +18,7 @@ use rustc::hir::map::{DefKey, DefPath, DefPathData}; use rustc::hir; use rustc::hir::intravisit::IdRange; -use rustc::middle::cstore::{InlinedItem, LinkagePreference}; +use rustc::middle::cstore::LinkagePreference; use rustc::hir::def::{self, Def, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc::middle::lang_items; @@ -819,20 +819,21 @@ impl<'a, 'tcx> CrateMetadata { } } - pub fn maybe_get_item_ast(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - id: DefIndex) - -> Option<&'tcx InlinedItem> { - debug!("Looking up item: {:?}", id); + pub fn maybe_get_item_body(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + id: DefIndex) + -> Option<&'tcx hir::Body> { if self.is_proc_macro(id) { return None; } - let item_doc = self.entry(id); - let item_did = self.local_def_id(id); - item_doc.ast.map(|ast| { - let ast = ast.decode(self); - decode_inlined_item(self, tcx, ast, item_did) + self.entry(id).ast.map(|ast| { + decode_body(self, tcx, self.local_def_id(id), ast.decode(self)) }) } + pub fn const_is_rvalue_promotable_to_static(&self, id: DefIndex) -> bool { + self.entry(id).ast.expect("const item missing `ast`") + .decode(self).rvalue_promotable_to_static + } + pub fn is_item_mir_available(&self, id: DefIndex) -> bool { !self.is_proc_macro(id) && self.maybe_entry(id).and_then(|item| item.decode(self).mir).is_some() diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 732887620cf..72dcb4ba9a3 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -12,8 +12,7 @@ use cstore; use index::Index; use schema::*; -use rustc::middle::cstore::{InlinedItemRef, LinkMeta}; -use rustc::middle::cstore::{LinkagePreference, NativeLibrary}; +use rustc::middle::cstore::{LinkMeta, LinkagePreference, NativeLibrary}; use rustc::hir::def; use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId}; use rustc::hir::map::definitions::DefPathTable; @@ -495,13 +494,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { generics: Some(self.encode_generics(def_id)), predicates: Some(self.encode_predicates(def_id)), - ast: if let hir::TraitItemKind::Const(_, Some(_)) = ast_item.node { - // We only save the HIR for associated consts with bodies - // (InlinedItemRef::from_trait_item panics otherwise) - let trait_def_id = trait_item.container.id(); - Some(self.encode_inlined_item( - InlinedItemRef::from_trait_item(trait_def_id, ast_item, tcx) - )) + ast: if let hir::TraitItemKind::Const(_, Some(body)) = ast_item.node { + Some(self.encode_body(body)) } else { None }, @@ -510,12 +504,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } fn encode_info_for_impl_item(&mut self, def_id: DefId) -> Entry<'tcx> { - let tcx = self.tcx; - let node_id = self.tcx.map.as_local_node_id(def_id).unwrap(); let ast_item = self.tcx.map.expect_impl_item(node_id); let impl_item = self.tcx.associated_item(def_id); - let impl_def_id = impl_item.container.id(); let container = match impl_item.defaultness { hir::Defaultness::Default { has_value: true } => AssociatedContainer::ImplDefault, @@ -544,17 +535,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ty::AssociatedKind::Type => EntryKind::AssociatedType(container) }; - let (ast, mir) = if impl_item.kind == ty::AssociatedKind::Const { - (true, true) - } else if let hir::ImplItemKind::Method(ref sig, _) = ast_item.node { + let (ast, mir) = if let hir::ImplItemKind::Const(_, body) = ast_item.node { + (Some(body), true) + } else if let hir::ImplItemKind::Method(ref sig, body) = ast_item.node { let generics = self.tcx.item_generics(def_id); let types = generics.parent_types as usize + generics.types.len(); let needs_inline = types > 0 || attr::requests_inline(&ast_item.attrs); let is_const_fn = sig.constness == hir::Constness::Const; + let ast = if is_const_fn { Some(body) } else { None }; let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; - (is_const_fn, needs_inline || is_const_fn || always_encode_mir) + (ast, needs_inline || is_const_fn || always_encode_mir) } else { - (false, false) + (None, false) }; Entry { @@ -572,13 +564,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { generics: Some(self.encode_generics(def_id)), predicates: Some(self.encode_predicates(def_id)), - ast: if ast { - Some(self.encode_inlined_item( - InlinedItemRef::from_impl_item(impl_def_id, ast_item, tcx) - )) - } else { - None - }, + ast: ast.map(|body| self.encode_body(body)), mir: if mir { self.encode_mir(def_id) } else { None }, } } @@ -809,11 +795,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }, ast: match item.node { - hir::ItemConst(..) | - hir::ItemFn(_, _, hir::Constness::Const, ..) => { - Some(self.encode_inlined_item( - InlinedItemRef::from_item(def_id, item, tcx) - )) + hir::ItemConst(_, body) | + hir::ItemFn(_, _, hir::Constness::Const, _, _, body) => { + Some(self.encode_body(body)) } _ => None, }, diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 11f39a8fdf8..d1d9a201567 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -26,8 +26,8 @@ use rustc::dep_graph::DepNode; use rustc::ty::cast::CastKind; -use rustc_const_eval::{ConstEvalErr, lookup_const_fn_by_id, compare_lit_exprs}; -use rustc_const_eval::{ConstFnNode, eval_const_expr_partial, lookup_const_by_id}; +use rustc_const_eval::{ConstEvalErr, compare_lit_exprs}; +use rustc_const_eval::{eval_const_expr_partial}; use rustc_const_eval::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll, Math}; use rustc_const_eval::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath}; use rustc_const_eval::ErrKind::UnresolvedPath; @@ -35,85 +35,36 @@ use rustc_const_eval::EvalHint::ExprTypeChecked; use rustc_const_math::{ConstMathErr, Op}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; +use rustc::hir::map::blocks::FnLikeNode; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; +use rustc::mir::transform::MirSource; use rustc::ty::{self, Ty, TyCtxt}; use rustc::traits::Reveal; use rustc::util::common::ErrorReported; -use rustc::util::nodemap::NodeMap; -use rustc::middle::const_qualif::ConstQualif; +use rustc::util::nodemap::NodeSet; use rustc::lint::builtin::CONST_ERR; use rustc::hir::{self, PatKind}; use syntax::ast; use syntax_pos::Span; -use rustc::hir::intravisit::{self, FnKind, Visitor, NestedVisitorMap}; +use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use std::collections::hash_map::Entry; use std::cmp::Ordering; - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum Mode { - Const, - ConstFn, - Static, - StaticMut, - - // An expression that occurs outside of any constant context - // (i.e. `const`, `static`, array lengths, etc.). The value - // can be variable at runtime, but will be promotable to - // static memory if we can prove it is actually constant. - Var, -} +use std::mem; struct CheckCrateVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, - mode: Mode, - qualif: ConstQualif, - rvalue_borrows: NodeMap, + in_fn: bool, + promotable: bool, + mut_rvalue_borrows: NodeSet, + param_env: ty::ParameterEnvironment<'tcx>, } impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { - fn with_mode(&mut self, mode: Mode, f: F) -> R - where F: FnOnce(&mut CheckCrateVisitor<'a, 'gcx>) -> R - { - let (old_mode, old_qualif) = (self.mode, self.qualif); - self.mode = mode; - self.qualif = ConstQualif::empty(); - let r = f(self); - self.mode = old_mode; - self.qualif = old_qualif; - r - } - - fn with_euv(&mut self, item_id: Option, f: F) -> R - where F: for<'b, 'tcx> FnOnce(&mut euv::ExprUseVisitor<'b, 'gcx, 'tcx>) -> R - { - let param_env = match item_id { - Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id), - None => self.tcx.empty_parameter_environment(), - }; - - self.tcx - .infer_ctxt(None, Some(param_env), Reveal::NotSpecializable) - .enter(|infcx| f(&mut euv::ExprUseVisitor::new(self, &infcx))) - } - - fn global_body(&mut self, mode: Mode, body: hir::BodyId) -> ConstQualif { - let expr = &self.tcx.map.body(body).value; - self.global_expr(mode, expr) - } - - fn global_expr(&mut self, mode: Mode, expr: &'gcx hir::Expr) -> ConstQualif { - assert!(mode != Mode::Var); - match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) { - Entry::Occupied(entry) => return *entry.get(), - Entry::Vacant(entry) => { - // Prevent infinite recursion on re-entry. - entry.insert(ConstQualif::empty()); - } - } + fn check_const_eval(&self, expr: &'gcx hir::Expr) { if let Err(err) = eval_const_expr_partial(self.tcx, expr, ExprTypeChecked, None) { match err.kind { UnimplementedConstVal(_) => {} @@ -129,184 +80,78 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { } } } - self.with_mode(mode, |this| { - this.with_euv(None, |euv| euv.consume_expr(expr)); - this.visit_expr(expr); - this.qualif - }) } - fn fn_like(&mut self, - fk: FnKind<'gcx>, - fd: &'gcx hir::FnDecl, - b: hir::BodyId, - s: Span, - fn_id: ast::NodeId) - -> ConstQualif { - match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) { - Entry::Occupied(entry) => return *entry.get(), - Entry::Vacant(entry) => { - // Prevent infinite recursion on re-entry. - entry.insert(ConstQualif::empty()); - } + // Adds the worst effect out of all the values of one type. + fn add_type(&mut self, ty: Ty<'gcx>) { + if ty.type_contents(self.tcx).interior_unsafe() { + self.promotable = false; } - let mode = match fk { - FnKind::ItemFn(_, _, _, hir::Constness::Const, ..) - => Mode::ConstFn, - FnKind::Method(_, m, ..) => { - if m.constness == hir::Constness::Const { - Mode::ConstFn - } else { - Mode::Var - } - } - _ => Mode::Var, + if self.tcx.type_needs_drop_given_env(ty, &self.param_env) { + self.promotable = false; + } + } + + fn handle_const_fn_call(&mut self, def_id: DefId, ret_ty: Ty<'gcx>) { + self.add_type(ret_ty); + + self.promotable &= if let Some(fn_id) = self.tcx.map.as_local_node_id(def_id) { + FnLikeNode::from_node(self.tcx.map.get(fn_id)).map_or(false, |fn_like| { + fn_like.constness() == hir::Constness::Const + }) + } else { + self.tcx.sess.cstore.is_const_fn(def_id) }; - - let qualif = self.with_mode(mode, |this| { - let body = this.tcx.map.body(b); - this.with_euv(Some(fn_id), |euv| euv.walk_fn(body)); - intravisit::walk_fn(this, fk, fd, b, s, fn_id); - this.qualif - }); - - // Keep only bits that aren't affected by function body (NON_ZERO_SIZED), - // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE). - let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE); - - self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif); - qualif - } - - fn add_qualif(&mut self, qualif: ConstQualif) { - self.qualif = self.qualif | qualif; - } - - /// Returns true if the call is to a const fn or method. - fn handle_const_fn_call(&mut self, _expr: &hir::Expr, def_id: DefId, ret_ty: Ty<'gcx>) -> bool { - match lookup_const_fn_by_id(self.tcx, def_id) { - Some(ConstFnNode::Local(fn_like)) => { - let qualif = self.fn_like(fn_like.kind(), - fn_like.decl(), - fn_like.body(), - fn_like.span(), - fn_like.id()); - - self.add_qualif(qualif); - - if ret_ty.type_contents(self.tcx).interior_unsafe() { - self.add_qualif(ConstQualif::MUTABLE_MEM); - } - - true - }, - Some(ConstFnNode::Inlined(ii)) => { - let node_id = ii.body.value.id; - - let qualif = match self.tcx.const_qualif_map.borrow_mut().entry(node_id) { - Entry::Occupied(entry) => *entry.get(), - _ => bug!("const qualif entry missing for inlined item") - }; - - self.add_qualif(qualif); - - if ret_ty.type_contents(self.tcx).interior_unsafe() { - self.add_qualif(ConstQualif::MUTABLE_MEM); - } - - true - }, - None => false - } - } - - fn record_borrow(&mut self, id: ast::NodeId, mutbl: hir::Mutability) { - match self.rvalue_borrows.entry(id) { - Entry::Occupied(mut entry) => { - // Merge the two borrows, taking the most demanding - // one, mutability-wise. - if mutbl == hir::MutMutable { - entry.insert(mutbl); - } - } - Entry::Vacant(entry) => { - entry.insert(mutbl); - } - } } } impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.tcx.map) + NestedVisitorMap::None } - fn visit_item(&mut self, i: &'tcx hir::Item) { - debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id)); - assert_eq!(self.mode, Mode::Var); - match i.node { - hir::ItemStatic(_, hir::MutImmutable, expr) => { - self.global_body(Mode::Static, expr); - } - hir::ItemStatic(_, hir::MutMutable, expr) => { - self.global_body(Mode::StaticMut, expr); - } - hir::ItemConst(_, expr) => { - self.global_body(Mode::Const, expr); - } - hir::ItemEnum(ref enum_definition, _) => { - for var in &enum_definition.variants { - if let Some(ex) = var.node.disr_expr { - self.global_body(Mode::Const, ex); - } - } - } - _ => { - intravisit::walk_item(self, i); + fn visit_nested_body(&mut self, body: hir::BodyId) { + match self.tcx.rvalue_promotable_to_static.borrow_mut().entry(body.node_id) { + Entry::Occupied(_) => return, + Entry::Vacant(entry) => { + // Prevent infinite recursion on re-entry. + entry.insert(false); } } - } - fn visit_trait_item(&mut self, t: &'tcx hir::TraitItem) { - match t.node { - hir::TraitItemKind::Const(_, default) => { - if let Some(expr) = default { - self.global_body(Mode::Const, expr); - } else { - intravisit::walk_trait_item(self, t); - } - } - _ => self.with_mode(Mode::Var, |v| intravisit::walk_trait_item(v, t)), + let item_id = self.tcx.map.body_owner(body); + + let outer_in_fn = self.in_fn; + self.in_fn = match MirSource::from_node(self.tcx, item_id) { + MirSource::Fn(_) => true, + _ => false + }; + + let body = self.tcx.map.body(body); + if !self.in_fn { + self.check_const_eval(&body.value); } - } - fn visit_impl_item(&mut self, i: &'tcx hir::ImplItem) { - match i.node { - hir::ImplItemKind::Const(_, expr) => { - self.global_body(Mode::Const, expr); - } - _ => self.with_mode(Mode::Var, |v| intravisit::walk_impl_item(v, i)), - } - } + let param_env = ty::ParameterEnvironment::for_item(self.tcx, item_id); + let outer_param_env = mem::replace(&mut self.param_env, param_env); + self.tcx.infer_ctxt(None, Some(self.param_env.clone()), Reveal::NotSpecializable) + .enter(|infcx| euv::ExprUseVisitor::new(self, &infcx).consume_body(body)); - fn visit_fn(&mut self, - fk: FnKind<'tcx>, - fd: &'tcx hir::FnDecl, - b: hir::BodyId, - s: Span, - fn_id: ast::NodeId) { - self.fn_like(fk, fd, b, s, fn_id); + self.visit_body(body); + + self.param_env = outer_param_env; + self.in_fn = outer_in_fn; } fn visit_pat(&mut self, p: &'tcx hir::Pat) { match p.node { PatKind::Lit(ref lit) => { - self.global_expr(Mode::Const, &lit); + self.check_const_eval(lit); } PatKind::Range(ref start, ref end) => { - self.global_expr(Mode::Const, &start); - self.global_expr(Mode::Const, &end); + self.check_const_eval(start); + self.check_const_eval(end); match compare_lit_exprs(self.tcx, p.span, start, end) { Ok(Ordering::Less) | @@ -320,119 +165,60 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { Err(ErrorReported) => {} } } - _ => intravisit::walk_pat(self, p), + _ => {} } + intravisit::walk_pat(self, p); } - fn visit_block(&mut self, block: &'tcx hir::Block) { - // Check all statements in the block - for stmt in &block.stmts { - match stmt.node { - hir::StmtDecl(ref decl, _) => { - match decl.node { - hir::DeclLocal(_) => {} - // Item statements are allowed - hir::DeclItem(_) => continue, + fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) { + match stmt.node { + hir::StmtDecl(ref decl, _) => { + match decl.node { + hir::DeclLocal(_) => { + self.promotable = false; } + // Item statements are allowed + hir::DeclItem(_) => {} } - hir::StmtExpr(..) => {} - hir::StmtSemi(..) => {} } - self.add_qualif(ConstQualif::NOT_CONST); + hir::StmtExpr(..) | + hir::StmtSemi(..) => { + self.promotable = false; + } } - intravisit::walk_block(self, block); + intravisit::walk_stmt(self, stmt); } fn visit_expr(&mut self, ex: &'tcx hir::Expr) { - let mut outer = self.qualif; - self.qualif = ConstQualif::empty(); + let outer = self.promotable; + self.promotable = true; let node_ty = self.tcx.tables().node_id_to_type(ex.id); check_expr(self, ex, node_ty); check_adjustments(self, ex); - // Special-case some expressions to avoid certain flags bubbling up. - match ex.node { - hir::ExprCall(ref callee, ref args) => { - for arg in args { - self.visit_expr(&arg) - } - - let inner = self.qualif; - self.visit_expr(&callee); - // The callee's size doesn't count in the call. - let added = self.qualif - inner; - self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED); - } - hir::ExprRepeat(ref element, _) => { - self.visit_expr(&element); - // The count is checked elsewhere (typeck). - let count = match node_ty.sty { - ty::TyArray(_, n) => n, - _ => bug!(), - }; - // [element; 0] is always zero-sized. - if count == 0 { - self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE); + if let hir::ExprMatch(ref discr, ref arms, _) = ex.node { + // Compute the most demanding borrow from all the arms' + // patterns and set that on the discriminator. + let mut mut_borrow = false; + for pat in arms.iter().flat_map(|arm| &arm.pats) { + if self.mut_rvalue_borrows.remove(&pat.id) { + mut_borrow = true; } } - hir::ExprMatch(ref discr, ref arms, _) => { - // Compute the most demanding borrow from all the arms' - // patterns and set that on the discriminator. - let mut borrow = None; - for pat in arms.iter().flat_map(|arm| &arm.pats) { - let pat_borrow = self.rvalue_borrows.remove(&pat.id); - match (borrow, pat_borrow) { - (None, _) | - (_, Some(hir::MutMutable)) => { - borrow = pat_borrow; - } - _ => {} - } - } - if let Some(mutbl) = borrow { - self.record_borrow(discr.id, mutbl); - } - intravisit::walk_expr(self, ex); + if mut_borrow { + self.mut_rvalue_borrows.insert(discr.id); } - _ => intravisit::walk_expr(self, ex), } + intravisit::walk_expr(self, ex); + // Handle borrows on (or inside the autorefs of) this expression. - match self.rvalue_borrows.remove(&ex.id) { - Some(hir::MutImmutable) => { - // Constants cannot be borrowed if they contain interior mutability as - // it means that our "silent insertion of statics" could change - // initializer values (very bad). - // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has - // propagated from another error, so erroring again would be just noise. - let tc = node_ty.type_contents(self.tcx); - if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() { - outer = outer | ConstQualif::NOT_CONST; - } - // If the reference has to be 'static, avoid in-place initialization - // as that will end up pointing to the stack instead. - if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) { - self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE; - self.add_qualif(ConstQualif::HAS_STATIC_BORROWS); - } - } - Some(hir::MutMutable) => { - // `&mut expr` means expr could be mutated, unless it's zero-sized. - if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) { - if self.mode == Mode::Var { - outer = outer | ConstQualif::NOT_CONST; - self.add_qualif(ConstQualif::MUTABLE_MEM); - } - } - if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) { - self.add_qualif(ConstQualif::HAS_STATIC_BORROWS); - } - } - None => {} + if self.mut_rvalue_borrows.remove(&ex.id) { + self.promotable = false; } - if self.mode == Mode::Var && !self.qualif.intersects(ConstQualif::NOT_CONST) { + if self.in_fn && self.promotable { match eval_const_expr_partial(self.tcx, ex, ExprTypeChecked, None) { Ok(_) => {} Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) | @@ -453,9 +239,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { } } - self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif); - // Don't propagate certain flags. - self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS); + self.tcx.rvalue_promotable_to_static.borrow_mut().insert(ex.id, self.promotable); + self.promotable &= outer; } } @@ -468,7 +253,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node_ty: Ty<'tcx>) { match node_ty.sty { ty::TyAdt(def, _) if def.has_dtor() => { - v.add_qualif(ConstQualif::NEEDS_DROP); + v.promotable = false; } _ => {} } @@ -478,17 +263,17 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node hir::ExprUnary(..) | hir::ExprBinary(..) | hir::ExprIndex(..) if v.tcx.tables().method_map.contains_key(&method_call) => { - v.add_qualif(ConstQualif::NOT_CONST); + v.promotable = false; } hir::ExprBox(_) => { - v.add_qualif(ConstQualif::NOT_CONST); + v.promotable = false; } hir::ExprUnary(op, ref inner) => { match v.tcx.tables().node_id_to_type(inner.id).sty { ty::TyRawPtr(_) => { assert!(op == hir::UnDeref); - v.add_qualif(ConstQualif::NOT_CONST); + v.promotable = false; } _ => {} } @@ -500,7 +285,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node op.node == hir::BiLe || op.node == hir::BiLt || op.node == hir::BiGe || op.node == hir::BiGt); - v.add_qualif(ConstQualif::NOT_CONST); + v.promotable = false; } _ => {} } @@ -510,7 +295,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node match v.tcx.cast_kinds.borrow().get(&from.id) { None => span_bug!(e.span, "no kind for cast"), Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => { - v.add_qualif(ConstQualif::NOT_CONST); + v.promotable = false; } _ => {} } @@ -518,33 +303,24 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node hir::ExprPath(ref qpath) => { let def = v.tcx.tables().qpath_def(qpath, e.id); match def { - Def::VariantCtor(_, CtorKind::Const) => { - // Size is determined by the whole enum, may be non-zero. - v.add_qualif(ConstQualif::NON_ZERO_SIZED); - } Def::VariantCtor(..) | Def::StructCtor(..) | Def::Fn(..) | Def::Method(..) => {} - Def::Static(..) => { - match v.mode { - Mode::Static | Mode::StaticMut => {} - Mode::Const | Mode::ConstFn => {} - Mode::Var => v.add_qualif(ConstQualif::NOT_CONST) - } - } - Def::Const(did) | Def::AssociatedConst(did) => { - let substs = Some(v.tcx.tables().node_id_item_substs(e.id) - .unwrap_or_else(|| v.tcx.intern_substs(&[]))); - if let Some((expr, _)) = lookup_const_by_id(v.tcx, did, substs) { - let inner = v.global_expr(Mode::Const, expr); - v.add_qualif(inner); - } - } - Def::Local(..) if v.mode == Mode::ConstFn => { - // Sadly, we can't determine whether the types are zero-sized. - v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED); + Def::AssociatedConst(_) => v.add_type(node_ty), + Def::Const(did) => { + v.promotable &= if let Some(node_id) = v.tcx.map.as_local_node_id(did) { + match v.tcx.map.expect_item(node_id).node { + hir::ItemConst(_, body) => { + v.visit_nested_body(body); + v.tcx.rvalue_promotable_to_static.borrow()[&body.node_id] + } + _ => false + } + } else { + v.tcx.sess.cstore.const_is_rvalue_promotable_to_static(did) + }; } _ => { - v.add_qualif(ConstQualif::NOT_CONST); + v.promotable = false; } } } @@ -565,65 +341,48 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } else { Def::Err }; - let is_const = match def { + match def { Def::StructCtor(_, CtorKind::Fn) | - Def::VariantCtor(_, CtorKind::Fn) => { - // `NON_ZERO_SIZED` is about the call result, not about the ctor itself. - v.add_qualif(ConstQualif::NON_ZERO_SIZED); - true - } + Def::VariantCtor(_, CtorKind::Fn) => {} Def::Fn(did) => { - v.handle_const_fn_call(e, did, node_ty) + v.handle_const_fn_call(did, node_ty) } Def::Method(did) => { match v.tcx.associated_item(did).container { ty::ImplContainer(_) => { - v.handle_const_fn_call(e, did, node_ty) + v.handle_const_fn_call(did, node_ty) } - ty::TraitContainer(_) => false + ty::TraitContainer(_) => v.promotable = false } } - _ => false - }; - if !is_const { - v.add_qualif(ConstQualif::NOT_CONST); + _ => v.promotable = false } } hir::ExprMethodCall(..) => { let method = v.tcx.tables().method_map[&method_call]; - let is_const = match v.tcx.associated_item(method.def_id).container { - ty::ImplContainer(_) => v.handle_const_fn_call(e, method.def_id, node_ty), - ty::TraitContainer(_) => false - }; - if !is_const { - v.add_qualif(ConstQualif::NOT_CONST); + match v.tcx.associated_item(method.def_id).container { + ty::ImplContainer(_) => v.handle_const_fn_call(method.def_id, node_ty), + ty::TraitContainer(_) => v.promotable = false } } hir::ExprStruct(..) => { if let ty::TyAdt(adt, ..) = v.tcx.tables().expr_ty(e).sty { // unsafe_cell_type doesn't necessarily exist with no_core if Some(adt.did) == v.tcx.lang_items.unsafe_cell_type() { - v.add_qualif(ConstQualif::MUTABLE_MEM); + v.promotable = false; } } } hir::ExprLit(_) | - hir::ExprAddrOf(..) => { - v.add_qualif(ConstQualif::NON_ZERO_SIZED); - } - - hir::ExprRepeat(..) => { - v.add_qualif(ConstQualif::PREFER_IN_PLACE); - } + hir::ExprAddrOf(..) | + hir::ExprRepeat(..) => {} hir::ExprClosure(..) => { // Paths in constant contexts cannot refer to local variables, // as there are none, and thus closures can't have upvars there. if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) { - assert!(v.mode == Mode::Var, - "global closures can't capture anything"); - v.add_qualif(ConstQualif::NOT_CONST); + v.promotable = false; } } @@ -652,7 +411,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node hir::ExprAssign(..) | hir::ExprAssignOp(..) | hir::ExprInlineAsm(..) => { - v.add_qualif(ConstQualif::NOT_CONST); + v.promotable = false; } } } @@ -671,7 +430,7 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp Some(Adjust::DerefRef { autoderefs, .. }) => { if (0..autoderefs as u32) .any(|autoderef| v.tcx.tables().is_overloaded_autoderef(e.id, autoderef)) { - v.add_qualif(ConstQualif::NOT_CONST); + v.promotable = false; } } } @@ -681,9 +440,10 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.visit_all_item_likes_in_krate(DepNode::CheckConst, &mut CheckCrateVisitor { tcx: tcx, - mode: Mode::Var, - qualif: ConstQualif::NOT_CONST, - rvalue_borrows: NodeMap(), + in_fn: false, + promotable: false, + mut_rvalue_borrows: NodeSet(), + param_env: tcx.empty_parameter_environment(), }.as_deep_visitor()); tcx.sess.abort_if_errors(); } @@ -692,24 +452,9 @@ impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> { fn consume(&mut self, _consume_id: ast::NodeId, _consume_span: Span, - cmt: mc::cmt, - _mode: euv::ConsumeMode) { - let mut cur = &cmt; - loop { - match cur.cat { - Categorization::StaticItem => { - break; - } - Categorization::Deref(ref cmt, ..) | - Categorization::Downcast(ref cmt, _) | - Categorization::Interior(ref cmt, _) => cur = cmt, + _cmt: mc::cmt, + _mode: euv::ConsumeMode) {} - Categorization::Rvalue(..) | - Categorization::Upvar(..) | - Categorization::Local(..) => break, - } - } - } fn borrow(&mut self, borrow_id: ast::NodeId, _borrow_span: Span, @@ -736,21 +481,9 @@ impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> { // Ignore the dummy immutable borrow created by EUV. break; } - let mutbl = bk.to_mutbl_lossy(); - if mutbl == hir::MutMutable && self.mode == Mode::StaticMut { - // Mutable slices are the only `&mut` allowed in - // globals, but only in `static mut`, nowhere else. - // FIXME: This exception is really weird... there isn't - // any fundamental reason to restrict this based on - // type of the expression. `&mut [1]` has exactly the - // same representation as &mut 1. - match cmt.ty.sty { - ty::TyArray(..) | - ty::TySlice(_) => break, - _ => {} - } + if bk.to_mutbl_lossy() == hir::MutMutable { + self.mut_rvalue_borrows.insert(borrow_id); } - self.record_borrow(borrow_id, mutbl); break; } Categorization::StaticItem => { diff --git a/src/librustc_passes/rvalues.rs b/src/librustc_passes/rvalues.rs index e4151a66c31..3da4f24b6c2 100644 --- a/src/librustc_passes/rvalues.rs +++ b/src/librustc_passes/rvalues.rs @@ -52,7 +52,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RvalueContext<'a, 'tcx> { }; let body = infcx.tcx.map.body(b); let mut euv = euv::ExprUseVisitor::new(&mut delegate, &infcx); - euv.walk_fn(body); + euv.consume_body(body); }); intravisit::walk_fn(self, fk, fd, b, s, fn_id) } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index c5c009ccca6..2bc42a46152 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -202,8 +202,6 @@ use rustc::mir::{self, Location}; use rustc::mir::visit as mir_visit; use rustc::mir::visit::Visitor as MirVisitor; -use rustc_const_eval as const_eval; - use syntax::abi::Abi; use syntax_pos::DUMMY_SP; use base::custom_coerce_unsize_info; @@ -344,19 +342,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, recursion_depth_reset = None; - // Scan the MIR in order to find function calls, closures, and - // drop-glue - let mir = scx.tcx().item_mir(def_id); - - let empty_substs = scx.empty_substs_for_def_id(def_id); - let visitor = MirNeighborCollector { - scx: scx, - mir: &mir, - output: &mut neighbors, - param_substs: empty_substs - }; - - visit_mir_and_promoted(visitor, &mir); + collect_neighbours(scx, Instance::mono(scx, def_id), &mut neighbors); } TransItem::Fn(instance) => { // Keep track of the monomorphization recursion depth @@ -365,18 +351,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, recursion_depths)); check_type_length_limit(scx.tcx(), instance); - // Scan the MIR in order to find function calls, closures, and - // drop-glue - let mir = scx.tcx().item_mir(instance.def); - - let visitor = MirNeighborCollector { - scx: scx, - mir: &mir, - output: &mut neighbors, - param_substs: instance.substs - }; - - visit_mir_and_promoted(visitor, &mir); + collect_neighbours(scx, instance, &mut neighbors); } } @@ -563,33 +538,12 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { // This is not a callee, but we still have to look for // references to `const` items if let mir::Literal::Item { def_id, substs } = constant.literal { - let tcx = self.scx.tcx(); let substs = monomorphize::apply_param_substs(self.scx, self.param_substs, &substs); - // If the constant referred to here is an associated - // item of a trait, we need to resolve it to the actual - // constant in the corresponding impl. Luckily - // const_eval::lookup_const_by_id() does that for us. - if let Some((expr, _)) = const_eval::lookup_const_by_id(tcx, - def_id, - Some(substs)) { - // The hir::Expr we get here is the initializer of - // the constant, what we really want is the item - // DefId. - let const_node_id = tcx.map.get_parent(expr.id); - let def_id = if tcx.map.is_inlined_node_id(const_node_id) { - tcx.sess.cstore.defid_for_inlined_node(const_node_id).unwrap() - } else { - tcx.map.local_def_id(const_node_id) - }; - - collect_const_item_neighbours(self.scx, - def_id, - substs, - self.output); - } + let instance = Instance::new(def_id, substs).resolve_const(self.scx); + collect_neighbours(self.scx, instance, self.output); } None @@ -1233,29 +1187,20 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(scx: &SharedCrateContext<'a, ' } } -// There are no translation items for constants themselves but their -// initializers might still contain something that produces translation items, -// such as cast that introduce a new vtable. -fn collect_const_item_neighbours<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - output: &mut Vec>) +/// Scan the MIR in order to find function calls, closures, and drop-glue +fn collect_neighbours<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, + instance: Instance<'tcx>, + output: &mut Vec>) { - // Scan the MIR in order to find function calls, closures, and - // drop-glue - let mir = scx.tcx().item_mir(def_id); + let mir = scx.tcx().item_mir(instance.def); - let visitor = MirNeighborCollector { + let mut visitor = MirNeighborCollector { scx: scx, mir: &mir, output: output, - param_substs: substs + param_substs: instance.substs }; - visit_mir_and_promoted(visitor, &mir); -} - -fn visit_mir_and_promoted<'tcx, V: MirVisitor<'tcx>>(mut visitor: V, mir: &mir::Mir<'tcx>) { visitor.visit_mir(&mir); for promoted in &mir.promoted { visitor.visit_mir(promoted); diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index f292a709650..d9179d6a6fa 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -737,14 +737,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local().drop_glues } - pub fn local_node_for_inlined_defid<'a>(&'a self, def_id: DefId) -> Option { - self.sess().cstore.local_node_for_inlined_defid(def_id) - } - - pub fn defid_for_inlined_node<'a>(&'a self, node_id: ast::NodeId) -> Option { - self.sess().cstore.defid_for_inlined_node(node_id) - } - pub fn instances<'a>(&'a self) -> &'a RefCell, ValueRef>> { &self.local().instances } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 08f68f8d49c..1cedaa0a4e1 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -18,7 +18,6 @@ use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; use rustc::mir; use rustc::mir::tcx::LvalueTy; -use rustc::traits; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::subst::Substs; @@ -36,7 +35,7 @@ use type_::Type; use value::Value; use syntax::ast; -use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::Span; use std::fmt; use std::ptr; @@ -238,24 +237,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } fn trans_def(ccx: &'a CrateContext<'a, 'tcx>, - mut instance: Instance<'tcx>, + instance: Instance<'tcx>, args: IndexVec>) -> Result, ConstEvalErr> { - // Try to resolve associated constants. - if let Some(trait_id) = ccx.tcx().trait_of_item(instance.def) { - let trait_ref = ty::TraitRef::new(trait_id, instance.substs); - let trait_ref = ty::Binder(trait_ref); - let vtable = common::fulfill_obligation(ccx.shared(), DUMMY_SP, trait_ref); - if let traits::VtableImpl(vtable_impl) = vtable { - let name = ccx.tcx().item_name(instance.def); - let ac = ccx.tcx().associated_items(vtable_impl.impl_def_id) - .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); - if let Some(ac) = ac { - instance = Instance::new(ac.def_id, vtable_impl.substs); - } - } - } - + let instance = instance.resolve_const(ccx.shared()); let mir = ccx.tcx().item_mir(instance.def); MirConstContext::new(ccx, &mir, instance.substs, args).trans() } diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 8f05cc793ef..4b31d5b7f88 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -11,11 +11,15 @@ use common::*; use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; +use rustc::traits; use rustc::ty::fold::{TypeFolder, TypeFoldable}; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::ppaux; use rustc::util::common::MemoizationMap; + +use syntax::codemap::DUMMY_SP; + use std::fmt; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -30,15 +34,35 @@ impl<'tcx> fmt::Display for Instance<'tcx> { } } -impl<'tcx> Instance<'tcx> { +impl<'a, 'tcx> Instance<'tcx> { pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) -> Instance<'tcx> { assert!(substs.regions().all(|&r| r == ty::ReErased)); Instance { def: def_id, substs: substs } } - pub fn mono<'a>(scx: &SharedCrateContext<'a, 'tcx>, def_id: DefId) -> Instance<'tcx> { + + pub fn mono(scx: &SharedCrateContext<'a, 'tcx>, def_id: DefId) -> Instance<'tcx> { Instance::new(def_id, scx.empty_substs_for_def_id(def_id)) } + + /// For associated constants from traits, return the impl definition. + pub fn resolve_const(&self, scx: &SharedCrateContext<'a, 'tcx>) -> Self { + if let Some(trait_id) = scx.tcx().trait_of_item(self.def) { + let trait_ref = ty::TraitRef::new(trait_id, self.substs); + let trait_ref = ty::Binder(trait_ref); + let vtable = fulfill_obligation(scx, DUMMY_SP, trait_ref); + if let traits::VtableImpl(vtable_impl) = vtable { + let name = scx.tcx().item_name(self.def); + let ac = scx.tcx().associated_items(vtable_impl.impl_def_id) + .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); + if let Some(ac) = ac { + return Instance::new(ac.def_id, vtable_impl.substs); + } + } + } + + *self + } } /// Monomorphizes a type from the AST by first applying the in-scope diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index af4fe2dc41b..5d927a503a1 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -171,7 +171,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { mc::MemCategorizationOptions { during_closure_kind_inference: true }); - euv.walk_fn(body); + euv.consume_body(body); } // Now that we've analyzed the closure, we know how each diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 94e9fdbfc3e..f2ba6952eb8 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -21,8 +21,6 @@ use rustc::hir::print as pprust; use rustc::ty; use rustc::util::nodemap::FxHashSet; -use rustc_const_eval::lookup_const_by_id; - use core::{DocContext, DocAccessLevels}; use doctree; use clean::{self, GetDefId}; @@ -346,7 +344,7 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec) { ty::AssociatedKind::Const => { let default = if item.defaultness.has_value() { Some(pprust::expr_to_string( - lookup_const_by_id(tcx, item.def_id, None).unwrap().0)) + &tcx.sess.cstore.maybe_get_item_body(tcx, item.def_id).unwrap().value)) } else { None }; @@ -477,16 +475,10 @@ fn build_module(cx: &DocContext, did: DefId) -> clean::Module { } fn build_const(cx: &DocContext, did: DefId) -> clean::Constant { - let (expr, ty) = lookup_const_by_id(cx.tcx, did, None).unwrap_or_else(|| { - panic!("expected lookup_const_by_id to succeed for {:?}", did); - }); - debug!("converting constant expr {:?} to snippet", expr); - let sn = pprust::expr_to_string(expr); - debug!("got snippet {}", sn); - clean::Constant { - type_: ty.map(|t| t.clean(cx)).unwrap_or_else(|| cx.tcx.item_type(did).clean(cx)), - expr: sn + type_: cx.tcx.item_type(did).clean(cx), + expr: pprust::expr_to_string( + &cx.tcx.sess.cstore.maybe_get_item_body(cx.tcx, did).unwrap().value) } }