Auto merge of #39927 - nikomatsakis:incr-comp-skip-borrowck-2, r=eddyb

transition borrowck to visit all **bodies** and not item-likes

This is a better structure for incremental compilation and also more compatible with the eventual borrowck mir. It also fixes #38520 as a drive-by fix.

r? @eddyb
This commit is contained in:
bors 2017-03-03 00:14:10 +00:00
commit 06c63f6e9e
34 changed files with 266 additions and 401 deletions

View File

@ -32,7 +32,7 @@ struct LoopScope {
}
pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
body: &hir::Expr) -> CFG {
body: &hir::Body) -> CFG {
let mut graph = graph::Graph::new();
let entry = graph.add_node(CFGNodeData::Entry);
@ -43,26 +43,18 @@ pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let fn_exit = graph.add_node(CFGNodeData::Exit);
let body_exit;
// Find the function this expression is from.
let mut node_id = body.id;
loop {
let node = tcx.hir.get(node_id);
if hir::map::blocks::FnLikeNode::from_node(node).is_some() {
break;
}
let parent = tcx.hir.get_parent_node(node_id);
assert!(node_id != parent);
node_id = parent;
}
// Find the tables for this body.
let owner_def_id = tcx.hir.local_def_id(tcx.hir.body_owner(body.id()));
let tables = tcx.item_tables(owner_def_id);
let mut cfg_builder = CFGBuilder {
tcx: tcx,
tables: tcx.item_tables(tcx.hir.local_def_id(node_id)),
tables: tables,
graph: graph,
fn_exit: fn_exit,
loop_scopes: Vec::new()
};
body_exit = cfg_builder.expr(body, entry);
body_exit = cfg_builder.expr(&body.value, entry);
cfg_builder.add_contained_edge(body_exit, fn_exit);
let CFGBuilder {graph, ..} = cfg_builder;
CFG {graph: graph,

View File

@ -59,7 +59,7 @@ pub type CFGEdge = graph::Edge<CFGEdgeData>;
impl CFG {
pub fn new<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
body: &hir::Expr) -> CFG {
body: &hir::Body) -> CFG {
construct::construct(tcx, body)
}

View File

@ -89,8 +89,10 @@ pub enum DepNode<D: Clone + Debug> {
// Represents the MIR for a fn; also used as the task node for
// things read/modify that MIR.
MirKrate,
Mir(D),
BorrowCheckKrate,
BorrowCheck(D),
RvalueCheck(D),
Reachability,
@ -114,6 +116,7 @@ pub enum DepNode<D: Clone + Debug> {
SizedConstraint(D),
AssociatedItemDefIds(D),
InherentImpls(D),
TypeckBodiesKrate,
TypeckTables(D),
UsedTraitImports(D),
MonomorphicConstEval(D),
@ -209,6 +212,9 @@ impl<D: Clone + Debug> DepNode<D> {
match *self {
Krate => Some(Krate),
BorrowCheckKrate => Some(BorrowCheckKrate),
MirKrate => Some(MirKrate),
TypeckBodiesKrate => Some(TypeckBodiesKrate),
CollectLanguageItems => Some(CollectLanguageItems),
CheckStaticRecursion => Some(CheckStaticRecursion),
ResolveLifetimes => Some(ResolveLifetimes),

View File

@ -25,5 +25,6 @@ pub use self::dep_node::WorkProductId;
pub use self::graph::DepGraph;
pub use self::graph::WorkProduct;
pub use self::query::DepGraphQuery;
pub use self::visit::visit_all_bodies_in_krate;
pub use self::visit::visit_all_item_likes_in_krate;
pub use self::raii::DepTask;

View File

@ -74,3 +74,13 @@ pub fn visit_all_item_likes_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>
};
krate.visit_all_item_likes(&mut tracking_visitor)
}
pub fn visit_all_bodies_in_krate<'a, 'tcx, C>(tcx: TyCtxt<'a, 'tcx, 'tcx>, callback: C)
where C: Fn(/* body_owner */ DefId, /* body id */ hir::BodyId),
{
let krate = tcx.hir.krate();
for &body_id in &krate.body_ids {
let body_owner_def_id = tcx.hir.body_owner_def_id(body_id);
callback(body_owner_def_id, body_id);
}
}

View File

@ -46,7 +46,7 @@ use hir::map::definitions::DefPathData;
use hir::def_id::{DefIndex, DefId};
use hir::def::{Def, PathResolution};
use session::Session;
use util::nodemap::{DefIdMap, NodeMap, FxHashMap};
use util::nodemap::{DefIdMap, NodeMap};
use std::collections::BTreeMap;
use std::iter;
@ -78,7 +78,7 @@ pub struct LoweringContext<'a> {
trait_items: BTreeMap<hir::TraitItemId, hir::TraitItem>,
impl_items: BTreeMap<hir::ImplItemId, hir::ImplItem>,
bodies: FxHashMap<hir::BodyId, hir::Body>,
bodies: BTreeMap<hir::BodyId, hir::Body>,
trait_impls: BTreeMap<DefId, Vec<NodeId>>,
trait_default_impl: BTreeMap<DefId, NodeId>,
@ -118,7 +118,7 @@ pub fn lower_crate(sess: &Session,
items: BTreeMap::new(),
trait_items: BTreeMap::new(),
impl_items: BTreeMap::new(),
bodies: FxHashMap(),
bodies: BTreeMap::new(),
trait_impls: BTreeMap::new(),
trait_default_impl: BTreeMap::new(),
loop_scopes: Vec::new(),
@ -196,6 +196,7 @@ impl<'a> LoweringContext<'a> {
let module = self.lower_mod(&c.module);
let attrs = self.lower_attrs(&c.attrs);
let exported_macros = c.exported_macros.iter().map(|m| self.lower_macro_def(m)).collect();
let body_ids = body_ids(&self.bodies);
hir::Crate {
module: module,
@ -206,6 +207,7 @@ impl<'a> LoweringContext<'a> {
trait_items: self.trait_items,
impl_items: self.impl_items,
bodies: self.bodies,
body_ids: body_ids,
trait_impls: self.trait_impls,
trait_default_impl: self.trait_default_impl,
}
@ -2524,3 +2526,11 @@ impl<'a> LoweringContext<'a> {
}
}
}
fn body_ids(bodies: &BTreeMap<hir::BodyId, hir::Body>) -> Vec<hir::BodyId> {
// Sorting by span ensures that we get things in order within a
// file, and also puts the files in a sensible order.
let mut body_ids: Vec<_> = bodies.keys().cloned().collect();
body_ids.sort_by_key(|b| bodies[b].value.span);
body_ids
}

View File

@ -259,6 +259,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
TyKind::ImplTrait(..) => {
self.create_def(ty.id, DefPathData::ImplTrait);
}
TyKind::Typeof(ref expr) => self.visit_ast_const_integer(expr),
_ => {}
}
visit::walk_ty(self, ty);

View File

@ -260,7 +260,9 @@ pub enum DefPathData {
/// Pattern binding
Binding(InternedString),
/// An `impl Trait` type node.
ImplTrait
ImplTrait,
/// A `typeof` type node.
Typeof,
}
impl Definitions {
@ -387,7 +389,8 @@ impl DefPathData {
ClosureExpr |
StructCtor |
Initializer |
ImplTrait => None
ImplTrait |
Typeof => None
}
}
@ -415,6 +418,7 @@ impl DefPathData {
StructCtor => "{{constructor}}",
Initializer => "{{initializer}}",
ImplTrait => "{{impl-Trait}}",
Typeof => "{{typeof}}",
};
Symbol::intern(s).as_str()

View File

@ -168,43 +168,48 @@ impl<'hir> MapEntry<'hir> {
})
}
fn is_body_owner(self, node_id: NodeId) -> bool {
fn associated_body(self) -> Option<BodyId> {
match self {
EntryItem(_, item) => {
match item.node {
ItemConst(_, body) |
ItemStatic(.., body) |
ItemFn(_, _, _, _, _, body) => body.node_id == node_id,
_ => false
ItemFn(_, _, _, _, _, body) => Some(body),
_ => None,
}
}
EntryTraitItem(_, item) => {
match item.node {
TraitItemKind::Const(_, Some(body)) |
TraitItemKind::Method(_, TraitMethod::Provided(body)) => {
body.node_id == node_id
}
_ => false
TraitItemKind::Method(_, TraitMethod::Provided(body)) => Some(body),
_ => None
}
}
EntryImplItem(_, item) => {
match item.node {
ImplItemKind::Const(_, body) |
ImplItemKind::Method(_, body) => body.node_id == node_id,
_ => false
ImplItemKind::Method(_, body) => Some(body),
_ => None,
}
}
EntryExpr(_, expr) => {
match expr.node {
ExprClosure(.., body, _) => body.node_id == node_id,
_ => false
ExprClosure(.., body, _) => Some(body),
_ => None,
}
}
_ => false
_ => None
}
}
fn is_body_owner(self, node_id: NodeId) -> bool {
match self.associated_body() {
Some(b) => b.node_id == node_id,
None => false,
}
}
}

View File

@ -31,7 +31,7 @@ pub use self::PathParameters::*;
use hir::def::Def;
use hir::def_id::DefId;
use util::nodemap::{NodeMap, FxHashMap, FxHashSet};
use util::nodemap::{NodeMap, FxHashSet};
use syntax_pos::{Span, ExpnId, DUMMY_SP};
use syntax::codemap::{self, Spanned};
@ -409,10 +409,15 @@ pub struct Crate {
pub trait_items: BTreeMap<TraitItemId, TraitItem>,
pub impl_items: BTreeMap<ImplItemId, ImplItem>,
pub bodies: FxHashMap<BodyId, Body>,
pub bodies: BTreeMap<BodyId, Body>,
pub trait_impls: BTreeMap<DefId, Vec<NodeId>>,
pub trait_default_impl: BTreeMap<DefId, NodeId>,
/// A list of the body ids written out in the order in which they
/// appear in the crate. If you're going to process all the bodies
/// in the crate, you should iterate over this list rather than the keys
/// of bodies.
pub body_ids: Vec<BodyId>,
}
impl Crate {

View File

@ -19,7 +19,7 @@ use ty::{self, TyCtxt, FreeRegion, Region};
use ty::wf::ImpliedBound;
use rustc_data_structures::transitive_relation::TransitiveRelation;
#[derive(Clone)]
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub struct FreeRegionMap {
// Stores the relation `a < b`, where `a` and `b` are regions.
relation: TransitiveRelation<Region>
@ -30,6 +30,10 @@ impl FreeRegionMap {
FreeRegionMap { relation: TransitiveRelation::new() }
}
pub fn is_empty(&self) -> bool {
self.relation.is_empty()
}
pub fn relate_free_regions_from_implied_bounds<'tcx>(&mut self,
implied_bounds: &[ImpliedBound<'tcx>])
{

View File

@ -248,6 +248,11 @@ pub struct TypeckTables<'tcx> {
/// If any errors occurred while type-checking this body,
/// this field will be set to `true`.
pub tainted_by_errors: bool,
/// Stores the free-region relationships that were deduced from
/// its where clauses and parameter types. These are then
/// read-again by borrowck.
pub free_region_map: FreeRegionMap,
}
impl<'tcx> TypeckTables<'tcx> {
@ -267,6 +272,7 @@ impl<'tcx> TypeckTables<'tcx> {
lints: lint::LintTable::new(),
used_trait_imports: DefIdSet(),
tainted_by_errors: false,
free_region_map: FreeRegionMap::new(),
}
}
@ -414,13 +420,6 @@ pub struct GlobalCtxt<'tcx> {
pub region_maps: RegionMaps,
// For each fn declared in the local crate, type check stores the
// free-region relationships that were deduced from its where
// clauses and parameter types. These are then read-again by
// borrowck. (They are not used during trans, and hence are not
// serialized or needed for cross-crate fns.)
free_region_maps: RefCell<NodeMap<FreeRegionMap>>,
pub hir: hir_map::Map<'tcx>,
pub maps: maps::Maps<'tcx>,
@ -645,16 +644,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
interned
}
pub fn store_free_region_map(self, id: NodeId, map: FreeRegionMap) {
if self.free_region_maps.borrow_mut().insert(id, map).is_some() {
bug!("Tried to overwrite interned FreeRegionMap for NodeId {:?}", id)
}
}
pub fn free_region_map(self, id: NodeId) -> FreeRegionMap {
self.free_region_maps.borrow()[&id].clone()
}
pub fn lift<T: ?Sized + Lift<'tcx>>(self, value: &T) -> Option<T::Lifted> {
value.lift_to_tcx(self)
}
@ -707,7 +696,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
types: common_types,
named_region_map: named_region_map,
region_maps: region_maps,
free_region_maps: RefCell::new(FxHashMap()),
variance_computed: Cell::new(false),
trait_map: resolutions.trait_map,
fulfilled_predicates: RefCell::new(fulfilled_predicates),

View File

@ -180,7 +180,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
data @ DefPathData::MacroDef(..) |
data @ DefPathData::ClosureExpr |
data @ DefPathData::Binding(..) |
data @ DefPathData::ImplTrait => {
data @ DefPathData::ImplTrait |
data @ DefPathData::Typeof => {
let parent_def_id = self.parent_def_id(def_id).unwrap();
self.push_item_path(buffer, parent_def_id);
buffer.push(&data.as_interned_str());

View File

@ -2602,6 +2602,17 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
dep_graph::visit_all_item_likes_in_krate(self.global_tcx(), dep_node_fn, visitor);
}
/// Invokes `callback` for each body in the krate. This will
/// create a read edge from `DepNode::Krate` to the current task;
/// it is meant to be run in the context of some global task like
/// `BorrowckCrate`. The callback would then create a task like
/// `BorrowckBody(DefId)` to process each individual item.
pub fn visit_all_bodies_in_krate<C>(self, callback: C)
where C: Fn(/* body_owner */ DefId, /* body id */ hir::BodyId),
{
dep_graph::visit_all_bodies_in_krate(self.global_tcx(), callback)
}
/// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err`
/// with the name of the crate containing the impl.
pub fn span_of_impl(self, impl_did: DefId) -> Result<Span, Symbol> {

View File

@ -27,7 +27,7 @@ use rustc::middle::mem_categorization as mc;
use std::mem;
use std::rc::Rc;
use syntax::ast;
use syntax_pos::{Span, DUMMY_SP};
use syntax_pos::DUMMY_SP;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum Fragment {
@ -200,7 +200,6 @@ impl FragmentSets {
pub fn instrument_move_fragments<'a, 'tcx>(this: &MoveData<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
sp: Span,
id: ast::NodeId) {
let span_err = tcx.hir.attrs(id).iter()
.any(|a| a.check_name("rustc_move_fragments"));
@ -208,6 +207,8 @@ pub fn instrument_move_fragments<'a, 'tcx>(this: &MoveData<'tcx>,
if !span_err && !print { return; }
let sp = tcx.hir.span(id);
let instrument_all_paths = |kind, vec_rc: &Vec<MovePathIndex>| {
for (i, mpi) in vec_rc.iter().enumerate() {
let lp = || this.path_loan_path(*mpi);

View File

@ -28,9 +28,6 @@ use rustc::ty::{self, TyCtxt};
use syntax::ast;
use syntax_pos::Span;
use rustc::hir;
use rustc::hir::Expr;
use rustc::hir::intravisit;
use rustc::hir::intravisit::{Visitor, NestedVisitorMap};
use self::restrictions::RestrictionResult;
@ -514,47 +511,3 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
}
}
/// Context used while gathering loans on static initializers
///
/// This visitor walks static initializer's expressions and makes
/// sure the loans being taken are sound.
struct StaticInitializerCtxt<'a, 'tcx: 'a> {
bccx: &'a BorrowckCtxt<'a, 'tcx>,
body_id: hir::BodyId,
}
impl<'a, 'tcx> Visitor<'tcx> for StaticInitializerCtxt<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
fn visit_expr(&mut self, ex: &'tcx Expr) {
if let hir::ExprAddrOf(mutbl, ref base) = ex.node {
let infcx = self.bccx.tcx.borrowck_fake_infer_ctxt(self.body_id);
let mc = mc::MemCategorizationContext::new(&infcx);
let base_cmt = mc.cat_expr(&base).unwrap();
let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);
// Check that we don't allow borrows of unsafe static items.
let err = check_aliasability(self.bccx, ex.span,
BorrowViolation(euv::AddrOf),
base_cmt, borrow_kind).is_err();
if err {
return; // reported an error, no sense in reporting more.
}
}
intravisit::walk_expr(self, ex);
}
}
pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, body: hir::BodyId) {
debug!("gather_loans_in_static_initializer(expr={:?})", body);
let mut sicx = StaticInitializerCtxt {
bccx: bccx,
body_id: body
};
let body = sicx.bccx.tcx.hir.body(body);
sicx.visit_body(body);
}

View File

@ -32,14 +32,12 @@ use rustc::middle::dataflow::DataFlowOperator;
use rustc::middle::dataflow::KillFrom;
use rustc::hir::def_id::DefId;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::free_region::FreeRegionMap;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::region;
use rustc::ty::{self, TyCtxt};
use std::fmt;
use std::mem;
use std::rc::Rc;
use std::hash::{Hash, Hasher};
use syntax::ast;
@ -47,7 +45,7 @@ use syntax_pos::{MultiSpan, Span};
use errors::DiagnosticBuilder;
use rustc::hir;
use rustc::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
use rustc::hir::intravisit::{self, Visitor};
pub mod check_loans;
@ -62,93 +60,14 @@ pub struct LoanDataFlowOperator;
pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
impl<'a, 'tcx> Visitor<'tcx> for BorrowckCtxt<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir)
}
fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx hir::FnDecl,
b: hir::BodyId, s: Span, id: ast::NodeId) {
match fk {
FnKind::ItemFn(..) |
FnKind::Method(..) => {
self.with_temp_region_map(id, |this| {
borrowck_fn(this, fk, fd, b, s, id, fk.attrs())
});
}
FnKind::Closure(..) => {
borrowck_fn(self, fk, fd, b, s, id, fk.attrs());
}
}
}
fn visit_item(&mut self, item: &'tcx hir::Item) {
borrowck_item(self, item);
}
fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) {
if let hir::TraitItemKind::Const(_, Some(expr)) = ti.node {
gather_loans::gather_loans_in_static_initializer(self, expr);
}
intravisit::walk_trait_item(self, ti);
}
fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) {
if let hir::ImplItemKind::Const(_, expr) = ii.node {
gather_loans::gather_loans_in_static_initializer(self, expr);
}
intravisit::walk_impl_item(self, ii);
}
}
pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
let mut bccx = BorrowckCtxt {
tcx: tcx,
free_region_map: FreeRegionMap::new(),
stats: BorrowStats {
loaned_paths_same: 0,
loaned_paths_imm: 0,
stable_paths: 0,
guaranteed_paths: 0
}
};
tcx.visit_all_item_likes_in_krate(DepNode::BorrowCheck, &mut bccx.as_deep_visitor());
if tcx.sess.borrowck_stats() {
println!("--- borrowck stats ---");
println!("paths requiring guarantees: {}",
bccx.stats.guaranteed_paths);
println!("paths requiring loans : {}",
make_stat(&bccx, bccx.stats.loaned_paths_same));
println!("paths requiring imm loans : {}",
make_stat(&bccx, bccx.stats.loaned_paths_imm));
println!("stable paths : {}",
make_stat(&bccx, bccx.stats.stable_paths));
}
fn make_stat(bccx: &BorrowckCtxt, stat: usize) -> String {
let total = bccx.stats.guaranteed_paths as f64;
let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total };
format!("{} ({:.0}%)", stat, perc)
}
}
fn borrowck_item<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, item: &'tcx hir::Item) {
// Gather loans for items. Note that we don't need
// to check loans for single expressions. The check
// loan step is intended for things that have a data
// flow dependent conditions.
match item.node {
hir::ItemStatic(.., ex) |
hir::ItemConst(_, ex) => {
gather_loans::gather_loans_in_static_initializer(this, ex);
}
_ => { }
}
intravisit::walk_item(this, item);
tcx.dep_graph.with_task(DepNode::BorrowCheckKrate, || {
tcx.visit_all_bodies_in_krate(|body_owner_def_id, body_id| {
tcx.dep_graph.with_task(DepNode::BorrowCheck(body_owner_def_id), || {
borrowck_fn(tcx, body_id);
});
});
});
}
/// Collection of conclusions determined via borrow checker analyses.
@ -158,40 +77,39 @@ pub struct AnalysisData<'a, 'tcx: 'a> {
pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
}
fn borrowck_fn<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
fk: FnKind<'tcx>,
decl: &'tcx hir::FnDecl,
body_id: hir::BodyId,
sp: Span,
id: ast::NodeId,
attributes: &[ast::Attribute]) {
debug!("borrowck_fn(id={})", id);
fn borrowck_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, body_id: hir::BodyId) {
debug!("borrowck_fn(body_id={:?})", body_id);
let body = this.tcx.hir.body(body_id);
let owner_id = tcx.hir.body_owner(body_id);
let owner_def_id = tcx.hir.local_def_id(owner_id);
let attributes = tcx.get_attrs(owner_def_id);
let tables = tcx.item_tables(owner_def_id);
if attributes.iter().any(|item| item.check_name("rustc_mir_borrowck")) {
this.with_temp_region_map(id, |this| {
mir::borrowck_mir(this, id, attributes)
});
let mut bccx = &mut BorrowckCtxt {
tcx: tcx,
tables: tables,
};
let body = bccx.tcx.hir.body(body_id);
if bccx.tcx.has_attr(owner_def_id, "rustc_mir_borrowck") {
mir::borrowck_mir(bccx, owner_id, &attributes);
}
let cfg = cfg::CFG::new(this.tcx, &body.value);
let cfg = cfg::CFG::new(bccx.tcx, &body);
let AnalysisData { all_loans,
loans: loan_dfcx,
move_data: flowed_moves } =
build_borrowck_dataflow_data(this, &cfg, body_id);
build_borrowck_dataflow_data(bccx, &cfg, body_id);
move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
this.tcx,
sp,
id);
move_data::fragments::build_unfragmented_map(this,
bccx.tcx,
owner_id);
move_data::fragments::build_unfragmented_map(bccx,
&flowed_moves.move_data,
id);
owner_id);
check_loans::check_loans(this, &loan_dfcx, &flowed_moves, &all_loans[..], body);
intravisit::walk_fn(this, fk, decl, body_id, sp, id);
check_loans::check_loans(bccx, &loan_dfcx, &flowed_moves, &all_loans[..], body);
}
fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
@ -241,23 +159,20 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
/// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
body: hir::BodyId,
body_id: hir::BodyId,
cfg: &cfg::CFG)
-> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>)
{
let owner_id = tcx.hir.body_owner(body_id);
let owner_def_id = tcx.hir.local_def_id(owner_id);
let tables = tcx.item_tables(owner_def_id);
let mut bccx = BorrowckCtxt {
tcx: tcx,
free_region_map: FreeRegionMap::new(),
stats: BorrowStats {
loaned_paths_same: 0,
loaned_paths_imm: 0,
stable_paths: 0,
guaranteed_paths: 0
}
tables: tables,
};
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, cfg, body);
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, cfg, body_id);
(bccx, dataflow_data)
}
@ -267,28 +182,9 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
pub struct BorrowckCtxt<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Hacky. As we visit various fns, we have to load up the
// free-region map for each one. This map is computed by during
// typeck for each fn item and stored -- closures just use the map
// from the fn item that encloses them. Since we walk the fns in
// order, we basically just overwrite this field as we enter a fn
// item and restore it afterwards in a stack-like fashion. Then
// the borrow checking code can assume that `free_region_map` is
// always the correct map for the current fn. Feels like it'd be
// better to just recompute this, rather than store it, but it's a
// bit of a pain to factor that code out at the moment.
free_region_map: FreeRegionMap,
// Statistics:
stats: BorrowStats
}
#[derive(Clone)]
struct BorrowStats {
loaned_paths_same: usize,
loaned_paths_imm: usize,
stable_paths: usize,
guaranteed_paths: usize
// tables for the current thing we are checking; set to
// Some in `borrowck_fn` and cleared later
tables: &'a ty::TypeckTables<'tcx>,
}
///////////////////////////////////////////////////////////////////////////
@ -574,19 +470,12 @@ pub enum MovedValueUseKind {
// Misc
impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
fn with_temp_region_map<F>(&mut self, id: ast::NodeId, f: F)
where F: for <'b> FnOnce(&'b mut BorrowckCtxt<'a, 'tcx>)
{
let new_free_region_map = self.tcx.free_region_map(id);
let old_free_region_map = mem::replace(&mut self.free_region_map, new_free_region_map);
f(self);
self.free_region_map = old_free_region_map;
}
pub fn is_subregion_of(&self, r_sub: &'tcx ty::Region, r_sup: &'tcx ty::Region)
pub fn is_subregion_of(&self,
r_sub: &'tcx ty::Region,
r_sup: &'tcx ty::Region)
-> bool
{
self.free_region_map.is_subregion_of(self.tcx, r_sub, r_sup)
self.tables.free_region_map.is_subregion_of(self.tcx, r_sub, r_sup)
}
pub fn report(&self, err: BckError<'tcx>) {
@ -912,11 +801,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
}
mc::AliasableStatic |
mc::AliasableStaticMut => {
let mut err = struct_span_err!(
self.tcx.sess, span, E0388,
"{} in a static location", prefix);
err.span_label(span, &format!("cannot write data in a static definition"));
err
// This path cannot occur. It happens when we have an
// `&mut` or assignment to a static. But in the case
// of `static X`, we get a mutability violation first,
// and never get here. In the case of `static mut X`,
// that is unsafe and hence the aliasability error is
// ignored.
span_bug!(span, "aliasability violation for static `{}`", prefix)
}
mc::AliasableBorrowed => {
let mut e = struct_span_err!(

View File

@ -287,27 +287,7 @@ https://doc.rust-lang.org/std/cell/
"##,
E0388: r##"
A mutable borrow was attempted in a static location.
Erroneous code example:
```compile_fail,E0388
static X: i32 = 1;
static STATIC_REF: &'static mut i32 = &mut X;
// error: cannot borrow data mutably in a static location
const CONST_REF: &'static mut i32 = &mut X;
// error: cannot borrow data mutably in a static location
```
To fix this error, you have to use constant borrow:
```
static X: i32 = 1;
static STATIC_REF: &'static i32 = &X;
```
E0388 was removed and is no longer issued.
"##,
E0389: r##"

View File

@ -28,6 +28,7 @@
#![feature(shared)]
#![feature(collections_range)]
#![feature(collections_bound)]
#![cfg_attr(stage0,feature(field_init_shorthand))]
#![feature(nonzero)]
#![feature(rustc_private)]
#![feature(staged_api)]

View File

@ -9,6 +9,7 @@
// except according to those terms.
use bitvec::BitMatrix;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
use std::cell::RefCell;
use std::fmt::Debug;
use std::mem;
@ -36,10 +37,10 @@ pub struct TransitiveRelation<T: Debug + PartialEq> {
closure: RefCell<Option<BitMatrix>>,
}
#[derive(Clone, PartialEq, PartialOrd)]
#[derive(Clone, PartialEq, PartialOrd, RustcEncodable, RustcDecodable)]
struct Index(usize);
#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
struct Edge {
source: Index,
target: Index,
@ -54,6 +55,10 @@ impl<T: Debug + PartialEq> TransitiveRelation<T> {
}
}
pub fn is_empty(&self) -> bool {
self.edges.is_empty()
}
fn index(&self, a: &T) -> Option<Index> {
self.elements.iter().position(|e| *e == *a).map(Index)
}
@ -305,6 +310,30 @@ fn pare_down(candidates: &mut Vec<usize>, closure: &BitMatrix) {
}
}
impl<T> Encodable for TransitiveRelation<T>
where T: Encodable + Debug + PartialEq
{
fn encode<E: Encoder>(&self, s: &mut E) -> Result<(), E::Error> {
s.emit_struct("TransitiveRelation", 2, |s| {
s.emit_struct_field("elements", 0, |s| self.elements.encode(s))?;
s.emit_struct_field("edges", 1, |s| self.edges.encode(s))?;
Ok(())
})
}
}
impl<T> Decodable for TransitiveRelation<T>
where T: Decodable + Debug + PartialEq
{
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
d.read_struct("TransitiveRelation", 2, |d| {
let elements = d.read_struct_field("elements", 0, |d| Decodable::decode(d))?;
let edges = d.read_struct_field("edges", 1, |d| Decodable::decode(d))?;
Ok(TransitiveRelation { elements, edges, closure: RefCell::new(None) })
})
}
}
#[test]
fn test_one_step() {
let mut relation = TransitiveRelation::new();

View File

@ -24,6 +24,7 @@
#![deny(warnings)]
#![feature(box_syntax)]
#![feature(loop_break_value)]
#![feature(libc)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]

View File

@ -718,13 +718,24 @@ fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec<borrowck_dot::Variant>,
mode: PpFlowGraphMode,
mut out: W)
-> io::Result<()> {
let cfg = match code {
blocks::Code::Expr(expr) => cfg::CFG::new(tcx, expr),
blocks::Code::FnLike(fn_like) => {
let body = tcx.hir.body(fn_like.body());
cfg::CFG::new(tcx, &body.value)
},
let body_id = match code {
blocks::Code::Expr(expr) => {
// Find the function this expression is from.
let mut node_id = expr.id;
loop {
let node = tcx.hir.get(node_id);
if let Some(n) = hir::map::blocks::FnLikeNode::from_node(node) {
break n.body();
}
let parent = tcx.hir.get_parent_node(node_id);
assert!(node_id != parent);
node_id = parent;
}
}
blocks::Code::FnLike(fn_like) => fn_like.body(),
};
let body = tcx.hir.body(body_id);
let cfg = cfg::CFG::new(tcx, &body);
let labelled_edges = mode != PpFlowGraphMode::UnlabelledEdges;
let lcfg = LabelledCFG {
hir_map: &tcx.hir,

View File

@ -1143,9 +1143,9 @@ impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> {
trait_items: _,
impl_items: _,
bodies: _,
trait_impls: _,
trait_default_impl: _,
body_ids: _,
} = *krate;
visit::Visitor::visit_mod(self, module, span, ast::CRATE_NODE_ID);

View File

@ -712,7 +712,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
// to have behaviour like the above, rather than
// e.g. accidentally recurring after an assert.
let cfg = cfg::CFG::new(cx.tcx, &body.value);
let cfg = cfg::CFG::new(cx.tcx, &body);
let mut work_queue = vec![cfg.entry];
let mut reached_exit_without_self_call = false;

View File

@ -30,7 +30,6 @@ use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::maps::Providers;
use rustc::ty::subst::Substs;
use rustc::hir;
use rustc::hir::intravisit::{Visitor, NestedVisitorMap};
use syntax::abi::Abi;
use syntax::ast;
use syntax_pos::Span;
@ -39,9 +38,11 @@ use std::cell::RefCell;
use std::mem;
pub fn build_mir_for_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
tcx.visit_all_item_likes_in_krate(DepNode::Mir, &mut BuildMir {
tcx: tcx
}.as_deep_visitor());
tcx.dep_graph.with_task(DepNode::MirKrate, || {
tcx.visit_all_bodies_in_krate(|body_owner_def_id, _body_id| {
tcx.item_mir(body_owner_def_id);
});
});
}
pub fn provide(providers: &mut Providers) {
@ -180,23 +181,6 @@ impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> {
///////////////////////////////////////////////////////////////////////////
// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
struct BuildMir<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>
}
impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
fn visit_nested_body(&mut self, body_id: hir::BodyId) {
self.tcx.item_mir(self.tcx.hir.body_owner_def_id(body_id));
let body = self.tcx.hir.body(body_id);
self.visit_body(body);
}
}
fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
closure_expr_id: ast::NodeId,
body_id: hir::BodyId)

View File

@ -515,76 +515,13 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> {
}
struct CheckItemTypesVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> }
struct CheckItemBodiesVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> }
impl<'a, 'tcx> Visitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir)
}
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> {
fn visit_item(&mut self, i: &'tcx hir::Item) {
check_item_type(self.tcx, i);
intravisit::walk_item(self, i);
}
fn visit_ty(&mut self, t: &'tcx hir::Ty) {
match t.node {
hir::TyArray(_, length) => {
self.tcx.item_tables(self.tcx.hir.local_def_id(length.node_id));
}
_ => {}
}
intravisit::walk_ty(self, t);
}
fn visit_expr(&mut self, e: &'tcx hir::Expr) {
match e.node {
hir::ExprRepeat(_, count) => {
self.tcx.item_tables(self.tcx.hir.local_def_id(count.node_id));
}
_ => {}
}
intravisit::walk_expr(self, e);
}
}
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item) {
match item.node {
hir::ItemFn(..) => {
self.tcx.item_tables(self.tcx.hir.local_def_id(item.id));
}
_ => { }
}
}
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
match trait_item.node {
hir::TraitItemKind::Const(_, Some(_)) |
hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => {
self.tcx.item_tables(self.tcx.hir.local_def_id(trait_item.id));
}
hir::TraitItemKind::Method(_, hir::TraitMethod::Required(_)) |
hir::TraitItemKind::Const(_, None) |
hir::TraitItemKind::Type(..) => {
// Nothing to do.
}
}
}
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
match impl_item.node {
hir::ImplItemKind::Const(..) |
hir::ImplItemKind::Method(..) => {
self.tcx.item_tables(self.tcx.hir.local_def_id(impl_item.id));
}
hir::ImplItemKind::Type(_) => {
// Nothing to do here.
}
}
}
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) { }
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) { }
}
pub fn check_wf_new<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult {
@ -596,16 +533,18 @@ pub fn check_wf_new<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult {
pub fn check_item_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult {
tcx.sess.track_errors(|| {
let mut visit = CheckItemTypesVisitor { tcx: tcx };
tcx.visit_all_item_likes_in_krate(DepNode::TypeckItemType,
&mut visit.as_deep_visitor());
&mut CheckItemTypesVisitor { tcx });
})
}
pub fn check_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult {
tcx.sess.track_errors(|| {
let mut visit = CheckItemBodiesVisitor { tcx: tcx };
tcx.visit_all_item_likes_in_krate(DepNode::TypeckTables, &mut visit);
tcx.dep_graph.with_task(DepNode::TypeckBodiesKrate, || {
tcx.visit_all_bodies_in_krate(|body_owner_def_id, _body_id| {
tcx.item_tables(body_owner_def_id);
});
});
})
}

View File

@ -120,6 +120,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
rcx.visit_region_obligations(id);
}
rcx.resolve_regions_and_report_errors();
assert!(self.tables.borrow().free_region_map.is_empty());
self.tables.borrow_mut().free_region_map = rcx.free_region_map;
}
/// Region checking during the WF phase for items. `wf_tys` are the
@ -154,10 +157,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
rcx.resolve_regions_and_report_errors();
// For the top-level fn, store the free-region-map. We don't store
// any map for closures; they just share the same map as the
// function that created them.
self.tcx.store_free_region_map(fn_id, rcx.free_region_map);
// In this mode, we also copy the free-region-map into the
// tables of the enclosing fcx. In the other regionck modes
// (e.g., `regionck_item`), we don't have an enclosing tables.
assert!(self.tables.borrow().free_region_map.is_empty());
self.tables.borrow_mut().free_region_map = rcx.free_region_map;
}
}

