Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate impl items from the parent impl #37660

Merged
merged 18 commits into from Nov 18, 2016

Conversation

Projects
None yet
5 participants
@nikomatsakis
Copy link
Contributor

nikomatsakis commented Nov 9, 2016

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.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 9, 2016

Hmm, had a thought last night. Another option that might make sense is to pull the Name for each impl item into the parent impl (it can appear both places). This would allow us to do the method search to find methods of name foo while just reading from the parent impl item. Moreover, it solves the problem better than separate the signature from the body: just doing that would mean that if I change the signature of a private method, we have to recompile callers of other methods, just because we haven't separated those two things.

Obviously, if we pursued the more aggressive strategy of rehashing partial results it sort of subsumes both cases. However, I remain somewhat nervous about that in the shorter term (though longer term it seems good). But also it seems advantageous to be able to determine as much as we can without having to re-run parts of the compiler.

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Nov 9, 2016

@nikomatsakis Maybe #37402 plus some dep node changes can solve that?
It moves out type information from ty::AssociatedItem so it's less than the signature.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 9, 2016

@eddyb well, it seems to me that the current dep information is correct, unless we move the Name out into the impl. That is, if I were to change the name of various inherent methods on Foo, I ought to re-type-check things that called methods on Foo. And, the way things are setup now, that would be reflected in a change to the impl-item -- indistinguishable from other changes.

So fundamentally there are two ways to fix this:

  • refine the HIR so that we separate out the things of interest:
    • this covers both separating sig from body and separating name from impl
    • the more precise we get it, the better things work, so doing both is prob good
  • change our approach to hash intermediate results:
    • this compensates for imprecise HIR information

It seems to me that for best results we want both. Having more precise information lets us quickly rule out work without even need to reconfirm. But at a certain point you get diminishing returns. Though perhaps the initial work to recompute signatures and so forth is cheap enough that it doesn't make sense to separate HIR more. I can easily believe that.

