Track drops across multiple yields
This commit is contained in:
parent
f712df8c5d
commit
c4dee40170
5 changed files with 73 additions and 26 deletions
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(|| {
|
||||
|
|
15
src/test/ui/generator/drop-yield-twice.rs
Normal file
15
src/test/ui/generator/drop-yield-twice.rs
Normal 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) {}
|
25
src/test/ui/generator/drop-yield-twice.stderr
Normal file
25
src/test/ui/generator/drop-yield-twice.stderr
Normal 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
|
||||
|
Loading…
Add table
Reference in a new issue