Skip to content

Commit f664cfc

Browse files
committed
Make generator and async-await tests pass
The main change needed to make this work is to do a pessimistic over- approximation for AssignOps. The existing ScopeTree analysis in region.rs works by doing both left to right and right to left order and then choosing the most conservative ordering. This behavior is needed because AssignOp's evaluation order depends on whether it is a primitive type or an overloaded operator, which runs as a method call. This change mimics the same behavior as region.rs in generator_interior.rs. Issue rust-lang#57478
1 parent c4dee40 commit f664cfc

File tree

2 files changed

+106
-38
lines changed

2 files changed

+106
-38
lines changed

compiler/rustc_typeck/src/check/generator_interior.rs

Lines changed: 104 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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::place::{Place, PlaceBase};
1819
use rustc_middle::middle::region::{self, YieldData};
1920
use rustc_middle::ty::{self, Ty, TyCtxt};
2021
use rustc_span::symbol::sym;
@@ -222,30 +223,37 @@ pub fn resolve_interior<'a, 'tcx>(
222223
) {
223224
let body = fcx.tcx.hir().body(body_id);
224225

225-
let mut drop_range_visitor = DropRangeVisitor::default();
226-
227-
// Run ExprUseVisitor to find where values are consumed.
228-
ExprUseVisitor::new(
229-
&mut drop_range_visitor,
230-
&fcx.infcx,
231-
def_id.expect_local(),
232-
fcx.param_env,
233-
&fcx.typeck_results.borrow(),
234-
)
235-
.consume_body(body);
236-
intravisit::walk_body(&mut drop_range_visitor, body);
237-
238-
let mut visitor = InteriorVisitor {
239-
fcx,
240-
types: FxIndexSet::default(),
241-
region_scope_tree: fcx.tcx.region_scope_tree(def_id),
242-
expr_count: 0,
243-
kind,
244-
prev_unresolved_span: None,
245-
guard_bindings: <_>::default(),
246-
guard_bindings_set: <_>::default(),
247-
linted_values: <_>::default(),
248-
drop_ranges: drop_range_visitor.drop_ranges,
226+
let mut visitor = {
227+
let mut drop_range_visitor = DropRangeVisitor {
228+
consumed_places: <_>::default(),
229+
borrowed_places: <_>::default(),
230+
drop_ranges: vec![<_>::default()],
231+
expr_count: 0,
232+
};
233+
234+
// Run ExprUseVisitor to find where values are consumed.
235+
ExprUseVisitor::new(
236+
&mut drop_range_visitor,
237+
&fcx.infcx,
238+
def_id.expect_local(),
239+
fcx.param_env,
240+
&fcx.typeck_results.borrow(),
241+
)
242+
.consume_body(body);
243+
intravisit::walk_body(&mut drop_range_visitor, body);
244+
245+
InteriorVisitor {
246+
fcx,
247+
types: FxIndexSet::default(),
248+
region_scope_tree: fcx.tcx.region_scope_tree(def_id),
249+
expr_count: 0,
250+
kind,
251+
prev_unresolved_span: None,
252+
guard_bindings: <_>::default(),
253+
guard_bindings_set: <_>::default(),
254+
linted_values: <_>::default(),
255+
drop_ranges: drop_range_visitor.drop_ranges.pop().unwrap(),
256+
}
249257
};
250258
intravisit::walk_body(&mut visitor, body);
251259

@@ -656,17 +664,37 @@ fn check_must_not_suspend_def(
656664
}
657665

658666
/// This struct facilitates computing the ranges for which a place is uninitialized.
659-
#[derive(Default)]
660667
struct DropRangeVisitor {
661668
consumed_places: HirIdSet,
662-
drop_ranges: HirIdMap<DropRange>,
669+
borrowed_places: HirIdSet,
670+
drop_ranges: Vec<HirIdMap<DropRange>>,
663671
expr_count: usize,
664672
}
665673

666674
impl DropRangeVisitor {
667675
fn record_drop(&mut self, hir_id: HirId) {
668-
debug!("marking {:?} as dropped at {}", hir_id, self.expr_count);
669-
self.drop_ranges.insert(hir_id, DropRange { dropped_at: self.expr_count });
676+
let drop_ranges = self.drop_ranges.last_mut().unwrap();
677+
if self.borrowed_places.contains(&hir_id) {
678+
debug!("not marking {:?} as dropped because it is borrowed at some point", hir_id);
679+
} else if self.consumed_places.contains(&hir_id) {
680+
debug!("marking {:?} as dropped at {}", hir_id, self.expr_count);
681+
drop_ranges.insert(hir_id, DropRange { dropped_at: self.expr_count });
682+
}
683+
}
684+
685+
fn push_drop_scope(&mut self) {
686+
self.drop_ranges.push(<_>::default());
687+
}
688+
689+
fn pop_and_merge_drop_scope(&mut self) {
690+
let mut old_last = self.drop_ranges.pop().unwrap();
691+
let drop_ranges = self.drop_ranges.last_mut().unwrap();
692+
for (k, v) in old_last.drain() {
693+
match drop_ranges.get(&k).cloned() {
694+
Some(v2) => drop_ranges.insert(k, v.intersect(&v2)),
695+
None => drop_ranges.insert(k, v),
696+
};
697+
}
670698
}
671699

672700
/// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all
@@ -685,6 +713,14 @@ impl DropRangeVisitor {
685713
}
686714
}
687715

716+
fn place_hir_id(place: &Place<'_>) -> Option<HirId> {
717+
match place.base {
718+
PlaceBase::Rvalue | PlaceBase::StaticItem => None,
719+
PlaceBase::Local(hir_id)
720+
| PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => Some(hir_id),
721+
}
722+
}
723+
688724
impl<'tcx> expr_use_visitor::Delegate<'tcx> for DropRangeVisitor {
689725
fn consume(
690726
&mut self,
@@ -693,14 +729,16 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for DropRangeVisitor {
693729
) {
694730
debug!("consume {:?}; diag_expr_id={:?}", place_with_id, diag_expr_id);
695731
self.consumed_places.insert(place_with_id.hir_id);
732+
place_hir_id(&place_with_id.place).map(|place| self.consumed_places.insert(place));
696733
}
697734

698735
fn borrow(
699736
&mut self,
700-
_place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
737+
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
701738
_diag_expr_id: hir::HirId,
702739
_bk: rustc_middle::ty::BorrowKind,
703740
) {
741+
place_hir_id(&place_with_id.place).map(|place| self.borrowed_places.insert(place));
704742
}
705743

706744
fn mutate(
@@ -726,17 +764,44 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor {
726764
NestedVisitorMap::None
727765
}
728766

729-
fn visit_expr(&mut self, expr: &Expr<'_>) {
730-
intravisit::walk_expr(self, expr);
767+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
768+
match expr.kind {
769+
ExprKind::AssignOp(_, lhs, rhs) => {
770+
// These operations are weird because their order of evaluation depends on whether
771+
// the operator is overloaded. In a perfect world, we'd just ask the type checker
772+
// whether this is a method call, but we also need to match the expression IDs
773+
// from RegionResolutionVisitor. RegionResolutionVisitor doesn't know the order,
774+
// so it runs both orders and picks the most conservative. We'll mirror that here.
775+
let mut old_count = self.expr_count;
776+
intravisit::walk_expr(self, lhs);
777+
intravisit::walk_expr(self, rhs);
778+
779+
self.push_drop_scope();
780+
std::mem::swap(&mut old_count, &mut self.expr_count);
781+
intravisit::walk_expr(self, rhs);
782+
intravisit::walk_expr(self, lhs);
783+
784+
// We should have visited the same number of expressions in either order.
785+
assert_eq!(old_count, self.expr_count);
786+
787+
self.pop_and_merge_drop_scope();
788+
}
789+
_ => intravisit::walk_expr(self, expr),
790+
}
731791

732792
self.expr_count += 1;
793+
self.consume_expr(expr);
794+
}
733795

734-
if self.consumed_places.contains(&expr.hir_id) {
735-
self.consume_expr(expr);
736-
}
796+
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
797+
intravisit::walk_pat(self, pat);
798+
799+
// Increment expr_count here to match what InteriorVisitor expects.
800+
self.expr_count += 1;
737801
}
738802
}
739803

804+
#[derive(Clone)]
740805
struct DropRange {
741806
/// The post-order id of the point where this expression is dropped.
742807
///
@@ -745,7 +810,11 @@ struct DropRange {
745810
}
746811

747812
impl DropRange {
813+
fn intersect(&self, other: &Self) -> Self {
814+
Self { dropped_at: self.dropped_at.max(other.dropped_at) }
815+
}
816+
748817
fn contains(&self, id: usize) -> bool {
749-
id >= self.dropped_at
818+
id > self.dropped_at
750819
}
751820
}

src/test/ui/async-await/async-fn-nonsend.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ async fn non_send_temporary_in_match() {
3636
}
3737

3838
async fn non_sync_with_method_call() {
39-
39+
// FIXME: it would be nice for this to work
4040
let f: &mut std::fmt::Formatter = panic!();
4141
if non_sync().fmt(f).unwrap() == () {
4242
fut().await;
@@ -47,9 +47,8 @@ fn assert_send(_: impl Send) {}
4747

4848
pub fn pass_assert() {
4949
assert_send(local_dropped_before_await());
50-
5150
assert_send(non_send_temporary_in_match());
5251
//~^ ERROR future cannot be sent between threads safely
5352
assert_send(non_sync_with_method_call());
54-
53+
//~^ ERROR future cannot be sent between threads safely
5554
}

0 commit comments

Comments
 (0)