Skip to content

Commit d40a0b3

Browse files
committed
Auto merge of #49943 - pnkfelix:fix-issue-49918, r=nikomatsakis
Treat generators as if they have an arbitrary destructor Conservatively assume dropping a generator touches its upvars, via locals' destructors. Fix #49918
2 parents 6a87289 + f12d7a5 commit d40a0b3

File tree

7 files changed

+106
-40
lines changed

7 files changed

+106
-40
lines changed

src/librustc_traits/dropck_outlives.rs

+32-8
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,38 @@ fn dtorck_constraint_for_ty<'a, 'gcx, 'tcx>(
193193
.map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty))
194194
.collect(),
195195

196-
ty::TyGenerator(def_id, substs, _) => {
197-
// Note that the interior types are ignored here.
198-
// Any type reachable inside the interior must also be reachable
199-
// through the upvars.
200-
substs
201-
.upvar_tys(def_id, tcx)
202-
.map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty))
203-
.collect()
196+
ty::TyGenerator(def_id, substs, _interior) => {
197+
// rust-lang/rust#49918: types can be constructed, stored
198+
// in the interior, and sit idle when generator yields
199+
// (and is subsequently dropped).
200+
//
201+
// It would be nice to descend into interior of a
202+
// generator to determine what effects dropping it might
203+
// have (by looking at any drop effects associated with
204+
// its interior).
205+
//
206+
// However, the interior's representation uses things like
207+
// TyGeneratorWitness that explicitly assume they are not
208+
// traversed in such a manner. So instead, we will
209+
// simplify things for now by treating all generators as
210+
// if they were like trait objects, where its upvars must
211+
// all be alive for the generator's (potential)
212+
// destructor.
213+
//
214+
// In particular, skipping over `_interior` is safe
215+
// because any side-effects from dropping `_interior` can
216+
// only take place through references with lifetimes
217+
// derived from lifetimes attached to the upvars, and we
218+
// *do* incorporate the upvars here.
219+
220+
let constraint = DtorckConstraint {
221+
outlives: substs.upvar_tys(def_id, tcx).map(|t| t.into()).collect(),
222+
dtorck_types: vec![],
223+
overflows: vec![],
224+
};
225+
debug!("dtorck_constraint: generator {:?} => {:?}", def_id, constraint);
226+
227+
Ok(constraint)
204228
}
205229

206230
ty::TyAdt(def, substs) => {
+27-11
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
1-
error: compilation successful
2-
--> $DIR/borrowing.rs:15:1
1+
error[E0597]: `a` does not live long enough
2+
--> $DIR/borrowing.rs:18:18
33
|
4-
LL | / fn main() { #![rustc_error] // rust-lang/rust#49855
5-
LL | | let _b = {
6-
LL | | let a = 3;
7-
LL | | unsafe { (|| yield &a).resume() }
8-
... |
9-
LL | | };
10-
LL | | }
11-
| |_^
4+
LL | unsafe { (|| yield &a).resume() }
5+
| ^^^^^^^^^^^^^
6+
| |
7+
| borrowed value does not live long enough
8+
| borrow may end up in a temporary, created here
9+
LL | //~^ ERROR: `a` does not live long enough
10+
LL | };
11+
| -- temporary later dropped here, potentially using the reference
12+
| |
13+
| borrowed value only lives until here
1214

13-
error: aborting due to previous error
15+
error[E0597]: `a` does not live long enough
16+
--> $DIR/borrowing.rs:24:9
17+
|
18+
LL | / || {
19+
LL | | yield &a
20+
LL | | //~^ ERROR: `a` does not live long enough
21+
LL | | }
22+
| |_________^ borrowed value does not live long enough
23+
LL | };
24+
| - borrowed value only lives until here
25+
LL | }
26+
| - borrow later used here, when `_b` is dropped
27+
28+
error: aborting due to 2 previous errors
1429

30+
For more information about this error, try `rustc --explain E0597`.

src/test/ui/generator/borrowing.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![feature(generators, generator_trait, rustc_attrs)]
11+
#![feature(generators, generator_trait)]
1212

1313
use std::ops::Generator;
1414

