Uh oh!
There was an error while loading. Please reload this page.
Immovable generators - #45337
Conversation
rust-highfive
commented
Oct 17, 2017
r? @arielb1 (rust_highfive has picked a reviewer for you, use r? to override) |
c512527 to
0ffc23aCompareThere was a problem hiding this comment.
Can't you replicate the zip loop that the TyTuple branch does?
There was a problem hiding this comment.
I had to deal with the Binder, which needs an intermediate type. I could create one though.
There was a problem hiding this comment.
You could implement Relate for &'tcx Slice<Ty<'tcx>> using the same algorithm as Substs.
There was a problem hiding this comment.
I changed this to use an intermediate type to make it clear the logic only applies to TyGeneratorWitness.
There was a problem hiding this comment.
This doesn't feel right. If the generator captures an upvar through a mutable reference, the generator doesn't have dropck constraints:
#![feature(generators, generator_trait)]use std::cell::RefCell;use std::ops::Generator;fnstatic_alloc<'a,T>(t:T) -> &'amutT{// this is an unsafe function, but it is sound (not running destructors// is safe), can be implemented using a static dropless arena of// `ManuallyDrop` data, and was planned to be added to the standard// library at some point.unsafe{use std::mem;letmut b = Box::new(t);let r = &mut*(&mut*b as*mutT);
mem::forget(b);
r
}}fnmain(){let(cell,mut gen);
cell = Box::new(RefCell::new(0));let ref_ = static_alloc(Some(cell.borrow_mut()));// the upvar is the non-dropck `&mut Option<Ref<'a, i32>>`.
gen = || {// but the generator can use it to drop a `Ref<'a, i32>`.let _d = ref_.take();yield;};
gen.resume();// drops the RefCell and then the Ref, leading to use-after-free}There was a problem hiding this comment.
It might be a better idea to treat generators like trait object and have them always be dtorck. If generators are "supposed to be" captured by an impl Trait or trait object, this shouldn't be a problem in practice..
There was a problem hiding this comment.
This gives the following error.:
error[E0597]: `ref_` does not live long enough
--> dtorck.rs:27:18
|
25 | gen = || {
| -- capture occurs here
26 | // but the generator can use it to drop a `Ref<'a, i32>`.
27 | let _d = ref_.take();
| ^^^^ does not live long enough
...
32 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
error: aborting due to previous error
Note that upvars are still checked. Even if we didn't ignore the interior, would higher-ranked regions have any effect on dtorck?
There was a problem hiding this comment.
Does this still give the error with your PR? the only upvar should be ref_, which should have an empty dtorck constraint. Maybe I just need to check this PR out to see that this holds?
There was a problem hiding this comment.
That error is from this PR.
There was a problem hiding this comment.
Could you make this into a test?
There was a problem hiding this comment.
TyFnPtr skips binders blindly in the next 5 lines, so sure.
There was a problem hiding this comment.
// It is either a Box or some already borrowed local.nit: it can also be a borrow from something based on an upvar, e.g. &fcx.tcx.
There was a problem hiding this comment.
I was thinking of MIR locals here.
There was a problem hiding this comment.
(*fcx).tcx is not an already borrowed MIR local, it's from the place where fcx was allocated from, which can be everywhere (e.g. it could be a field of an arena or Vec, which are not a Box).
I just want to avoid comments that lie to avoid rumors spreading.
There was a problem hiding this comment.
You could also only promote locals that are borrowed across a yield if you don't want to promote everything.
There was a problem hiding this comment.
I'd need a liveness analysis which properly handles borrows then. I'm waiting on NLL before I do that. My plan is to have a pass which optimizes Storage* instructions based on NLL and then let the generator transformation only use the dataflow analysis on those instructions.
There was a problem hiding this comment.
Maybe add a comment to the following effect:
The types in the generator interior contain lifetimes local to the generator itself, which should not be exposed outside of the generator. Therefore, we replace these lifetimes with existentially-bound lifetimes, which reflect the exact value of the lifetimes not being known by users.
These lifetimes are used in auto trait impl checking (for example, if a Sync generator contains an &'α T, we need to check whether &'α T: Sync), so knowledge of the exact relationships between them isn't particularly important.
Note that lifetimes in the type of a local that is currently dead need not be alive. It's up to the generator to ensure that captured lifetimes that are actually used are alive.
There was a problem hiding this comment.
Note that lifetimes in the type of a local that is currently dead need not be alive. It's up to the generator to ensure that captured lifetimes that are actually used are alive.
I'm not sure what you mean by this. I added your other paragraphs as comments.
There was a problem hiding this comment.
Can you add a test that static generators actually work - that you can yield *b and that it stays valid?
There was a problem hiding this comment.
You could do yield *b in movable generator, given NLL. Did you mean I should use the reference after the yield so the test still works with NLL?
There was a problem hiding this comment.
That's one thing. Another thing is that you should have a test that actually runs the generator and asserts that the references have the same value between resumes, to quickly catch if some change horribly breaks immovable generator codegen.
There was a problem hiding this comment.
Could you also add an rpass test for the "vexpr lifetime" case (aka #44197)?
There was a problem hiding this comment.
I also need to think of more tests to add here.
bors
commented
Oct 21, 2017
☔ The latest upstream changes (presumably #45381) made this pull request unmergeable. Please resolve the merge conflicts. |
carols10cents
commented
Oct 30, 2017
Triage ping @Zoxc -- looks like there are merge conflicts again and some test failures! |
Zoxc
commented
Oct 30, 2017
This PR is blocked on landing #44917. |
398c882 to
233b261Comparebors
commented
Nov 5, 2017
☔ The latest upstream changes (presumably #45072) made this pull request unmergeable. Please resolve the merge conflicts. |
bors
commented
Nov 8, 2017
☔ The latest upstream changes (presumably #45735) made this pull request unmergeable. Please resolve the merge conflicts. |
bors
commented
Nov 15, 2017
☔ The latest upstream changes (presumably #45944) made this pull request unmergeable. Please resolve the merge conflicts. |
kennytm
commented
Nov 22, 2017
triage: still blocked by #44917. |
nikomatsakis
commented
Jan 19, 2018
As an aside, It doesn't seem to me that creating a "static generator" has to be unsafe, but I don't mind that it is. Am I missing something though? That is, is it UB to create a static generator under some conditions (even if you never invoke next?). I would have thought that the proof burden would be:
|
arielb1
commented
Jan 19, 2018
In the generator's contents signature, all regions are replaced with bound regions. This means a generator type would be something of the form: Basically, the generator contains some of the interior times for some set of substituted lifetimes, so if an Note that we lose all information about relations between the lifetimes - we might have had |
arielb1
commented
Jan 19, 2018
ATM static generators use the "standard" safe Generator trait. This means that static generators, once created, are a safety hazard - moving them and calling The above is probably a bad idea - it is very easy to make an unsafe API using them - and we should create an new |
nikomatsakis
commented
Jan 19, 2018
@arielb1 oh, I hadn't even looked at the trait part of this PR, I just assumed we would have an I think that's what we want, yeah. |
Zoxc
commented
Jan 22, 2018
It is always safe to create a generator.
You have to ensure that once you call |
nikomatsakis
commented
Jan 22, 2018
By this I guess you mean a non-static generator?
Right, and for this reason I think that the But I think we can tweak this in follow-up work. I'm pretty happy with this PR. r=me once rebased. |
…ws across suspension points to borrowck. Fixesrust-lang#44197, rust-lang#45259 and rust-lang#45093.
Zoxc
commented
Jan 23, 2018
@bors r=nikomatsakis |
bors
commented
Jan 23, 2018
📌 Commit 55c6c88 has been approved by |
bors
commented
Jan 23, 2018
Immovable generators
This adds support for immovable generators which allow you to borrow local values inside generator across suspension points. These are declared using a `static` keyword:
```rust
let mut generator = static || {
let local = &Vec::new();
yield;
local.push(0i8);
};
generator.resume();
// ERROR moving the generator after it has resumed would invalidate the interior reference
// drop(generator);
```
Region inference is no longer affected by the types stored in generators so the regions inside should be similar to other code (and unaffected by the presence of `yield` expressions). The borrow checker is extended to pick up the slack so interior references still result in errors for movable generators. This fixes#44197, #45259 and #45093.
This PR depends on [PR #44917 (immovable types)](#44917), I suggest potential reviewers ignore the first commit as it adds immovable types.bors
commented
Jan 24, 2018
☀️ Test successful - status-appveyor, status-travis |
This adds support for immovable generators which allow you to borrow local values inside generator across suspension points. These are declared using a
statickeyword:Region inference is no longer affected by the types stored in generators so the regions inside should be similar to other code (and unaffected by the presence of
yieldexpressions). The borrow checker is extended to pick up the slack so interior references still result in errors for movable generators. This fixes#44197, #45259 and #45093.This PR depends on PR #44917 (immovable types), I suggest potential reviewers ignore the first commit as it adds immovable types.