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

Be more careful when selecting LUB of free regions #27892

Merged
merged 17 commits into from
Aug 22, 2015

Conversation

nikomatsakis
Copy link
Contributor

Issue #27583 was caused by the fact that LUB('a,'b) yielded 'static, even if there existed a region 'tcx:'a+'b. This PR replaces the old very hacky code for computing how free regions relate to one another with something rather more robust. This solves the issue for #27583, though I think that similar bizarro bugs can no doubt arise in other ways -- the root of the problem is that the region-inference code was written in an era when a LUB always existed, but that hasn't held for some time. To truly solve this problem, it needs to be generalized to cope with that reality. But let's leave that battle for another day.

r? @aturon

@nikomatsakis
Copy link
Contributor Author

cc @arielb1, this seems like something you might enjoy.

@arielb1
Copy link
Contributor

arielb1 commented Aug 19, 2015

@nikomatsakis

Calculating the LUB of ReScope regions takes up a decent amount of time (25% of typeck time if you believe callgrind) - could you somehow throw it into your graph abstraction or would an ad-hoc solution be better?

@nikomatsakis
Copy link
Contributor Author

@arielb1 I could probably throw it in, but OTOH you can do quite a bit better in that case since it's a tree. If we did a bit of pre-calculation, we could do LUB support very fast.

@nikomatsakis
Copy link
Contributor Author

@arielb1 iirc the current code is super naive, using hashmaps and the like. premature optimization and all that.

@aturon
Copy link
Member

aturon commented Aug 19, 2015

@arielb1: it's taking me a while to find time to review this, so if you want to r? the PR please feel free! (Just let me know.)

@arielb1
Copy link
Contributor

arielb1 commented Aug 19, 2015

@aturon

I want somebody to look at the algorithm.

@nikomatsakis
Copy link
Contributor Author

r? @pnkfelix

@rust-highfive rust-highfive assigned pnkfelix and unassigned aturon Aug 20, 2015
@@ -10,24 +10,18 @@

//! This file defines
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love

/// Returns the set of bounds `X` such that:
///
/// - `a < X` and `b < X`
/// - there is no `Y` such that `a < Y` and `Y < X`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definition for the returned set of bounds does not seem adequate.

  1. A reader might rightfully wonder "does the range of Y in the second condition include the elements of X? Or is X excluded from the universe of Y?"

    (From reading the code overall I concluded that the universe of Y does indeed include the elements of X, which leads us to the second issue...)

  2. It does not cover what happens for situations with strongly-connected-components such as:

    c -> d
    d -> c
    a -> c
    b -> d
    

    I infer, from the tests that come later in the PR, that in a case like the above, minimal_upper_bounds(a, b) will return either [c] or [d]; but neither of those options conforms with the specification.


Update: I just re-read the definition more carefully and realized that it only says "there is no Y such that a < Y and Y < X -- namely, it does not mention b anywhere, though I inferred (perhaps incorrectly) that the "a < Y" condition was meant to cover b as well, as in lets say "there is no Y such that a < Y and b < Y and Y < X."

  • This means that, as stated, minimal_upper_bounds in my above example with an SCC in fact can return [c] without violating any of the conditions it listed.

So I'm rethinking my criticism, it could be that this was very carefully crafted to allow for a consistent choice in the case of reaching an SCC. But if that is the case, this comment would greatly benefit from mentioning the SCC case and pointing out the intent here.


Update 2: I don't think my attempt to make sense of the original definition works out. It may well be that a is indeed meant to have some special role, but if you consider something like

a -> c
c -> d
b -> d

then that should obviously yield [d], but by the definition above, choosing Y == c would cause the second condition to be violated. That is, I think the second condition does need to be revised to include b in some role, to cover the behavior in such situations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for trying to shared to make sense of it, I think I was just imprecise in writings things out (though I certainly considered the SCC case). (The answer for SCCs is that we pick the lowest numbered element of the SCC as its representative.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a set of tests I sketched while thinking about cases of interest here. (However, given that you've pointed out that the SCC answer is that we pick the lowest numbered element, and not that a has some special role, then a number of these tests will be arguably redundant.)

http://blog.pnkfx.org/linked/lub-graphs.html

(View source to see graphviz text for each case. Or actually I'll just add that to the rendering.)

// not minimal upper bounds. So you may have e.g.
// `[a, b, tcx, x]` where `a < tcx` and `b < tcx` and
// `x < a` and `x < b`. This could be reduced to
// just `[x]`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I first read this comment I was confused as to what step, if any, the "This could be reduced to just [x]" was referring to. After reading the whole thing, I understood that it was saying that [x] is the expected output from the whole series of steps. I think the presentation would be improved if you gave the concrete hypothetical input [a, b, tcx, x] at the outset, before the first step.

@pnkfelix
Copy link
Member

So I had a bunch of nits, but of course the real question is about the algorithm being used.

It seems reasonable at first glance. If the element order were preserved as elements were removed from the vector then I would be more confident in its correctness. But the use of swap_remove, as noted above, gives me a little bit of worry. (Though not that much worry; I just want to make sure that the ordering constraints are not perturbed by the swapping.)

@nikomatsakis
Copy link
Contributor Author

@pnkfelix

But the use of swap_remove, as noted above, gives me a little bit of worry

So as I wrote, I'm pretty sure it's fine, because the algorithm is working its way left-to-right, and you can reorder the "not yet processed" things as you like. But it is somewhat subtle. I could rewrite it to compress-and-truncate, but then I'd either want a copy bound or a lot of calls to clone. Either of which is fine really, but...perhaps I'll just leave a comment.

@nikomatsakis
Copy link
Contributor Author

oh, wait, I forgot, my vector is just indices. Yeah, ok, I'll remove the subtle call to swap_remove.

@nikomatsakis
Copy link
Contributor Author

@pnkfelix how about "postdom_upper_bound"?

@nikomatsakis
Copy link
Contributor Author

/me decides on his own.

@nikomatsakis
Copy link
Contributor Author

@bors r=pnkfelix

@bors
Copy link
Contributor

bors commented Aug 21, 2015

📌 Commit 81eab1c has been approved by pnkfelix

bors added a commit that referenced this pull request Aug 22, 2015
Issue #27583 was caused by the fact that `LUB('a,'b)` yielded `'static`, even if there existed a region `'tcx:'a+'b`. This PR replaces the old very hacky code for computing how free regions relate to one another with something rather more robust. This solves the issue for #27583, though I think that similar bizarro bugs can no doubt arise in other ways -- the root of the problem is that the region-inference code was written in an era when a LUB always existed, but that hasn't held for some time. To *truly* solve this problem, it needs to be generalized to cope with that reality. But let's leave that battle for another day.

r? @aturon
@bors
Copy link
Contributor

bors commented Aug 22, 2015

⌛ Testing commit 81eab1c with merge 5e5b99f...

@bors bors merged commit 81eab1c into rust-lang:master Aug 22, 2015
@nikomatsakis nikomatsakis deleted the issue-27583 branch March 30, 2016 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants