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

Remove contraction from region inference #29188

Merged
merged 8 commits into from Oct 29, 2015

Conversation

Projects
None yet
7 participants
@nikomatsakis
Copy link
Contributor

nikomatsakis commented Oct 20, 2015

This fixes #29048 (though I think adding better transactional support would be a better fix for that issue, but that is more difficult). It also simplifies region inference and changes the model to a pure data flow one, as discussed in this internals thread. I am not 100% sure though if this PR is the right thing to do -- or at least maybe not at this moment, so thoughts on that would be appreciated.

r? @pnkfelix
cc @arielb1

}
Ok(())
}));
if self.is_local() { // (1)

This comment has been minimized.

@arielb1

arielb1 Oct 20, 2015

Contributor

Does this fix RUST_LOG=rustc::middle::traits?

This comment has been minimized.

@nikomatsakis

nikomatsakis Oct 23, 2015

Author Contributor

@arielb1

Does this fix RUST_LOG=rustc::middle::traits?

Yes, I expect so. I think I was having problems with RUST_LOG=rustc_typeck, but I suspect it's the same issue. And yes, a fallible version of item_path_str would be better.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Oct 21, 2015

Interestingly, this PR fixes a bug that @dherman was encountered in his nanny project. I need to investigate that a bit more deeply though.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Oct 23, 2015

I looked into @dherman's case. It comes down to a trait method defined like this:

fn foo<'root, T:Trait<'root>>(t: T) -> &i32

the point being that 'root does not appear in the signature in anyway, and hence has no incoming constraints. In some sense this PR is a reasonable way to fix that problem, though I think that the fix I really want is to guarantee that all type/lifetime parameters to a fn outlive calls to that fn. But that requires some changes to the language to achieve completely. Still, I am somewhat surprised that there aren't incoming edges to the variable from the point where the parameters are instantiated -- I think a reasonable change might be to make it so that when you create a region variable, you must provide a lower bound (which could be ty::ReEmpty). I've started a PR to that effect a few times but never quite finished it.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Oct 23, 2015

@arielb1

Isn't SupSupConflict dead now? You may want to remove it.

yes, done.

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Oct 23, 2015

@nikomatsakis

I am unable to reproduce @dherman's case, but its not like putting a restriction on 'a would be helpful in any way, because of the "lifetime truncation" behaviour.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Oct 23, 2015

@arielb1 what do you mean by "lifetime truncation"?

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Oct 23, 2015

@nikomatsakis

The fact that you can substitute an fn with some lifetime parameters that don't appear in the signature and then call it after these lifetime parameters are dead.

A reasonable argument for the soundness of this is that, because lifetime bounds in Rust are never strict, if you LUB all the lifetimes in a validate predicate with some lifetime you still get something valid - this is basically the reason lifetime erasure works (and therefore I call it "truncation").

For example, in your code:

trait Trait<'root> {}
impl<'a,'root> Trait<'root> for &'a i32 where 'a: 'root {}
fn foo<'root, T:Trait<'root>>(t: T) -> i32 {
    /* ... - your original signature doesn't specify the return type's lifetime */
}

fn main() {
    'v: {
        let v = &2;
        ('t: { foo::<'t, &'v i32> })(&v);
    }
}

foo is substituted with the lifetime parameter 't, which is valid as &'v i32: Trait<'t> holds (because of 'v: 't). On the other hand, 't is not live when we call foo, so for soundness we can LUB 't with the lifetime of the call to get some lifetime 't2, LUB 'v with the lifetime of the call to get 'v back, and we have that foo::<'v, &'v i32> is a valid substitution.

This, of course, breaks horribly if we have types that can't be bought fresh to some lifetime - e.g. unloadable code, but we already know that.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Oct 23, 2015

OK, yes. This is what I was saying I would potentially like to fix, though
it requires language changes.

On Fri, Oct 23, 2015 at 1:55 PM, arielb1 notifications@github.com wrote:

@nikomatsakis https://github.com/nikomatsakis

The fact that you can substitute an fn with some lifetime parameters that
don't appear in the signature and then call it after these lifetime
parameters are dead.

A reasonable argument for the soundness of this is that, because lifetime
bounds in Rust are never strict, if you LUB all the lifetimes in a validate
predicate with some lifetime you still get something valid - this is
basically the reason lifetime erasure works (and therefore I call it
"truncation").

For example, in your code:

trait Trait<'root> {}impl<'a,'root> Trait<'root> for &'a i32 where 'a: 'root {}fn foo<'root, T:Trait<'root>>(t: T) -> i32 {
/* ... - your original signature doesn't specify the return type's lifetime */
}
fn main() {
'v: {
let v = &2;
('t: { foo::<'t, &'v i32> })(&v);
}
}

