Skip to content

Commit 35e8924

Browse files
authored
Auto merge of #37660 - nikomatsakis:incremental-36349, r=eddyb
Separate impl items from the parent impl This change separates impl item bodies out of the impl itself. This gives incremental more resolution. In so doing, it refactors how the visitors work, and cleans up a bit of the collect/check logic (mostly by moving things out of collect that didn't really belong there, because they were just checking conditions). However, this is not as effective as I expected, for a kind of frustrating reason. In particular, when invoking `foo.bar()` you still wind up with dependencies on private items. The problem is that the method resolution code scans that list for methods with the name `bar` -- and this winds up touching *all* the methods, even private ones. I can imagine two obvious ways to fix this: - separating fn bodies from fn sigs (#35078, currently being pursued by @flodiebold) - a more aggressive model of incremental that @michaelwoerister has been advocating, in which we hash the intermediate results (e.g., the outputs of collect) so that we can see that the intermediate result hasn't changed, even if a particular impl item has changed. So all in all I'm not quite sure whether to land this or not. =) It still seems like it has to be a win in some cases, but not with the test cases we have just now. I can try to gin up some test cases, but I'm not sure if they will be totally realistic. On the other hand, some of the early refactorings to the visitor trait seem worthwhile to me regardless. cc #36349 -- well, this is basically a fix for that issue, I guess r? @michaelwoerister NB: Based atop of @eddyb's PR #37402; don't land until that lands.
2 parents c356537 + c938007 commit 35e8924

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1765
-630
lines changed

src/librustc/dep_graph/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ pub use self::dep_node::WorkProductId;
2525
pub use self::graph::DepGraph;
2626
pub use self::graph::WorkProduct;
2727
pub use self::query::DepGraphQuery;
28-
pub use self::visit::visit_all_items_in_krate;
28+
pub use self::visit::visit_all_item_likes_in_krate;
2929
pub use self::raii::DepTask;

src/librustc/dep_graph/visit.rs

+19-9
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,30 @@
1010

1111
use hir;
1212
use hir::def_id::DefId;
13-
use hir::intravisit::Visitor;
13+
use hir::itemlikevisit::ItemLikeVisitor;
1414
use ty::TyCtxt;
1515

1616
use super::dep_node::DepNode;
1717

18-
1918
/// Visit all the items in the krate in some order. When visiting a
2019
/// particular item, first create a dep-node by calling `dep_node_fn`
2120
/// and push that onto the dep-graph stack of tasks, and also create a
2221
/// read edge from the corresponding AST node. This is used in
2322
/// compiler passes to automatically record the item that they are
2423
/// working on.
25-
pub fn visit_all_items_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
26-
mut dep_node_fn: F,
27-
visitor: &mut V)
28-
where F: FnMut(DefId) -> DepNode<DefId>, V: Visitor<'tcx>
24+
pub fn visit_all_item_likes_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
25+
mut dep_node_fn: F,
26+
visitor: &mut V)
27+
where F: FnMut(DefId) -> DepNode<DefId>, V: ItemLikeVisitor<'tcx>
2928
{
3029
struct TrackingVisitor<'visit, 'tcx: 'visit, F: 'visit, V: 'visit> {
3130
tcx: TyCtxt<'visit, 'tcx, 'tcx>,
3231
dep_node_fn: &'visit mut F,
3332
visitor: &'visit mut V
3433
}
3534

36-
impl<'visit, 'tcx, F, V> Visitor<'tcx> for TrackingVisitor<'visit, 'tcx, F, V>
37-
where F: FnMut(DefId) -> DepNode<DefId>, V: Visitor<'tcx>
35+
impl<'visit, 'tcx, F, V> ItemLikeVisitor<'tcx> for TrackingVisitor<'visit, 'tcx, F, V>
36+
where F: FnMut(DefId) -> DepNode<DefId>, V: ItemLikeVisitor<'tcx>
3837
{
3938
fn visit_item(&mut self, i: &'tcx hir::Item) {
4039
let item_def_id = self.tcx.map.local_def_id(i.id);
@@ -46,6 +45,17 @@ pub fn visit_all_items_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
4645
self.visitor.visit_item(i);
4746
debug!("Ended task {:?}", task_id);
4847
}
48+
49+
fn visit_impl_item(&mut self, i: &'tcx hir::ImplItem) {
50+
let impl_item_def_id = self.tcx.map.local_def_id(i.id);
51+
let task_id = (self.dep_node_fn)(impl_item_def_id);
52+
let _task = self.tcx.dep_graph.in_task(task_id.clone());
53+
debug!("Started task {:?}", task_id);
54+
assert!(!self.tcx.map.is_inlined_def_id(impl_item_def_id));
55+
self.tcx.dep_graph.read(DepNode::Hir(impl_item_def_id));
56+
self.visitor.visit_impl_item(i);
57+
debug!("Ended task {:?}", task_id);
58+
}
4959
}
5060

5161
let krate = tcx.dep_graph.with_ignore(|| tcx.map.krate());
@@ -54,5 +64,5 @@ pub fn visit_all_items_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
5464
dep_node_fn: &mut dep_node_fn,
5565
visitor: visitor
5666
};
57-
krate.visit_all_items(&mut tracking_visitor)
67+
krate.visit_all_item_likes(&mut tracking_visitor)
5868
}

src/librustc/hir/def.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub type DefMap = NodeMap<PathResolution>;
106106
// within.
107107
pub type ExportMap = NodeMap<Vec<Export>>;
108108

109-
#[derive(Copy, Clone, RustcEncodable, RustcDecodable)]
109+
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
110110
pub struct Export {
111111
pub name: ast::Name, // The name of the target.
112112
pub def: Def, // The definition of the target.

src/librustc/hir/intravisit.rs

+110-14
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
//! HIR walker. Each overridden visit method has full control over what
11+
//! HIR walker for walking the contents of nodes.
12+
//!
13+
//! **For an overview of the visitor strategy, see the docs on the
14+
//! `super::itemlikevisit::ItemLikeVisitor` trait.**
15+
//!
16+
//! If you have decided to use this visitor, here are some general
17+
//! notes on how to do it:
18+
//!
19+
//! Each overridden visit method has full control over what
1220
//! happens with its node, it can do its own traversal of the node's children,
1321
//! call `intravisit::walk_*` to apply the default traversal algorithm, or prevent
1422
//! deeper traversal by doing nothing.
@@ -30,6 +38,8 @@ use syntax::ast::{NodeId, CRATE_NODE_ID, Name, Attribute};
3038
use syntax::codemap::Spanned;
3139
use syntax_pos::Span;
3240
use hir::*;
41+
use hir::map::Map;
42+
use super::itemlikevisit::DeepVisitor;
3343

3444
use std::cmp;
3545
use std::u32;
@@ -76,22 +86,70 @@ pub trait Visitor<'v> : Sized {
7686
///////////////////////////////////////////////////////////////////////////
7787
// Nested items.
7888

79-
/// Invoked when a nested item is encountered. By default, does
80-
/// nothing. If you want a deep walk, you need to override to
81-
/// fetch the item contents. But most of the time, it is easier
82-
/// (and better) to invoke `Crate::visit_all_items`, which visits
83-
/// all items in the crate in some order (but doesn't respect
84-
/// nesting).
89+
/// The default versions of the `visit_nested_XXX` routines invoke
90+
/// this method to get a map to use; if they get back `None`, they
91+
/// just skip nested things. Otherwise, they will lookup the
92+
/// nested item-like things in the map and visit it. So the best
93+
/// way to implement a nested visitor is to override this method
94+
/// to return a `Map`; one advantage of this is that if we add
95+
/// more types of nested things in the future, they will
96+
/// automatically work.
97+
///
98+
/// **If for some reason you want the nested behavior, but don't
99+
/// have a `Map` are your disposal:** then you should override the
100+
/// `visit_nested_XXX` methods, and override this method to
101+
/// `panic!()`. This way, if a new `visit_nested_XXX` variant is
102+
/// added in the future, we will see the panic in your code and
103+
/// fix it appropriately.
104+
fn nested_visit_map(&mut self) -> Option<&Map<'v>> {
105+
None
106+
}
107+
108+
/// Invoked when a nested item is encountered. By default does
109+
/// nothing unless you override `nested_visit_map` to return
110+
/// `Some(_)`, in which case it will walk the item. **You probably
111+
/// don't want to override this method** -- instead, override
112+
/// `nested_visit_map` or use the "shallow" or "deep" visit
113+
/// patterns described on `itemlikevisit::ItemLikeVisitor`. The only
114+
/// reason to override this method is if you want a nested pattern
115+
/// but cannot supply a `Map`; see `nested_visit_map` for advice.
85116
#[allow(unused_variables)]
86117
fn visit_nested_item(&mut self, id: ItemId) {
118+
let opt_item = self.nested_visit_map()
119+
.map(|map| map.expect_item(id.id));
120+
if let Some(item) = opt_item {
121+
self.visit_item(item);
122+
}
87123
}
88124