I've talked about this with @michaelwoerister a fair amount. I tend to view this as "firewalls". That is, you have points where we hash the intermediate results because we can't (or don't care to) track the edges closely enough. The first example is from the files to the HIR. The second (existing) example is after trans collection. It makes sense to do it in more places, I suspect. Then in between these firewalls you can be conservative and just trace out the impact of a chance and invalidate the work in between said change and the next "firewall" (where you will want to re-hash). This will all fit very nicely with the work you are doing to make the compiler more demand-driven, of course, so I had hoped we could start moving more in that direction as that proceeds, but in short term get the HIR precise enough. (I still believe that.)

@michaelwoerister

This comment has been minimized.

Copy link
Contributor

michaelwoerister commented Nov 9, 2016

Another option that might make sense is to pull the Name for each impl item into the parent impl (it can appear both places).

That seems like a good idea to me.

@michaelwoerister

This comment has been minimized.

Copy link
Contributor

michaelwoerister commented Nov 9, 2016

Obviously, if we pursued the more aggressive strategy of rehashing partial results it sort of subsumes both cases.

I also think that is true and as a consequence we should avoid doing any contortions in HIR representation just to improve dependency tracking. But the examples so far seem to make sense on their on right.

@@ -974,9 +979,11 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
let trait_def = tcx.lookup_trait_def(impl_trait_ref.def_id);
let mut overridden_associated_type = None;

let impl_items = || impl_item_ids.iter().map(|&id| ccx.tcx.map.impl_item(id));

This comment has been minimized.

@michaelwoerister

michaelwoerister Nov 9, 2016

Contributor

Why wrap this in a closure?

@michaelwoerister

This comment has been minimized.

Copy link
Contributor

michaelwoerister commented Nov 9, 2016

So, I did one pass looking through all the commits and I think it should indeed be merged. There are many good refactorings in it that make things cleaner, especially with respect to what's going on in ty::collect. Also making the different levels for HIR visitors more explicit is a good thing.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 9, 2016

@michaelwoerister so I just pushed a new commit intended to make the "nested" pattern a little more future-proof. This was mildly more tedious than I thought because it requires having better lifetime annotations to make it work (most visitors are written to accept a much wider range of lifetimes than is actually needed, since it winds up needing less explicit use of 'tcx). But it seems like an improvement regardless. Take a look.

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Nov 9, 2016

☔️ The latest upstream changes (presumably #37670) made this pull request unmergeable. Please resolve the merge conflicts.

@michaelwoerister

This comment has been minimized.

Copy link
Contributor

michaelwoerister commented Nov 10, 2016

I just pushed a new commit intended to make the "nested" pattern a little more future-proof.

I like it. It's a bit unfortunate with the additional lifetime annotations but not really a problem.
It's a bit subtle that overriding nested_visit_map() will turn the visitor into a nested one. Maybe a name like nesting_enabled for the method would improve that?

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 11, 2016

@michaelwoerister

It's a bit subtle that overriding nested_visit_map() will turn the visitor into a nested one. Maybe a name like nesting_enabled for the method would improve that?

Sure, I can rename it. I'd prefer to leverage specialization to make a NestedVisitor trait or something but we can't quite do that nicely.

Also, I just pushed a PR that separates the name into the impl, which actually fixes the test cases that were blocked on this.

@nikomatsakis nikomatsakis force-pushed the nikomatsakis:incremental-36349 branch from 75a5c98 to bd0a191 Nov 11, 2016

let name_and_namespace = |def_id| {
let item = self.tcx.associated_item(def_id);
let name_and_namespace = |r: &ty::AssociatedItemRef| {
let item = self.tcx.associated_item(r.def_id);

This comment has been minimized.

@michaelwoerister

michaelwoerister Nov 11, 2016

Contributor

This is one of the cases where it would make sense to also store the kind of associated item in the ref.

This comment has been minimized.

@eddyb

eddyb Nov 11, 2016

Member

I think I agree. I'd almost go as far as having associated_item use the DefId of its container for the dep node, and then just use associated_items everywhere.

@michaelwoerister

This comment has been minimized.

Copy link
Contributor

michaelwoerister commented Nov 11, 2016

Also, I just pushed a PR that separates the name into the impl, which actually fixes the test cases that were blocked on this.

I just looked through the new commits. Very nice :)

@nikomatsakis nikomatsakis force-pushed the nikomatsakis:incremental-36349 branch 4 times, most recently from acfac99 to 4fb31ca Nov 11, 2016

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 11, 2016

@michaelwoerister @eddyb ready for re-review; I removed AssociatedItemRef and consolidated everything into ImplItemRef (the same should be done for traits, but I'd prefer to leave it for later).

I think there is one thing missing from this PR before it can land though: we are not hashing all of the things in the ImplItemRef along with the impl, I suspect, so we need to do that, and I need to make some ICH tests for it.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 12, 2016

Oh, I should add -- it didn't quite work as well as the old approach across crates, but I think that's a separate issue really. I filed #37720 describing it.

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Nov 12, 2016

☔️ The latest upstream changes (presumably #37730) made this pull request unmergeable. Please resolve the merge conflicts.

// for details.
ctp::setup_constraining_predicates(&mut ty_predicates.predicates,
trait_ref,
&mut ctp::parameters_for_impl(selfty, trait_ref));

This comment has been minimized.

@eddyb

eddyb Nov 12, 2016

Member

Lovely! This might actually solve my trouble with this ordering thing, looks like it's self-contained now.

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Nov 12, 2016

r=me with a rebase.

@nikomatsakis nikomatsakis force-pushed the nikomatsakis:incremental-36349 branch 3 times, most recently from 15209aa to 9364fc2 Nov 14, 2016

nikomatsakis added some commits Nov 15, 2016

canonicalize base incremental path on windows
This sidesteps problems with long paths because the canonical path
includes the "magic long path prefix" on Windows.

@nikomatsakis nikomatsakis force-pushed the nikomatsakis:incremental-36349 branch from ec345a3 to ab79438 Nov 17, 2016

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 17, 2016

@bors r+

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Nov 17, 2016

📌 Commit ab79438 has been approved by nikomatsakis

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 17, 2016

@bors r=eddyb

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Nov 17, 2016

💡 This pull request was already approved, no need to approve it again.

  • There's another pull request that is currently being tested, blocking this pull request: #37732
@bors

This comment has been minimized.

Copy link
Contributor

bors commented Nov 17, 2016

📌 Commit ab79438 has been approved by eddyb

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 17, 2016

@bors r=eddyb

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Nov 17, 2016

📌 Commit c938007 has been approved by eddyb

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Nov 18, 2016

⌛️ Testing commit c938007 with merge 35e8924...

bors added a commit that referenced this pull request Nov 18, 2016

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.

@bors bors merged commit c938007 into rust-lang:master Nov 18, 2016

1 of 2 checks passed

continuous-integration/travis-ci/pr The Travis CI build failed
Details
homu Test successful
Details

nikomatsakis added a commit to nikomatsakis/rust that referenced this pull request Nov 21, 2016

in region, treat current (and future) item-likes alike
The `visit_fn` code mutates its surrounding context.  Between *items*,
this was saved/restored, but between impl items it was not. This meant
that we wound up with `CallSiteScope` entries with two parents (or
more!).  As far as I can tell, this is harmless in actual type-checking,
since the regions you interact with are always from at most one of those
branches. But it can slow things down.

Before, the effect was limited, since it only applied to impl items
within an impl. After rust-lang#37660, impl items are visisted all together at
the end, and hence this could create a very messed up
hierarchy. Isolating impl item properly solves both issues.

I cannot come up with a way to unit-test this; for posterity, however,
you can observe the messed up hierarchies with a test as simple as the
following, which would create a callsite scope with two parents both
before and after

```
struct Foo {
}

impl Foo {
    fn bar(&self) -> usize {
        22
    }

    fn baz(&self) -> usize {
        22
    }
}

fn main() { }
```

Fixes rust-lang#37864.

nikomatsakis added a commit to nikomatsakis/rust that referenced this pull request Nov 29, 2016

in region, treat current (and future) item-likes alike
The `visit_fn` code mutates its surrounding context.  Between *items*,
this was saved/restored, but between impl items it was not. This meant
that we wound up with `CallSiteScope` entries with two parents (or
more!).  As far as I can tell, this is harmless in actual type-checking,
since the regions you interact with are always from at most one of those
branches. But it can slow things down.

Before, the effect was limited, since it only applied to impl items
within an impl. After rust-lang#37660, impl items are visisted all together at
the end, and hence this could create a very messed up
hierarchy. Isolating impl item properly solves both issues.

I cannot come up with a way to unit-test this; for posterity, however,
you can observe the messed up hierarchies with a test as simple as the
following, which would create a callsite scope with two parents both
before and after

```
struct Foo {
}

impl Foo {
    fn bar(&self) -> usize {
        22
    }

    fn baz(&self) -> usize {
        22
    }
}

fn main() { }
```

Fixes rust-lang#37864.

nikomatsakis added a commit to nikomatsakis/rust that referenced this pull request Dec 1, 2016

in region, treat current (and future) item-likes alike
The `visit_fn` code mutates its surrounding context.  Between *items*,
this was saved/restored, but between impl items it was not. This meant
that we wound up with `CallSiteScope` entries with two parents (or
more!).  As far as I can tell, this is harmless in actual type-checking,
since the regions you interact with are always from at most one of those
branches. But it can slow things down.

Before, the effect was limited, since it only applied to impl items
within an impl. After rust-lang#37660, impl items are visisted all together at
the end, and hence this could create a very messed up
hierarchy. Isolating impl item properly solves both issues.

I cannot come up with a way to unit-test this; for posterity, however,
you can observe the messed up hierarchies with a test as simple as the
following, which would create a callsite scope with two parents both
before and after

```
struct Foo {
}

impl Foo {
    fn bar(&self) -> usize {
        22
    }

    fn baz(&self) -> usize {
        22
    }
}

fn main() { }
```

Fixes rust-lang#37864.

bors added a commit that referenced this pull request Dec 2, 2016

Auto merge of #37920 - nikomatsakis:compile-time-regression-37864, r=mw
in region, treat current (and future) item-likes alike

The `visit_fn` code mutates its surrounding context.  Between *items*,
this was saved/restored, but between impl items it was not. This meant
that we wound up with `CallSiteScope` entries with two parents (or
more!).  As far as I can tell, this is harmless in actual type-checking,
since the regions you interact with are always from at most one of those
branches. But it can slow things down.

Before, the effect was limited, since it only applied to impl items
within an impl. After #37660, impl items are visisted all together at
the end, and hence this could create a very messed up
hierarchy. Isolating impl item properly solves both issues.

I cannot come up with a way to unit-test this; for posterity, however,
you can observe the messed up hierarchies with a test as simple as the
following, which would create a callsite scope with two parents both
before and after

```
struct Foo {
}

impl Foo {
    fn bar(&self) -> usize {
        22
    }

    fn baz(&self) -> usize {
        22
    }
}

fn main() { }
```

Fixes #37864.

r? @michaelwoerister

cc @pnkfelix -- can you think of a way to make a regr test?

bors added a commit that referenced this pull request Dec 2, 2016

Auto merge of #37920 - nikomatsakis:compile-time-regression-37864, r=mw
in region, treat current (and future) item-likes alike

The `visit_fn` code mutates its surrounding context.  Between *items*,
this was saved/restored, but between impl items it was not. This meant
that we wound up with `CallSiteScope` entries with two parents (or
more!).  As far as I can tell, this is harmless in actual type-checking,
since the regions you interact with are always from at most one of those
branches. But it can slow things down.

Before, the effect was limited, since it only applied to impl items
within an impl. After #37660, impl items are visisted all together at
the end, and hence this could create a very messed up
hierarchy. Isolating impl item properly solves both issues.

I cannot come up with a way to unit-test this; for posterity, however,
you can observe the messed up hierarchies with a test as simple as the
following, which would create a callsite scope with two parents both
before and after

```
struct Foo {
}

impl Foo {
    fn bar(&self) -> usize {
        22
    }

    fn baz(&self) -> usize {
        22
    }
}

fn main() { }
```

Fixes #37864.

r? @michaelwoerister

cc @pnkfelix -- can you think of a way to make a regr test?

bors added a commit that referenced this pull request Dec 2, 2016

Auto merge of #37920 - nikomatsakis:compile-time-regression-37864, r=mw
in region, treat current (and future) item-likes alike

The `visit_fn` code mutates its surrounding context.  Between *items*,
this was saved/restored, but between impl items it was not. This meant
that we wound up with `CallSiteScope` entries with two parents (or
more!).  As far as I can tell, this is harmless in actual type-checking,
since the regions you interact with are always from at most one of those
branches. But it can slow things down.

Before, the effect was limited, since it only applied to impl items
within an impl. After #37660, impl items are visisted all together at
the end, and hence this could create a very messed up
hierarchy. Isolating impl item properly solves both issues.

I cannot come up with a way to unit-test this; for posterity, however,
you can observe the messed up hierarchies with a test as simple as the
following, which would create a callsite scope with two parents both
before and after

```
struct Foo {
}

impl Foo {
    fn bar(&self) -> usize {
        22
    }

    fn baz(&self) -> usize {
        22
    }
}

fn main() { }
```

Fixes #37864.

r? @michaelwoerister

cc @pnkfelix -- can you think of a way to make a regr test?

bors added a commit that referenced this pull request Dec 4, 2016

Auto merge of #37920 - nikomatsakis:compile-time-regression-37864, r=mw
in region, treat current (and future) item-likes alike

The `visit_fn` code mutates its surrounding context.  Between *items*,
this was saved/restored, but between impl items it was not. This meant
that we wound up with `CallSiteScope` entries with two parents (or
more!).  As far as I can tell, this is harmless in actual type-checking,
since the regions you interact with are always from at most one of those
branches. But it can slow things down.

Before, the effect was limited, since it only applied to impl items
within an impl. After #37660, impl items are visisted all together at
the end, and hence this could create a very messed up
hierarchy. Isolating impl item properly solves both issues.

I cannot come up with a way to unit-test this; for posterity, however,
you can observe the messed up hierarchies with a test as simple as the
following, which would create a callsite scope with two parents both
before and after

```
struct Foo {
}

impl Foo {
    fn bar(&self) -> usize {
        22
    }

    fn baz(&self) -> usize {
        22
    }
}

fn main() { }
```

Fixes #37864.

r? @michaelwoerister

cc @pnkfelix -- can you think of a way to make a regr test?

stjepang added a commit to stjepang/rust that referenced this pull request Dec 16, 2016

in region, treat current (and future) item-likes alike
The `visit_fn` code mutates its surrounding context.  Between *items*,
this was saved/restored, but between impl items it was not. This meant
that we wound up with `CallSiteScope` entries with two parents (or
more!).  As far as I can tell, this is harmless in actual type-checking,
since the regions you interact with are always from at most one of those
branches. But it can slow things down.

Before, the effect was limited, since it only applied to impl items
within an impl. After rust-lang#37660, impl items are visisted all together at
the end, and hence this could create a very messed up
hierarchy. Isolating impl item properly solves both issues.

I cannot come up with a way to unit-test this; for posterity, however,
you can observe the messed up hierarchies with a test as simple as the
following, which would create a callsite scope with two parents both
before and after

```
struct Foo {
}

impl Foo {
    fn bar(&self) -> usize {
        22
    }

    fn baz(&self) -> usize {
        22
    }
}

fn main() { }
```

Fixes rust-lang#37864.

@nikomatsakis nikomatsakis deleted the nikomatsakis:incremental-36349 branch Apr 14, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.