convert constant promotion into a query

This commit is contained in:
Niko Matsakis 2017-09-11 13:09:14 -04:00
parent 807e157b4c
commit 0f568e2f34
13 changed files with 165 additions and 54 deletions

View file

@ -553,6 +553,7 @@ define_dep_nodes!( <'tcx>
[] LookupDeprecationEntry(DefId),
[] ItemBodyNestedBodies(DefId),
[] ConstIsRvaluePromotableToStatic(DefId),
[] RvaluePromotableMap(DefId),
[] ImplParent(DefId),
[] TraitOfItem(DefId),
[] IsExportedSymbol(DefId),

View file

@ -27,10 +27,11 @@ use middle::region;
use ty::{self, TyCtxt, adjustment};
use hir::{self, PatKind};
use std::rc::Rc;
use syntax::ast;
use syntax::ptr::P;
use syntax_pos::Span;
use util::nodemap::ItemLocalMap;
///////////////////////////////////////////////////////////////////////////
// The Delegate trait
@ -262,15 +263,30 @@ macro_rules! return_if_err {
}
impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx, 'tcx> {
/// Creates the ExprUseVisitor, configuring it with the various options provided:
///
/// - `delegate` -- who receives the callbacks
/// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`)
/// - `region_scope_tree` --- region scope tree for the code being analyzed
/// - `tables` --- typeck results for the code being analyzed
/// - `rvalue_promotable_map` --- if you care about rvalue promotion, then provide
/// the map here (it can be computed with `tcx.rvalue_promotable_map(def_id)`).
/// `None` means that rvalues will be given more conservative lifetimes.
///
/// See also `with_infer`, which is used *during* typeck.
pub fn new(delegate: &'a mut (Delegate<'tcx>+'a),
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
region_scope_tree: &'a region::ScopeTree,
tables: &'a ty::TypeckTables<'tcx>)
tables: &'a ty::TypeckTables<'tcx>,
rvalue_promotable_map: Option<Rc<ItemLocalMap<bool>>>)
-> Self
{
ExprUseVisitor {
mc: mc::MemCategorizationContext::new(tcx, region_scope_tree, tables),
mc: mc::MemCategorizationContext::new(tcx,
region_scope_tree,
tables,
rvalue_promotable_map),
delegate,
param_env,
}

View file

@ -86,6 +86,7 @@ use syntax_pos::Span;
use std::fmt;
use std::rc::Rc;
use util::nodemap::ItemLocalMap;
#[derive(Clone, PartialEq)]
pub enum Categorization<'tcx> {
@ -285,6 +286,7 @@ pub struct MemCategorizationContext<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
pub region_scope_tree: &'a region::ScopeTree,
pub tables: &'a ty::TypeckTables<'tcx>,
rvalue_promotable_map: Option<Rc<ItemLocalMap<bool>>>,
infcx: Option<&'a InferCtxt<'a, 'gcx, 'tcx>>,
}
@ -392,21 +394,46 @@ impl MutabilityCategory {
impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
region_scope_tree: &'a region::ScopeTree,
tables: &'a ty::TypeckTables<'tcx>)
tables: &'a ty::TypeckTables<'tcx>,
rvalue_promotable_map: Option<Rc<ItemLocalMap<bool>>>)
-> MemCategorizationContext<'a, 'tcx, 'tcx> {
MemCategorizationContext { tcx, region_scope_tree, tables, infcx: None }
MemCategorizationContext {
tcx,
region_scope_tree,
tables,
rvalue_promotable_map,
infcx: None
}
}
}
impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
/// Creates a `MemCategorizationContext` during type inference.
/// This is used during upvar analysis and a few other places.
/// Because the typeck tables are not yet complete, the results
/// from the analysis must be used with caution:
///
/// - rvalue promotions are not known, so the lifetimes of
/// temporaries may be overly conservative;
/// - similarly, as the results of upvar analysis are not yet
/// known, the results around upvar accesses may be incorrect.
pub fn with_infer(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
region_scope_tree: &'a region::ScopeTree,
tables: &'a ty::TypeckTables<'tcx>)
-> MemCategorizationContext<'a, 'gcx, 'tcx> {
let tcx = infcx.tcx;
// Subtle: we can't do rvalue promotion analysis until the
// typeck false is complete, which means that you can't trust
// the rvalue lifetimes that result, but that's ok, since we
// don't need to know those during type inference.
let rvalue_promotable_map = None;
MemCategorizationContext {
tcx: infcx.tcx,
tcx,
region_scope_tree,
tables,
rvalue_promotable_map,
infcx: Some(infcx),
}
}
@ -871,8 +898,9 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
span: Span,
expr_ty: Ty<'tcx>)
-> cmt<'tcx> {
let promotable = self.tcx.rvalue_promotable_to_static.borrow().get(&id).cloned()
.unwrap_or(false);
let hir_id = self.tcx.hir.node_to_hir_id(id);
let promotable = self.rvalue_promotable_map.as_ref().map(|m| m[&hir_id.local_id])
.unwrap_or(false);
// Always promote `[T; 0]` (even when e.g. borrowed mutably).
let promotable = match expr_ty.sty {
@ -887,7 +915,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
let re = if promotable {
self.tcx.types.re_static
} else {
self.temporary_scope(self.tcx.hir.node_to_hir_id(id).local_id)
self.temporary_scope(hir_id.local_id)
};
let ret = self.cat_rvalue(id, span, re, expr_ty);
debug!("cat_rvalue_node ret {:?}", ret);

View file

@ -904,9 +904,6 @@ pub struct GlobalCtxt<'tcx> {
/// Merge this with `selection_cache`?
pub evaluation_cache: traits::EvaluationCache<'tcx>,
/// Maps Expr NodeId's to `true` iff `&expr` can have 'static lifetime.
pub rvalue_promotable_to_static: RefCell<NodeMap<bool>>,
/// The definite name of the current crate after taking into account
/// attributes, commandline parameters, etc.
pub crate_name: Symbol,
@ -1178,7 +1175,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
normalized_cache: RefCell::new(FxHashMap()),
selection_cache: traits::SelectionCache::new(),
evaluation_cache: traits::EvaluationCache::new(),
rvalue_promotable_to_static: RefCell::new(NodeMap()),
crate_name: Symbol::intern(crate_name),
data_layout,
layout_interner: RefCell::new(FxHashSet()),

View file

@ -29,7 +29,12 @@ pub(super) trait QueryDescription: QueryConfig {
impl<M: QueryConfig<Key=DefId>> QueryDescription for M {
default fn describe(tcx: TyCtxt, def_id: DefId) -> String {
format!("processing `{}`", tcx.item_path_str(def_id))
if !tcx.sess.verbose() {
format!("processing `{}`", tcx.item_path_str(def_id))
} else {
let name = unsafe { ::std::intrinsics::type_name::<M>() };
format!("processing `{}` applied to `{:?}`", name, def_id)
}
}
}
@ -214,6 +219,13 @@ impl<'tcx> QueryDescription for queries::const_is_rvalue_promotable_to_static<'t
}
}
impl<'tcx> QueryDescription for queries::rvalue_promotable_map<'tcx> {
fn describe(tcx: TyCtxt, def_id: DefId) -> String {
format!("checking which parts of `{}` are promotable to static",
tcx.item_path_str(def_id))
}
}
impl<'tcx> QueryDescription for queries::is_mir_available<'tcx> {
fn describe(tcx: TyCtxt, def_id: DefId) -> String {
format!("checking if item is mir available: `{}`",

View file

@ -37,7 +37,7 @@ use ty::{self, CrateInherentImpls, Ty, TyCtxt};
use ty::layout::{Layout, LayoutError};
use ty::steal::Steal;
use ty::subst::Substs;
use util::nodemap::{DefIdSet, DefIdMap};
use util::nodemap::{DefIdSet, DefIdMap, ItemLocalMap};
use util::common::{profq_msg, ProfileQueriesMsg};
use rustc_data_structures::indexed_set::IdxSetBuf;
@ -228,6 +228,7 @@ define_maps! { <'tcx>
[] fn is_exported_symbol: IsExportedSymbol(DefId) -> bool,
[] fn item_body_nested_bodies: ItemBodyNestedBodies(DefId) -> ExternBodyNestedBodies,
[] fn const_is_rvalue_promotable_to_static: ConstIsRvaluePromotableToStatic(DefId) -> bool,
[] fn rvalue_promotable_map: RvaluePromotableMap(DefId) -> Rc<ItemLocalMap<bool>>,
[] fn is_mir_available: IsMirAvailable(DefId) -> bool,
[] fn vtable_methods: vtable_methods_node(ty::PolyTraitRef<'tcx>)
-> Rc<Vec<Option<(DefId, &'tcx Substs<'tcx>)>>>,

View file

@ -206,7 +206,13 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
all_loans,
param_env,
};
euv::ExprUseVisitor::new(&mut clcx, bccx.tcx, param_env, &bccx.region_scope_tree, bccx.tables)
let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id);
euv::ExprUseVisitor::new(&mut clcx,
bccx.tcx,
param_env,
&bccx.region_scope_tree,
bccx.tables,
Some(rvalue_promotable_map))
.consume_body(body);
}

View file

@ -48,7 +48,13 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
move_error_collector: move_error::MoveErrorCollector::new(),
};
euv::ExprUseVisitor::new(&mut glcx, bccx.tcx, param_env, &bccx.region_scope_tree, bccx.tables)
let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id);
euv::ExprUseVisitor::new(&mut glcx,
bccx.tcx,
param_env,
&bccx.region_scope_tree,
bccx.tables,
Some(rvalue_promotable_map))
.consume_body(bccx.body);
glcx.report_potential_errors();

