Skip to content

Commit f246c0b

Browse files
committed
Attribute drop to parent expression of the consume point
This is needed to handle cases like `[a, b.await, c]`. `ExprUseVisitor` considers `a` to be consumed when it is passed to the array, but the array is not quite live yet at that point. This means we were missing the `a` value across the await point. Attributing drops to the parent expression means we do not consider the value consumed until the consuming expression has finished. Issue rust-lang#57478
1 parent f664cfc commit f246c0b

File tree

5 files changed

+87
-31
lines changed

5 files changed

+87
-31
lines changed

compiler/rustc_typeck/src/check/generator_interior.rs

+47-23
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use crate::expr_use_visitor::{self, ExprUseVisitor};
77

88
use super::FnCtxt;
9-
use hir::HirIdMap;
9+
use hir::{HirIdMap, Node};
1010
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
1111
use rustc_errors::pluralize;
1212
use rustc_hir as hir;
@@ -15,6 +15,7 @@ use rustc_hir::def_id::DefId;
1515
use rustc_hir::hir_id::HirIdSet;
1616
use rustc_hir::intravisit::{self, Visitor};
1717
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
18+
use rustc_middle::hir::map::Map;
1819
use rustc_middle::hir::place::{Place, PlaceBase};
1920
use rustc_middle::middle::region::{self, YieldData};
2021
use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -225,6 +226,7 @@ pub fn resolve_interior<'a, 'tcx>(
225226

226227
let mut visitor = {
227228
let mut drop_range_visitor = DropRangeVisitor {
229+
hir: fcx.tcx.hir(),
228230
consumed_places: <_>::default(),
229231
borrowed_places: <_>::default(),
230232
drop_ranges: vec![<_>::default()],
@@ -664,19 +666,28 @@ fn check_must_not_suspend_def(
664666
}
665667

666668
/// This struct facilitates computing the ranges for which a place is uninitialized.
667-
struct DropRangeVisitor {
668-
consumed_places: HirIdSet,
669+
struct DropRangeVisitor<'tcx> {
670+
hir: Map<'tcx>,
671+
/// Maps a HirId to a set of HirIds that are dropped by that node.
672+
consumed_places: HirIdMap<HirIdSet>,
669673
borrowed_places: HirIdSet,
670674
drop_ranges: Vec<HirIdMap<DropRange>>,
671675
expr_count: usize,
672676
}
673677

674-
impl DropRangeVisitor {
678+
impl DropRangeVisitor<'tcx> {
679+
fn mark_consumed(&mut self, consumer: HirId, target: HirId) {
680+
if !self.consumed_places.contains_key(&consumer) {
681+
self.consumed_places.insert(consumer, <_>::default());
682+
}
683+
self.consumed_places.get_mut(&consumer).map(|places| places.insert(target));
684+
}
685+
675686
fn record_drop(&mut self, hir_id: HirId) {
676687
let drop_ranges = self.drop_ranges.last_mut().unwrap();
677688
if self.borrowed_places.contains(&hir_id) {
678689
debug!("not marking {:?} as dropped because it is borrowed at some point", hir_id);
679-
} else if self.consumed_places.contains(&hir_id) {
690+
} else {
680691
debug!("marking {:?} as dropped at {}", hir_id, self.expr_count);
681692
drop_ranges.insert(hir_id, DropRange { dropped_at: self.expr_count });
682693
}
@@ -700,15 +711,24 @@ impl DropRangeVisitor {
700711
/// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all
701712
/// expressions. This method consumes a little deeper into the expression when needed.
702713
fn consume_expr(&mut self, expr: &hir::Expr<'_>) {
703-
self.record_drop(expr.hir_id);
704-
match expr.kind {
705-
hir::ExprKind::Path(hir::QPath::Resolved(
706-
_,
707-
hir::Path { res: hir::def::Res::Local(hir_id), .. },
708-
)) => {
709-
self.record_drop(*hir_id);
714+
debug!("consuming expr {:?}, count={}", expr.hir_id, self.expr_count);
715+
let places = self
716+
.consumed_places
717+
.get(&expr.hir_id)
718+
.map_or(vec![], |places| places.iter().cloned().collect());
719+
for place in places {
720+
self.record_drop(place);
721+
if let Some(Node::Expr(expr)) = self.hir.find(place) {
722+
match expr.kind {
723+
hir::ExprKind::Path(hir::QPath::Resolved(
724+
_,
725+
hir::Path { res: hir::def::Res::Local(hir_id), .. },
726+
)) => {
727+
self.record_drop(*hir_id);
728+
}
729+
_ => (),
730+
}
710731
}
711-
_ => (),
712732
}
713733
}
714734
}
@@ -721,15 +741,19 @@ fn place_hir_id(place: &Place<'_>) -> Option<HirId> {
721741
}
722742
}
723743

724-
impl<'tcx> expr_use_visitor::Delegate<'tcx> for DropRangeVisitor {
744+
impl<'tcx> expr_use_visitor::Delegate<'tcx> for DropRangeVisitor<'tcx> {
725745
fn consume(
726746
&mut self,
727747
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
728748
diag_expr_id: hir::HirId,
729749
) {
730-
debug!("consume {:?}; diag_expr_id={:?}", place_with_id, diag_expr_id);
731-
self.consumed_places.insert(place_with_id.hir_id);
732-
place_hir_id(&place_with_id.place).map(|place| self.consumed_places.insert(place));
750+
let parent = match self.hir.find_parent_node(place_with_id.hir_id) {
751+
Some(parent) => parent,
752+
None => place_with_id.hir_id,
753+
};
754+
debug!("consume {:?}; diag_expr_id={:?}, using parent {:?}", place_with_id, diag_expr_id, parent);
755+
self.mark_consumed(parent, place_with_id.hir_id);
756+
place_hir_id(&place_with_id.place).map(|place| self.mark_consumed(parent, place));
733757
}
734758

735759
fn borrow(
@@ -757,7 +781,7 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for DropRangeVisitor {
757781
}
758782
}
759783

760-
impl<'tcx> Visitor<'tcx> for DropRangeVisitor {
784+
impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
761785
type Map = intravisit::ErasedMap<'tcx>;
762786

763787
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@@ -766,20 +790,20 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor {
766790

767791
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
768792
match expr.kind {
769-
ExprKind::AssignOp(_, lhs, rhs) => {
793+
ExprKind::AssignOp(_op, lhs, rhs) => {
770794
// These operations are weird because their order of evaluation depends on whether
771795
// the operator is overloaded. In a perfect world, we'd just ask the type checker
772796
// whether this is a method call, but we also need to match the expression IDs
773797
// from RegionResolutionVisitor. RegionResolutionVisitor doesn't know the order,
774798
// so it runs both orders and picks the most conservative. We'll mirror that here.
775799
let mut old_count = self.expr_count;
776-
intravisit::walk_expr(self, lhs);
777-
intravisit::walk_expr(self, rhs);
800+
self.visit_expr(lhs);
801+
self.visit_expr(rhs);
778802

779803
self.push_drop_scope();
780804
std::mem::swap(&mut old_count, &mut self.expr_count);
781-
intravisit::walk_expr(self, rhs);
782-
intravisit::walk_expr(self, lhs);
805+
self.visit_expr(rhs);
806+
self.visit_expr(lhs);
783807

784808
// We should have visited the same number of expressions in either order.
785809
assert_eq!(old_count, self.expr_count);

src/test/ui/async-await/unresolved_type_param.rs

+8
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,16 @@ async fn bar<T>() -> () {}
88
async fn foo() {
99
bar().await;
1010
//~^ ERROR type inside `async fn` body must be known in this context
11+
//~| ERROR type inside `async fn` body must be known in this context
12+
//~| ERROR type inside `async fn` body must be known in this context
1113
//~| NOTE cannot infer type for type parameter `T`
14+
//~| NOTE cannot infer type for type parameter `T`
15+
//~| NOTE cannot infer type for type parameter `T`
16+
//~| NOTE the type is part of the `async fn` body because of this `await`
1217
//~| NOTE the type is part of the `async fn` body because of this `await`
18+
//~| NOTE the type is part of the `async fn` body because of this `await`
19+
//~| NOTE in this expansion of desugaring of `await`
20+
//~| NOTE in this expansion of desugaring of `await`
1321
//~| NOTE in this expansion of desugaring of `await`
1422
}
1523
fn main() {}

src/test/ui/async-await/unresolved_type_param.stderr

+25-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,30 @@ note: the type is part of the `async fn` body because of this `await`
1010
LL | bar().await;
1111
| ^^^^^^
1212

13-
error: aborting due to previous error
13+
error[E0698]: type inside `async fn` body must be known in this context
14+
--> $DIR/unresolved_type_param.rs:9:5
15+
|
16+
LL | bar().await;
17+
| ^^^ cannot infer type for type parameter `T` declared on the function `bar`
18+
|
19+
note: the type is part of the `async fn` body because of this `await`
20+
--> $DIR/unresolved_type_param.rs:9:5
21+
|
22+
LL | bar().await;
23+
| ^^^^^^^^^^^
24+
25+
error[E0698]: type inside `async fn` body must be known in this context
26+
--> $DIR/unresolved_type_param.rs:9:5
27+
|
28+
LL | bar().await;
29+
| ^^^ cannot infer type for type parameter `T` declared on the function `bar`
30+
|
31+
note: the type is part of the `async fn` body because of this `await`
32+
--> $DIR/unresolved_type_param.rs:9:5
33+
|
34+
LL | bar().await;
35+
| ^^^^^^^^^^^
36+
37+
error: aborting due to 3 previous errors
1438

1539
For more information about this error, try `rustc --explain E0698`.

src/test/ui/lint/must_not_suspend/dedup.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ async fn wheeee<T>(t: T) {
1313
}
1414

1515
async fn yes() {
16-
wheeee(No {}).await; //~ ERROR `No` held across
16+
wheeee(&No {}).await; //~ ERROR `No` held across
1717
}
1818

1919
fn main() {
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
error: `No` held across a suspend point, but should not be
2-
--> $DIR/dedup.rs:16:12
2+
--> $DIR/dedup.rs:16:13
33
|
4-
LL | wheeee(No {}).await;
5-
| ^^^^^ ------ the value is held across this suspend point
4+
LL | wheeee(&No {}).await;
5+
| --------^^^^^------- the value is held across this suspend point
66
|
77
note: the lint level is defined here
88
--> $DIR/dedup.rs:3:9
99
|
1010
LL | #![deny(must_not_suspend)]
1111
| ^^^^^^^^^^^^^^^^
1212
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
13-
--> $DIR/dedup.rs:16:12
13+
--> $DIR/dedup.rs:16:13
1414
|
15-
LL | wheeee(No {}).await;
16-
| ^^^^^
15+
LL | wheeee(&No {}).await;
16+
| ^^^^^
1717

1818
error: aborting due to previous error
1919

0 commit comments

Comments
 (0)