View File

@ -50,6 +50,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
wbcx.visit_type_nodes();
wbcx.visit_cast_types();
wbcx.visit_lints();
wbcx.visit_free_region_map();
let used_trait_imports = mem::replace(&mut self.tables.borrow_mut().used_trait_imports,
DefIdSet());
@ -274,6 +275,10 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
self.fcx.tables.borrow_mut().lints.transfer(&mut self.tables.lints);
}
fn visit_free_region_map(&mut self) {
self.tables.free_region_map = self.fcx.tables.borrow().free_region_map.clone();
}
fn visit_anon_types(&mut self) {
let gcx = self.tcx().global_tcx();
for (&node_id, &concrete_ty) in self.fcx.anon_types.borrow().iter() {

View File

@ -1147,6 +1147,7 @@ fn ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
NodeExpr(_) => match tcx.hir.get(tcx.hir.get_parent_node(node_id)) {
NodeTy(&hir::Ty { node: TyArray(_, body), .. }) |
NodeTy(&hir::Ty { node: TyTypeof(body), .. }) |
NodeExpr(&hir::Expr { node: ExprRepeat(_, body), .. })
if body.node_id == node_id => tcx.types.usize,

View File

@ -19,8 +19,7 @@ static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
//~| NOTE statics require immutable values
//~| ERROR E0017
//~| NOTE statics require immutable values
//~| ERROR E0388
//~| NOTE cannot write data in a static definition
//~| ERROR cannot borrow
static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017
//~| NOTE statics require immutable values
//~| ERROR E0017

View File

@ -15,7 +15,7 @@ const CR: &'static mut i32 = &mut C; //~ ERROR E0017
//~| ERROR E0017
static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
//~| ERROR E0017
//~| ERROR E0388
//~| ERROR cannot borrow
static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017
//~| ERROR E0017

View File

@ -0,0 +1,28 @@
// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Regression test for #38520. Check that moves of `Foo` are not
// permitted as `Foo` is not copy (even in a static/const
// initializer).
#![feature(const_fn)]
struct Foo(usize);
const fn get(x: Foo) -> usize {
x.0
}
const X: Foo = Foo(22);
static Y: usize = get(*&X); //~ ERROR E0507
const Z: usize = get(*&X); //~ ERROR E0507
fn main() {
}

View File

@ -13,6 +13,6 @@ pub fn main() {
//~^ ERROR blocks in constants are limited to items and tail expressions
let p = 3;
//~^ ERROR blocks in constants are limited to items and tail expressions
&p
&p //~ ERROR `p` does not live long enough
};
}

View File

@ -1,11 +1,3 @@
error: cannot borrow immutable borrowed content `*a` as mutable
--> $DIR/mut-arg-hint.rs:18:5
|
17 | pub fn foo<'a>(mut a: &'a String) {
| ---------- use `&'a mut String` here to make mutable
18 | a.push_str("foo");
| ^ cannot borrow as mutable
error: cannot borrow immutable borrowed content `*a` as mutable
--> $DIR/mut-arg-hint.rs:13:9
|
@ -14,6 +6,14 @@ error: cannot borrow immutable borrowed content `*a` as mutable
13 | a.push_str("bar");
| ^ cannot borrow as mutable
error: cannot borrow immutable borrowed content `*a` as mutable
--> $DIR/mut-arg-hint.rs:18:5
|
17 | pub fn foo<'a>(mut a: &'a String) {
| ---------- use `&'a mut String` here to make mutable
18 | a.push_str("foo");
| ^ cannot borrow as mutable
error: cannot borrow immutable borrowed content `*a` as mutable
--> $DIR/mut-arg-hint.rs:25:9
|