89-
/// Visit the top-level item and (optionally) nested items. See
125+
/// Like `visit_nested_item()`, but for impl items. See
126+
/// `visit_nested_item()` for advice on when to override this
127+
/// method.
128+
#[allow(unused_variables)]
129+
fn visit_nested_impl_item(&mut self, id: ImplItemId) {
130+
let opt_item = self.nested_visit_map()
131+
.map(|map| map.impl_item(id));
132+
if let Some(item) = opt_item {
133+
self.visit_impl_item(item);
134+
}
135+
}
136+
137+
/// Visit the top-level item and (optionally) nested items / impl items. See
90138
/// `visit_nested_item` for details.
91139
fn visit_item(&mut self, i: &'v Item) {
92140
walk_item(self, i)
93141
}
94142

143+
/// When invoking `visit_all_item_likes()`, you need to supply an
144+
/// item-like visitor. This method converts a "intra-visit"
145+
/// visitor into an item-like visitor that walks the entire tree.
146+
/// If you use this, you probably don't want to process the
147+
/// contents of nested item-like things, since the outer loop will
148+
/// visit them as well.
149+
fn as_deep_visitor<'s>(&'s mut self) -> DeepVisitor<'s, Self> {
150+
DeepVisitor::new(self)
151+
}
152+
95153
///////////////////////////////////////////////////////////////////////////
96154

97155
fn visit_id(&mut self, _node_id: NodeId) {
@@ -147,6 +205,9 @@ pub trait Visitor<'v> : Sized {
147205
fn visit_impl_item(&mut self, ii: &'v ImplItem) {
148206
walk_impl_item(self, ii)
149207
}
208+
fn visit_impl_item_ref(&mut self, ii: &'v ImplItemRef) {
209+
walk_impl_item_ref(self, ii)
210+
}
150211
fn visit_trait_ref(&mut self, t: &'v TraitRef) {
151212
walk_trait_ref(self, t)
152213
}
@@ -206,6 +267,12 @@ pub trait Visitor<'v> : Sized {
206267
fn visit_vis(&mut self, vis: &'v Visibility) {
207268
walk_vis(self, vis)
208269
}
270+
fn visit_associated_item_kind(&mut self, kind: &'v AssociatedItemKind) {
271+
walk_associated_item_kind(self, kind);
272+
}
273+
fn visit_defaultness(&mut self, defaultness: &'v Defaultness) {
274+
walk_defaultness(self, defaultness);
275+
}
209276
}
210277

211278
pub fn walk_opt_name<'v, V: Visitor<'v>>(visitor: &mut V, span: Span, opt_name: Option<Name>) {
@@ -341,12 +408,14 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
341408
visitor.visit_id(item.id);
342409
visitor.visit_trait_ref(trait_ref)
343410
}
344-
ItemImpl(.., ref type_parameters, ref opt_trait_reference, ref typ, ref impl_items) => {
411+
ItemImpl(.., ref type_parameters, ref opt_trait_reference, ref typ, ref impl_item_refs) => {
345412
visitor.visit_id(item.id);
346413
visitor.visit_generics(type_parameters);
347414
walk_list!(visitor, visit_trait_ref, opt_trait_reference);
348415
visitor.visit_ty(typ);
349-
walk_list!(visitor, visit_impl_item, impl_items);
416+
for impl_item_ref in impl_item_refs {
417+
visitor.visit_impl_item_ref(impl_item_ref);
418+
}
350419
}
351420
ItemStruct(ref struct_definition, ref generics) |
352421
ItemUnion(ref struct_definition, ref generics) => {
@@ -677,10 +746,14 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai
677746
}
678747

679748
pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem) {
680-
visitor.visit_vis(&impl_item.vis);
681-
visitor.visit_name(impl_item.span, impl_item.name);
682-
walk_list!(visitor, visit_attribute, &impl_item.attrs);
683-
match impl_item.node {
749+
// NB: Deliberately force a compilation error if/when new fields are added.
750+
let ImplItem { id: _, name, ref vis, ref defaultness, ref attrs, ref node, span } = *impl_item;
751+
752+
visitor.visit_name(span, name);
753+
visitor.visit_vis(vis);
754+
visitor.visit_defaultness(defaultness);
755+
walk_list!(visitor, visit_attribute, attrs);
756+
match *node {
684757
ImplItemKind::Const(ref ty, ref expr) => {
685758
visitor.visit_id(impl_item.id);
686759
visitor.visit_ty(ty);
@@ -703,6 +776,17 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt
703776
}
704777
}
705778

779+
pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, impl_item_ref: &'v ImplItemRef) {
780+
// NB: Deliberately force a compilation error if/when new fields are added.
781+
let ImplItemRef { id, name, ref kind, span, ref vis, ref defaultness } = *impl_item_ref;
782+
visitor.visit_nested_impl_item(id);
783+
visitor.visit_name(span, name);
784+
visitor.visit_associated_item_kind(kind);
785+
visitor.visit_vis(vis);
786+
visitor.visit_defaultness(defaultness);
787+
}
788+
789+
706790
pub fn walk_struct_def<'v, V: Visitor<'v>>(visitor: &mut V, struct_definition: &'v VariantData) {
707791
visitor.visit_id(struct_definition.id());
708792
walk_list!(visitor, visit_struct_field, struct_definition.fields());
@@ -872,6 +956,18 @@ pub fn walk_vis<'v, V: Visitor<'v>>(visitor: &mut V, vis: &'v Visibility) {
872956
}
873957
}
874958

959+
pub fn walk_associated_item_kind<'v, V: Visitor<'v>>(_: &mut V, _: &'v AssociatedItemKind) {
960+
// No visitable content here: this fn exists so you can call it if
961+
// the right thing to do, should content be added in the future,
962+
// would be to walk it.
963+
}
964+
965+
pub fn walk_defaultness<'v, V: Visitor<'v>>(_: &mut V, _: &'v Defaultness) {
966+
// No visitable content here: this fn exists so you can call it if
967+
// the right thing to do, should content be added in the future,
968+
// would be to walk it.
969+
}
970+
875971
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq)]
876972
pub struct IdRange {
877973
pub min: NodeId,

src/librustc/hir/itemlikevisit.rs

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use super::{Item, ImplItem};
12+
use super::intravisit::Visitor;
13+
14+
/// The "item-like visitor" visitor defines only the top-level methods
15+
/// that can be invoked by `Crate::visit_all_item_likes()`. Whether
16+
/// this trait is the right one to implement will depend on the
17+
/// overall pattern you need. Here are the three available patterns,
18+
/// in roughly the order of desirability:
19+
///
20+
/// 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR.
21+
/// - Example: find all items with a `#[foo]` attribute on them.
22+
/// - How: Implement `ItemLikeVisitor` and call `tcx.visit_all_item_likes_in_krate()`.
23+
/// - Pro: Efficient; just walks the lists of item-like things, not the nodes themselves.
24+
/// - Pro: Integrates well into dependency tracking.
25+
/// - Con: Don't get information about nesting
26+
/// - Con: Don't have methods for specific bits of HIR, like "on
27+
/// every expr, do this".
28+
/// 2. **Deep visit**: Want to scan for specific kinds of HIR nodes within
29+
/// an item, but don't care about how item-like things are nested
30+
/// within one another.
31+
/// - Example: Examine each expression to look for its type and do some check or other.
32+
/// - How: Implement `intravisit::Visitor` and use
33+
/// `tcx.visit_all_item_likes_in_krate(visitor.as_deep_visitor())`. Within
34+
/// your `intravisit::Visitor` impl, implement methods like
35+
/// `visit_expr()`; don't forget to invoke
36+
/// `intravisit::walk_visit_expr()` to keep walking the subparts.
37+
/// - Pro: Visitor methods for any kind of HIR node, not just item-like things.
38+
/// - Pro: Integrates well into dependency tracking.
39+
/// - Con: Don't get information about nesting between items
40+
/// 3. **Nested visit**: Want to visit the whole HIR and you care about the nesting between
41+
/// item-like things.
42+
/// - Example: Lifetime resolution, which wants to bring lifetimes declared on the
43+
/// impl into scope while visiting the impl-items, and then back out again.
44+
/// - How: Implement `intravisit::Visitor` and override the `visit_nested_foo()` foo methods
45+
/// as needed. Walk your crate with `intravisit::walk_crate()` invoked on `tcx.map.krate()`.
46+
/// - Pro: Visitor methods for any kind of HIR node, not just item-like things.
47+
/// - Pro: Preserves nesting information
48+
/// - Con: Does not integrate well into dependency tracking.
49+
///
50+
/// Note: the methods of `ItemLikeVisitor` intentionally have no
51+
/// defaults, so that as we expand the list of item-like things, we
52+
/// revisit the various visitors to see if they need to change. This
53+
/// is harder to do with `intravisit::Visitor`, so when you add a new
54+
/// `visit_nested_foo()` method, it is recommended that you search for
55+
/// existing `fn visit_nested` methods to see where changes are
56+
/// needed.
57+
pub trait ItemLikeVisitor<'hir> {
58+
fn visit_item(&mut self, item: &'hir Item);
59+
fn visit_impl_item(&mut self, impl_item: &'hir ImplItem);
60+
}
61+
62+
pub struct DeepVisitor<'v, V: 'v> {
63+
visitor: &'v mut V,
64+
}
65+
66+
impl<'v, 'hir, V> DeepVisitor<'v, V>
67+
where V: Visitor<'hir> + 'v
68+
{
69+
pub fn new(base: &'v mut V) -> Self {
70+
DeepVisitor { visitor: base }
71+
}
72+
}
73+
74+
impl<'v, 'hir, V> ItemLikeVisitor<'hir> for DeepVisitor<'v, V>
75+
where V: Visitor<'hir>
76+
{
77+
fn visit_item(&mut self, item: &'hir Item) {
78+
self.visitor.visit_item(item);
79+
}
80+
81+
fn visit_impl_item(&mut self, impl_item: &'hir ImplItem) {
82+
self.visitor.visit_impl_item(impl_item);
83+
}
84+
}

0 commit comments

Comments
 (0)