Skip to content

Commit add169d

Browse files
committed
Distinguish binding assignments, use Ty::needs_drop
This better captures the actual behavior, rather than using hacks around whether the assignment has any projections.
1 parent 1c12c92 commit add169d

File tree

3 files changed

+48
-17
lines changed

3 files changed

+48
-17
lines changed

compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs

+19-14
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ use crate::{
66
use hir::{def_id::DefId, Body, HirId, HirIdMap};
77
use rustc_data_structures::stable_set::FxHashSet;
88
use rustc_hir as hir;
9-
use rustc_middle::hir::map::Map;
9+
use rustc_middle::ty::{ParamEnv, TyCtxt};
1010

1111
pub(super) fn find_consumed_and_borrowed<'a, 'tcx>(
1212
fcx: &'a FnCtxt<'a, 'tcx>,
1313
def_id: DefId,
1414
body: &'tcx Body<'tcx>,
1515
) -> ConsumedAndBorrowedPlaces {
16-
let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx.hir());
16+
let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx, fcx.param_env);
1717
expr_use_visitor.consume_body(fcx, def_id, body);
1818
expr_use_visitor.places
1919
}
@@ -36,14 +36,16 @@ pub(super) struct ConsumedAndBorrowedPlaces {
3636
/// Interesting values are those that are either dropped or borrowed. For dropped values, we also
3737
/// record the parent expression, which is the point where the drop actually takes place.
3838
struct ExprUseDelegate<'tcx> {
39-
hir: Map<'tcx>,
39+
tcx: TyCtxt<'tcx>,
40+
param_env: ParamEnv<'tcx>,
4041
places: ConsumedAndBorrowedPlaces,
4142
}
4243

4344
impl<'tcx> ExprUseDelegate<'tcx> {
44-
fn new(hir: Map<'tcx>) -> Self {
45+
fn new(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
4546
Self {
46-
hir,
47+
tcx,
48+
param_env,
4749
places: ConsumedAndBorrowedPlaces {
4850
consumed: <_>::default(),
4951
borrowed: <_>::default(),
@@ -77,7 +79,7 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
7779
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
7880
diag_expr_id: HirId,
7981
) {
80-
let parent = match self.hir.find_parent_node(place_with_id.hir_id) {
82+
let parent = match self.tcx.hir().find_parent_node(place_with_id.hir_id) {
8183
Some(parent) => parent,
8284
None => place_with_id.hir_id,
8385
};
@@ -108,20 +110,23 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
108110
diag_expr_id: HirId,
109111
) {
110112
debug!("mutate {assignee_place:?}; diag_expr_id={diag_expr_id:?}");
111-
// Count mutations as a borrow when done through a projection.
112-
//
113-
// The goal here is to catch cases such as `x.y = 42`, since MIR will count this
114-
// as a borrow of `x`, and we need to match that behavior.
115-
//
116-
// FIXME(eholk): this is probably still more conservative than we need to be. For example,
117-
// we may need to count `*x = 42` as a borrow of `x`, since it overwrites all of `x`.
118-
if !assignee_place.place.projections.is_empty() {
113+
// If the type being assigned needs dropped, then the mutation counts as a borrow
114+
// since it is essentially doing `Drop::drop(&mut x); x = new_value;`.
115+
if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) {
119116
self.places
120117
.borrowed
121118
.insert(TrackedValue::from_place_with_projections_allowed(assignee_place));
122119
}
123120
}
124121

122+
fn bind(
123+
&mut self,
124+
binding_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
125+
diag_expr_id: HirId,
126+
) {
127+
debug!("bind {binding_place:?}; diag_expr_id={diag_expr_id:?}");
128+
}
129+
125130
fn fake_read(
126131
&mut self,
127132
_place: expr_use_visitor::Place<'tcx>,

compiler/rustc_typeck/src/expr_use_visitor.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ pub trait Delegate<'tcx> {
5151
/// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
5252
fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId);
5353

54+
/// The path at `binding_place` is a binding that is being initialized.
55+
///
56+
/// This covers cases such as `let x = 42;`
57+
fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
58+
// Bindings can normally be treated as a regular assignment, so by default we
59+
// forward this to the mutate callback.
60+
self.mutate(binding_place, diag_expr_id)
61+
}
62+
5463
/// The `place` should be a fake read because of specified `cause`.
5564
fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId);
5665
}
@@ -648,11 +657,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
648657
let pat_ty = return_if_err!(mc.node_ty(pat.hir_id));
649658
debug!("walk_pat: pat_ty={:?}", pat_ty);
650659

651-
// Each match binding is effectively an assignment to the
652-
// binding being produced.
653660
let def = Res::Local(canonical_id);
654661
if let Ok(ref binding_place) = mc.cat_res(pat.hir_id, pat.span, pat_ty, def) {
655-
delegate.mutate(binding_place, binding_place.hir_id);
662+
delegate.bind(binding_place, binding_place.hir_id);
656663
}
657664

658665
// It is also a borrow or copy/move of the value being matched.
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// edition:2021
2+
// compile-flags: -Zdrop-tracking
3+
// build-pass
4+
5+
struct A;
6+
impl Drop for A { fn drop(&mut self) {} }
7+
8+
pub async fn f() {
9+
let mut a = A;
10+
a = A;
11+
drop(a);
12+
async {}.await;
13+
}
14+
15+
fn assert_send<T: Send>(_: T) {}
16+
17+
fn main() {
18+
let _ = f();
19+
}

0 commit comments

Comments
 (0)