15-
fn main() { #![rustc_error] // rust-lang/rust#49855
15+
fn main() {
1616
let _b = {
1717
let a = 3;
1818
unsafe { (|| yield &a).resume() }
+16-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
error: compilation successful
2-
--> $DIR/dropck.rs:16:1
1+
error[E0597]: `ref_` does not live long enough
2+
--> $DIR/dropck.rs:22:11
33
|
4-
LL | / fn main() { #![rustc_error] // rust-lang/rust#49855
5-
LL | | let (cell, mut gen);
6-
LL | | cell = Box::new(RefCell::new(0));
7-
LL | | let ref_ = Box::leak(Box::new(Some(cell.borrow_mut())));
8-
... |
9-
LL | | // drops the RefCell and then the Ref, leading to use-after-free
10-
LL | | }
11-
| |_^
4+
LL | gen = || {
5+
| ___________^
6+
LL | | // but the generator can use it to drop a `Ref<'a, i32>`.
7+
LL | | let _d = ref_.take(); //~ ERROR `ref_` does not live long enough
8+
LL | | yield;
9+
LL | | };
10+
| |_____^ borrowed value does not live long enough
11+
...
12+
LL | }
13+
| -
14+
| |
15+
| borrowed value only lives until here
16+
| borrow later used here, when `gen` is dropped
1217

1318
error: aborting due to previous error
1419

20+
For more information about this error, try `rustc --explain E0597`.

src/test/ui/generator/dropck.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![feature(generators, generator_trait, box_leak, rustc_attrs)]
11+
#![feature(generators, generator_trait, box_leak)]
1212

1313
use std::cell::RefCell;
1414
use std::ops::Generator;
1515

16-
fn main() { #![rustc_error] // rust-lang/rust#49855
16+
fn main() {
1717
let (cell, mut gen);
1818
cell = Box::new(RefCell::new(0));
1919
let ref_ = Box::leak(Box::new(Some(cell.borrow_mut())));
20+
//~^ ERROR `*cell` does not live long enough [E0597]
2021
// the upvar is the non-dropck `&mut Option<Ref<'a, i32>>`.
2122
gen = || {
2223
// but the generator can use it to drop a `Ref<'a, i32>`.

src/test/ui/generator/dropck.stderr

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1+
error[E0597]: `*cell` does not live long enough
2+
--> $DIR/dropck.rs:19:40
3+
|
4+
LL | let ref_ = Box::leak(Box::new(Some(cell.borrow_mut())));
5+
| ^^^^ borrowed value does not live long enough
6+
...
7+
LL | }
8+
| - `*cell` dropped here while still borrowed
9+
|
10+
= note: values in a scope are dropped in the opposite order they are created
11+
112
error[E0597]: `ref_` does not live long enough
2-
--> $DIR/dropck.rs:23:18
13+
--> $DIR/dropck.rs:24:18
314
|
415
LL | gen = || {
516
| -- capture occurs here
@@ -12,6 +23,6 @@ LL | }
1223
|
1324
= note: values in a scope are dropped in the opposite order they are created
1425

15-
error: aborting due to previous error
26+
error: aborting due to 2 previous errors
1627

1728
For more information about this error, try `rustc --explain E0597`.

src/test/ui/generator/ref-escapes-but-not-over-yield.nll.stderr

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
error[E0597]: `b` does not live long enough
22
--> $DIR/ref-escapes-but-not-over-yield.rs:24:13
33
|
4-
LL | a = &b;
5-
| ^^ borrowed value does not live long enough
6-
LL | //~^ ERROR `b` does not live long enough
7-
LL | };
8-
| - borrowed value only lives until here
4+
LL | let mut b = move || {
5+
| _________________-
6+
LL | | yield();
7+
LL | | let b = 5;
8+
LL | | a = &b;
9+
| | ^^ borrowed value does not live long enough
10+
LL | | //~^ ERROR `b` does not live long enough
11+
LL | | };
12+
| | -
13+
| | |
14+
| | borrowed value only lives until here
15+
| |_____temporary later dropped here, potentially using the reference
16+
| borrow may end up in a temporary, created here
917

1018
error: aborting due to previous error
1119

0 commit comments

Comments
 (0)