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

introduce region-clauses into the ParamEnv, use to replace the body_id #42341

Open
nikomatsakis opened this issue May 31, 2017 · 3 comments
Open
Labels
A-trait-system Area: Trait system C-cleanup Category: PRs that clean code up or issues documenting cleanup. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@nikomatsakis
Copy link
Contributor

When we are in a closure body, we often gain additional implied "outlives" relation based on the types of the closure arguments. Consider this:

foo(|x: &T| ...)

Here, the type of x will be &'0 T for some anonymous region '0. Within the function body, then, we can conclude that T: '0. We currently handle this through a variety of messy schemes. As part of chalkificaton, I would like to consolidate this into a cleaner, more uniform handling of environments and universes. This issue lays out an "evolving" plan for doing that.

Current handling

Currently, for every trait obligation that we have to prove (e.g., T: Debug), we have an ObligationCause -- this is primarily used for debugging, in that it specifies why we have to prove this thing. However, this type also carries a body_id identifying the closure it came from. When we wind up having to prove an outlives relationship like Foo: 'a, we record this in the fulfillment context (a kind of record of things we have yet to prove), and we track the body_id where the obligation was incurred. Then, after type-checking, in the region-checking phase, we walk down the AST and figure out -- for each closure body -- what outlives facts we know at that point in time. We then pull out the list of outlives obligations from the fulfillment cx for a given body-id and try to prove them, using those facts. This whole process is complex and fragile.

Roughly how I want it to work

We now have the ability to have every obligation have its own, distinct environment (we used to have just one ambient environment). This means that instead of having this ad-hoc body_id field, we can just extend the environment when we enter into a closure body. So, for example, when type-checking the body of the closure in the beginning, our environment would be extended with a T: 'a outlives clause. We can do this right away, just as soon as we start type-checking the closure. This environment is automatically propagated to sub-goals, so when we wind up with some outlives obligations that we have to prove, they will have T: 'a in their list of clauses (i.e., the facts that they can draw upon). This means that regionck doesn't have to do anything "special" -- rather, the input to regionck will change from being a flat list of obligations, to obligations that can carry more environmental information.

There is one complication here:

  • when we first start type-checking closures, we often have not yet inferred the types of the arguments yet. This is partly why the current scheme defers everything until regionck. But this is ok, we can still insert "outlives" obligations that contain inference variables. We'll give them their final values before we go and solve things.

Actual steps

OK, I ran out of time for this part. =)

@nikomatsakis nikomatsakis added A-trait-system Area: Trait system T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 31, 2017
@djzin
Copy link
Contributor

djzin commented Jun 2, 2017

Is it possible to fully desugar closures into structs that implement the Fn traits? Or is this extremely ambitious / impossible? Because it sounds like if simplifying logic / more uniform handling of these things is the goal, turning closures into "just another function" would be a profitable step in this direction. This is certainly how I think about closures... but AFAICT they are treated as much more of a "fundamental" in the compiler currently

So if you have a closure foo(|&y| x + y), this would desugar into (roughly)

struct Closure<T> {
    x: T,
}

impl<'a, T, U> FnOnce<(&'a U,)> for Closure<T>
    where T: Add<U>,
          U: Copy + 'a,
{
    type Output = <T as Add<U>>::Output;
    extern "rust-call" fn call_once(self, (&y,): (&'a U,)) -> Self::Output {
        self.x + y
    }
}

foo(Closure{x: &x})

@nikomatsakis
Copy link
Contributor Author

@djzin this does happen during MIR lowering, and eventually I do plan to move all the region processing so that it occurs on the MIR. It's good point -- maybe it's not worth worrying about the body_id, and instead a better idea to pursue the work to move regionck so that it occurs on MIR. That would also move us closer to non-lexical lifetimes.

@djzin
Copy link
Contributor

djzin commented Jun 11, 2017

So what does it take to move regionck to MIR? I am happy to help with this any way I can; I'm not super well-versed in this stuff at the moment but I can learn :)

@Mark-Simulacrum Mark-Simulacrum added the C-cleanup Category: PRs that clean code up or issues documenting cleanup. label Jul 27, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-trait-system Area: Trait system C-cleanup Category: PRs that clean code up or issues documenting cleanup. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants