Track drops across multiple yields

This commit is contained in:
Eric Holk 2021-10-22 12:45:02 -07:00
parent f712df8c5d
commit c4dee40170
5 changed files with 73 additions and 26 deletions

View file

@ -308,7 +308,7 @@ pub struct ScopeTree {
/// The reason is that semantically, until the `box` expression returns,
/// the values are still owned by their containing expressions. So
/// we'll see that `&x`.
pub yield_in_scope: FxHashMap<Scope, YieldData>,
pub yield_in_scope: FxHashMap<Scope, Vec<YieldData>>,
/// The number of visit_expr and visit_pat calls done in the body.
/// Used to sanity check visit_expr/visit_pat call count when
@ -423,8 +423,8 @@ impl ScopeTree {
/// Checks whether the given scope contains a `yield`. If so,
/// returns `Some(YieldData)`. If not, returns `None`.
pub fn yield_in_scope(&self, scope: Scope) -> Option<YieldData> {
self.yield_in_scope.get(&scope).cloned()
pub fn yield_in_scope(&self, scope: Scope) -> Option<&Vec<YieldData>> {
self.yield_in_scope.get(&scope)
}
/// Gives the number of expressions visited in a body.

View file

@ -365,7 +365,8 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
let target_scopes = visitor.fixup_scopes.drain(start_point..);
for scope in target_scopes {
let mut yield_data = visitor.scope_tree.yield_in_scope.get_mut(&scope).unwrap();
let mut yield_data =
visitor.scope_tree.yield_in_scope.get_mut(&scope).unwrap().last_mut().unwrap();
let count = yield_data.expr_and_pat_count;
let span = yield_data.span;
@ -428,7 +429,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
};
let data =
YieldData { span, expr_and_pat_count: visitor.expr_and_pat_count, source: *source };
visitor.scope_tree.yield_in_scope.insert(scope, data);
match visitor.scope_tree.yield_in_scope.get_mut(&scope) {
Some(yields) => yields.push(data),
None => {
visitor.scope_tree.yield_in_scope.insert(scope, vec![data]);
}
}
if visitor.pessimistic_yield {
debug!("resolve_expr in pessimistic_yield - marking scope {:?} for fixup", scope);
visitor.fixup_scopes.push(scope);

View file

@ -69,29 +69,29 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
//
// See the mega-comment at `yield_in_scope` for a proof.
debug!(
"comparing counts yield: {} self: {}, source_span = {:?}",
yield_data.expr_and_pat_count, self.expr_count, source_span
);
yield_data
.iter()
.find(|yield_data| {
debug!(
"comparing counts yield: {} self: {}, source_span = {:?}",
yield_data.expr_and_pat_count, self.expr_count, source_span
);
match self.drop_ranges.get(&hir_id) {
Some(range) if range.contains(yield_data.expr_and_pat_count) => {
debug!("value is dropped at yield point; not recording");
return None
}
_ => (),
}
match self.drop_ranges.get(&hir_id) {
Some(range) if range.contains(yield_data.expr_and_pat_count) => {
debug!("value is dropped at yield point; not recording");
return false;
}
_ => (),
}
// If it is a borrowing happening in the guard,
// it needs to be recorded regardless because they
// do live across this yield point.
if guard_borrowing_from_pattern
|| yield_data.expr_and_pat_count >= self.expr_count
{
Some(yield_data)
} else {
None
}
// If it is a borrowing happening in the guard,
// it needs to be recorded regardless because they
// do live across this yield point.
guard_borrowing_from_pattern
|| yield_data.expr_and_pat_count >= self.expr_count
})
.cloned()
})
})
.unwrap_or_else(|| {

View file

@ -0,0 +1,15 @@
#![feature(negative_impls, generators)]
struct Foo(i32);
impl !Send for Foo {}
fn main() {
assert_send(|| { //~ ERROR generator cannot be sent between threads safely
let guard = Foo(42);
yield;
drop(guard);
yield;
})
}
fn assert_send<T: Send>(_: T) {}

View file

@ -0,0 +1,25 @@
error: generator cannot be sent between threads safely
--> $DIR/drop-yield-twice.rs:7:5
|
LL | assert_send(|| {
| ^^^^^^^^^^^ generator is not `Send`
|
= help: within `[generator@$DIR/drop-yield-twice.rs:7:17: 12:6]`, the trait `Send` is not implemented for `Foo`
note: generator is not `Send` as this value is used across a yield
--> $DIR/drop-yield-twice.rs:9:9
|
LL | let guard = Foo(42);
| ----- has type `Foo` which is not `Send`
LL | yield;
| ^^^^^ yield occurs here, with `guard` maybe used later
...
LL | })
| - `guard` is later dropped here
note: required by a bound in `assert_send`
--> $DIR/drop-yield-twice.rs:15:19
|
LL | fn assert_send<T: Send>(_: T) {}
| ^^^^ required by this bound in `assert_send`
error: aborting due to previous error