mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
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:
commit
06c63f6e9e
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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>])
|
||||
{
|
||||
|
@ -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),
|
||||
|
@ -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());
|
||||
|
@ -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> {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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!(
|
||||
|
@ -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##"
|
||||
|
@ -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)]
|
||||
|
@ -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();
|
||||
|
@ -24,6 +24,7 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
#![feature(box_syntax)]
|
||||
#![feature(loop_break_value)]
|
||||
#![feature(libc)]
|
||||
#![feature(quote)]
|
||||
#![feature(rustc_diagnostic_macros)]
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() {
|
||||
}
|
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
|
|
||||
|
Loading…
Reference in New Issue
Block a user