View file

@ -526,7 +526,7 @@ fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Expr) {
let mut checker = MutationChecker {
cx,
};
ExprUseVisitor::new(&mut checker, cx.tcx, cx.param_env, cx.region_scope_tree, cx.tables)
ExprUseVisitor::new(&mut checker, cx.tcx, cx.param_env, cx.region_scope_tree, cx.tables, None)
.walk_expr(guard);
}

View file

@ -38,7 +38,7 @@ use rustc_typeck as typeck;
use rustc_privacy;
use rustc_plugin::registry::Registry;
use rustc_plugin as plugin;
use rustc_passes::{ast_validation, no_asm, loops, consts, static_recursion, hir_stats};
use rustc_passes::{self, ast_validation, no_asm, loops, consts, static_recursion, hir_stats};
use rustc_const_eval::{self, check_match};
use super::Compilation;
use ::DefaultTransCrate;
@ -973,6 +973,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
traits::provide(&mut local_providers);
reachable::provide(&mut local_providers);
rustc_const_eval::provide(&mut local_providers);
rustc_passes::provide(&mut local_providers);
middle::region::provide(&mut local_providers);
cstore::provide_local(&mut local_providers);
lint::provide(&mut local_providers);

View file

@ -56,7 +56,8 @@ impl<'a, 'b, 'tcx> IsolatedEncoder<'a, 'b, 'tcx> {
};
let lazy_body = self.lazy(body);
let tables = self.tcx.body_tables(body_id);
let body_owner_def_id = self.tcx.hir.body_owner_def_id(body_id);
let tables = self.tcx.typeck_tables_of(body_owner_def_id);
let lazy_tables = self.lazy(tables);
let mut visitor = NestedBodyCollector {
@ -67,7 +68,7 @@ impl<'a, 'b, 'tcx> IsolatedEncoder<'a, 'b, 'tcx> {
let lazy_nested_bodies = self.lazy_seq_ref_from_slice(&visitor.bodies_found);
let rvalue_promotable_to_static =
self.tcx.rvalue_promotable_to_static.borrow()[&body.value.id];
self.tcx.const_is_rvalue_promotable_to_static(body_owner_def_id);
self.lazy(&Ast {
body: lazy_body,

View file

@ -39,37 +39,79 @@ 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::ty::maps::{queries, Providers};
use rustc::ty::subst::Substs;
use rustc::traits::Reveal;
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::NodeSet;
use rustc::util::nodemap::{ItemLocalMap, NodeSet};
use rustc::lint::builtin::CONST_ERR;
use rustc::hir::{self, PatKind, RangeEnd};
use std::rc::Rc;
use syntax::ast;
use syntax_pos::{Span, DUMMY_SP};
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
use std::collections::hash_map::Entry;
use std::cmp::Ordering;
pub fn provide(providers: &mut Providers) {
*providers = Providers {
rvalue_promotable_map,
const_is_rvalue_promotable_to_static,
..*providers
};
}
pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
for &body_id in &tcx.hir.krate().body_ids {
let visitor = &mut CheckCrateVisitor {
tcx,
tables: &ty::TypeckTables::empty(None),
in_fn: false,
in_static: false,
promotable: false,
mut_rvalue_borrows: NodeSet(),
param_env: ty::ParamEnv::empty(Reveal::UserFacing),
identity_substs: Substs::empty(),
};
visitor.visit_nested_body(body_id);
let def_id = tcx.hir.body_owner_def_id(body_id);
tcx.const_is_rvalue_promotable_to_static(def_id);
}
tcx.sess.abort_if_errors();
}
fn const_is_rvalue_promotable_to_static<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
-> bool
{
assert!(def_id.is_local());
let node_id = tcx.hir.as_local_node_id(def_id)
.expect("rvalue_promotable_map invoked with non-local def-id");
let body_id = tcx.hir.body_owned_by(node_id);
let body_hir_id = tcx.hir.node_to_hir_id(body_id.node_id);
tcx.rvalue_promotable_map(def_id).contains_key(&body_hir_id.local_id)
}
fn rvalue_promotable_map<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
-> Rc<ItemLocalMap<bool>>
{
let outer_def_id = tcx.closure_base_def_id(def_id);
if outer_def_id != def_id {
return tcx.rvalue_promotable_map(outer_def_id);
}
let mut visitor = CheckCrateVisitor {
tcx,
tables: &ty::TypeckTables::empty(None),
in_fn: false,
in_static: false,
promotable: false,
mut_rvalue_borrows: NodeSet(),
param_env: ty::ParamEnv::empty(Reveal::UserFacing),
identity_substs: Substs::empty(),
result_map: ItemLocalMap(),
};
// `def_id` should be a `Body` owner
let node_id = tcx.hir.as_local_node_id(def_id)
.expect("rvalue_promotable_map invoked with non-local def-id");
let body_id = tcx.hir.body_owned_by(node_id);
visitor.visit_nested_body(body_id);
Rc::new(visitor.result_map)
}
struct CheckCrateVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
in_fn: bool,
@ -79,6 +121,7 @@ struct CheckCrateVisitor<'a, 'tcx: 'a> {
param_env: ty::ParamEnv<'tcx>,
identity_substs: &'tcx Substs<'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
result_map: ItemLocalMap<bool>,
}
impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
@ -126,18 +169,11 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
// note that we *do* visit nested bodies, because we override `visit_nested_body` below
NestedVisitorMap::None
}
fn visit_nested_body(&mut self, body_id: hir::BodyId) {
match self.tcx.rvalue_promotable_to_static.borrow_mut().entry(body_id.node_id) {
Entry::Occupied(_) => return,
Entry::Vacant(entry) => {
// Prevent infinite recursion on re-entry.
entry.insert(false);
}
}
let item_id = self.tcx.hir.body_owner(body_id);
let item_def_id = self.tcx.hir.local_def_id(item_id);
@ -168,7 +204,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
let tcx = self.tcx;
let param_env = self.param_env;
let region_scope_tree = self.tcx.region_scope_tree(item_def_id);
euv::ExprUseVisitor::new(self, tcx, param_env, &region_scope_tree, self.tables)
euv::ExprUseVisitor::new(self, tcx, param_env, &region_scope_tree, self.tables, None)
.consume_body(body);
self.visit_body(body);
@ -287,7 +323,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
}
}
self.tcx.rvalue_promotable_to_static.borrow_mut().insert(ex.id, self.promotable);
self.result_map.insert(ex.hir_id.local_id, self.promotable);
self.promotable &= outer;
}
}
@ -388,16 +424,17 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
let promotable = if v.tcx.trait_of_item(did).is_some() {
// Don't peek inside trait associated constants.
false
} else if let Some(node_id) = v.tcx.hir.as_local_node_id(did) {
match v.tcx.hir.maybe_body_owned_by(node_id) {
Some(body) => {
v.visit_nested_body(body);
v.tcx.rvalue_promotable_to_static.borrow()[&body.node_id]
}
None => false
}
} else {
v.tcx.const_is_rvalue_promotable_to_static(did)
queries::const_is_rvalue_promotable_to_static::try_get(v.tcx, e.span, did)
.unwrap_or_else(|mut err| {
// A cycle between constants ought to be reported elsewhere.
err.cancel();
v.tcx.sess.delay_span_bug(
e.span,
&format!("cycle encountered during const qualification: {:?}",
did));
false
})
};
// Just in case the type is more specific than the definition,

View file

@ -33,6 +33,8 @@ extern crate syntax;
extern crate syntax_pos;
extern crate rustc_errors as errors;
use rustc::ty::maps::Providers;
mod diagnostics;
pub mod ast_validation;
@ -44,3 +46,7 @@ pub mod no_asm;
pub mod static_recursion;
__build_diagnostic_array! { librustc_passes, DIAGNOSTICS }
pub fn provide(providers: &mut Providers) {
consts::provide(providers);
}