foo is substituted with the lifetime parameter 't, which is valid as &'v
i32: Trait<'t> holds (because of 'v: 't). On the other hand, 't is not
live when we call foo, so for soundness we can LUB 't with the lifetime
of the call to get some lifetime 't2, LUB 'v with the lifetime of the
call to get 'v back, and we have that foo::<'v, &'v i32> is a valid
substitution.

This, of course, breaks horribly if we have types that can't be bought
fresh to some lifetime - e.g. unloadable code, but we already know that.


Reply to this email directly or view it on GitHub
#29188 (comment).

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Oct 26, 2015

@pnkfelix would you be able to review this sooner rather than later? It does fix a regression two regressions, which will be branched into beta any day now.

@pnkfelix

This comment has been minimized.

Copy link
Member

pnkfelix commented Oct 28, 2015

okay it took me a little bit longer than I expected to wrap my head around this. You left in the fn contraction method, so at first I thought something more subtle was going on, but all it now does is check that the ConstrainVarSubReg constraints hold.

It seems like the code is just calling out for a simplification pass; e.g. the factoring of fn check_node within the body of fn contract_node, which made some sense when there was a separate fn adjust_node path, just seems like unnecessary abstraction.

In addition, it would be nice if the README were updated, removing the discussion of processing the Contracting nodes.

But there's no reason to hold up this PR for those kinds of changes.

@pnkfelix

This comment has been minimized.

Copy link
Member

pnkfelix commented Oct 28, 2015

@nikomatsakis r=me once you've addressed the travis failure in some reasonable manner.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Oct 28, 2015

@pnkfelix

It seems like the code is just calling out for a simplification pass; e.g. the factoring of fn check_node within the body of fn contract_node, which made some sense when there was a separate fn adjust_node path, just seems like unnecessary abstraction.

Yes, I agree, I was just being hasty. I will try to clean it up a bit.

In addition, it would be nice if the README were updated, removing the discussion of processing the Contracting nodes.

agreed

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Oct 28, 2015

region_inference/README.md needs some rework to account for unboxed closures and the new higher-rank inference system (I think).

nikomatsakis added some commits Oct 17, 2015

Remove contraction. The contraction rules predated the notion of an
empty region, and they complicate region inference to no particular end.
They also lead in some cases to spurious errors like #29048 (though in
some cases these errors are helpful in tracking down missing
constraints).
Don't "double check" var-sub-var constraints, which are handled in
expansion already by growing the RHS to be bigger than LHS (all the way
to `'static` if necessary). This is needed because contraction doesn't
handle givens. Fixes #28934.

@nikomatsakis nikomatsakis force-pushed the nikomatsakis:remove-contraction branch from 1ccd800 to c2277de Oct 28, 2015

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Oct 29, 2015

@bors r=pnkfelix

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Oct 29, 2015

📌 Commit c2277de has been approved by pnkfelix

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Oct 29, 2015

⌛️ Testing commit c2277de with merge 427140f...

bors added a commit that referenced this pull request Oct 29, 2015

Auto merge of #29188 - nikomatsakis:remove-contraction, r=pnkfelix
This fixes #29048 (though I think adding better transactional support would be a better fix for that issue, but that is more difficult). It also simplifies region inference and changes the model to a pure data flow one, as discussed in [this internals thread](https://internals.rust-lang.org/t/rough-thoughts-on-the-impl-of-region-inference-mir-etc/2800). I am not 100% sure though if this PR is the right thing to do -- or at least maybe not at this moment, so thoughts on that would be appreciated.

r? @pnkfelix 
cc @arielb1

@bors bors merged commit c2277de into rust-lang:master Oct 29, 2015

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Oct 30, 2015

Does the beta-nominated tag mean this PR is to be backported to 1.5?

I’d appreciate this: at the moment I have code that compiles on stable and nightly, but not 1.5.0-beta.2: https://travis-ci.org/servo/rust-cssparser/builds/88377023
… and no known work-around.

@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented Oct 30, 2015

@SimonSapin yeah if accepted this'll be backported to 1.5

@brson

This comment has been minimized.

Copy link
Contributor

brson commented Nov 3, 2015

@rust-lang/lang @SimonSapin wants this backported soon. Can you review?

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 4, 2015

@brson I think this is really a @rust-lang/compiler issue. But I'm inclined to accept it this for beta backport.

@pnkfelix

This comment has been minimized.

Copy link
Member

pnkfelix commented Nov 4, 2015

+1 to backport

@brson brson referenced this pull request Nov 4, 2015

Merged

Beta next #29592

@brson

This comment has been minimized.

Copy link
Contributor

brson commented Nov 4, 2015

Backport PR is up when you are ready.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Nov 5, 2015

Accepting for beta.

@nikomatsakis nikomatsakis deleted the nikomatsakis:remove-contraction branch Mar 30, 2016

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.