Skip to content

Commit a9a2b89

Browse files
committed
always create nodes for call and yield destination locals
1 parent 1fedadd commit a9a2b89

File tree

4 files changed

+68
-106
lines changed

4 files changed

+68
-106
lines changed

compiler/rustc_mir_dataflow/src/impls/live_borrows.rs

Lines changed: 53 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,8 @@ use rustc_data_structures::graph::implementation::{Graph, NodeIndex};
128128
use rustc_middle::mir::visit::PlaceContext;
129129
use rustc_middle::mir::visit::Visitor;
130130
use rustc_middle::mir::*;
131-
use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitable};
131+
use rustc_middle::ty;
132132

133-
use core::ops::ControlFlow;
134133
use either::Either;
135134

136135
#[derive(Copy, Clone, Debug)]
@@ -158,43 +157,6 @@ impl NodeKind {
158157
}
159158
}
160159

161-
/// TypeVisitor that looks for `ty::Ref`, `ty::RawPtr`. Additionally looks for `ty::Param`,
162-
/// which could themselves refer to references or raw pointers.
163-
struct MaybeHasRefsOrPointersVisitor {
164-
has_refs_or_pointers: bool,
165-
has_params: bool,
166-
}
167-
168-
impl MaybeHasRefsOrPointersVisitor {
169-
fn new() -> Self {
170-
MaybeHasRefsOrPointersVisitor { has_refs_or_pointers: false, has_params: false }
171-
}
172-
}
173-
174-
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for MaybeHasRefsOrPointersVisitor {
175-
type BreakTy = ();
176-
177-
#[instrument(skip(self), level = "debug")]
178-
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
179-
match ty.kind() {
180-
ty::Ref(..) => {
181-
self.has_refs_or_pointers = true;
182-
return ControlFlow::Break(());
183-
}
184-
ty::RawPtr(..) => {
185-
self.has_refs_or_pointers = true;
186-
return ControlFlow::Break(());
187-
}
188-
ty::Param(_) => {
189-
self.has_params = true;
190-
}
191-
_ => {}
192-
}
193-
194-
ty.super_visit_with(self)
195-
}
196-
}
197-
198160
/// Used to build a dependency graph between borrows/pointers and the `Local`s that
199161
/// they reference.
200162
/// We add edges to the graph in two kinds of situations:
@@ -265,6 +227,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
265227
match &statement.kind {
266228
StatementKind::Assign(assign) => {
267229
let assign_place = assign.0;
230+
debug!("assign_place_ty: {:?}", assign_place.ty(self.local_decls, self.tcx).ty);
268231
let assign_local = assign_place.local;
269232
self.current_local = Some(assign_local);
270233
debug!("set current_local to {:?}", self.current_local);
@@ -305,12 +268,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
305268
let src_node_kind = NodeKind::Borrow(src_local);
306269
let src_node_idx = self.maybe_create_node(src_node_kind);
307270

308-
let node_idx = if let Some(node_idx) = self.locals_to_node_indexes.get(&place.local)
309-
{
310-
*node_idx
311-
} else {
312-
bug!("should have added a node for the moved borrowed place before");
313-
};
271+
let node_kind = NodeKind::Borrow(place.local);
272+
let node_idx = self.maybe_create_node(node_kind);
314273

315274
debug!(
316275
"adding edge from {:?}({:?}) -> {:?}({:?})",
@@ -346,10 +305,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
346305

347306
self.dep_graph.add_edge(src_node_idx, node_idx, ());
348307
}
349-
_ => {
350-
// FIXME Need to also create edges for `Local`s that correspond to `NodeKind::LocalWithRefs` here
308+
Rvalue::Cast(..) => {
309+
// FIXME we probably should handle pointer casts here directly
310+
351311
self.super_rvalue(rvalue, location)
352312
}
313+
_ => self.super_rvalue(rvalue, location),
353314
}
354315
}
355316

@@ -385,7 +346,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
385346
None => {}
386347
},
387348
_ => {
388-
// FIXME Need to also create edges for `Local`s that correspond to `NodeKind::LocalWithRefs` here
349+
// FIXME I think we probably need to introduce edges here if `place.local`
350+
// corresponds to a `NodeKind::LocalWithRefs`
389351
}
390352
}
391353

@@ -404,26 +366,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
404366
let dest_ty = destination.ty(self.local_decls, self.tcx).ty;
405367
debug!(?dest_ty);
406368

407-
let mut has_refs_or_pointers_visitor = MaybeHasRefsOrPointersVisitor::new();
408-
dest_ty.visit_with(&mut has_refs_or_pointers_visitor);
409-
410-
let dest_might_include_refs_or_pointers =
411-
if has_refs_or_pointers_visitor.has_refs_or_pointers {
412-
true
413-
} else if has_refs_or_pointers_visitor.has_params {
414-
// if we pass in any references or raw pointers as arguments and the
415-
// return type includes type parameters, these type parameters could
416-
// refer to those refs/ptrs, so we have to be conservative here and possibly
417-
// include an edge.
418-
true
419-
} else {
420-
false
421-
};
422-
423-
if dest_might_include_refs_or_pointers {
424-
self.current_local = Some(destination.local);
425-
debug!("self.current_local: {:?}", self.current_local);
426-
}
369+
// To ensure safety we need to add `destination` to the graph as a `Node` with `NodeKind::LocalWithRefs`
370+
// if we pass in any refs/ptrs or `Local`s corresponding to `NodeKind::LocalWithRefs`. The reason for this
371+
// is that the function could include those refs/ptrs in its return value. It's not sufficient
372+
// to look for the existence of `ty::Ref` or `ty::RawPtr` in the type of the return type, since the
373+
// function could also cast pointers to integers e.g. .
374+
self.current_local = Some(destination.local);
427375

428376
self.visit_operand(func, location);
429377
for arg in args {
@@ -436,26 +384,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
436384
let resume_arg_ty = resume_arg.ty(self.local_decls, self.tcx).ty;
437385
debug!(?resume_arg_ty);
438386

439-
let mut has_refs_or_pointers_visitor = MaybeHasRefsOrPointersVisitor::new();
440-
resume_arg_ty.visit_with(&mut has_refs_or_pointers_visitor);
441-
442-
let dest_might_include_refs_or_pointers =
443-
if has_refs_or_pointers_visitor.has_refs_or_pointers {
444-
true
445-
} else if has_refs_or_pointers_visitor.has_params {
446-
// if we pass in any references or raw pointers as arguments and the
447-
// return type includes type parameters, these type parameters could
448-
// refer to those refs/ptrs, so we have to be conservative here and possibly
449-
// include an edge.
450-
true
451-
} else {
452-
false
453-
};
454-
455-
if dest_might_include_refs_or_pointers {
456-
self.current_local = Some(resume_arg.local);
457-
debug!("self.current_local: {:?}", self.current_local);
458-
}
387+
// We may need to add edges from `destination`, see the comment for this statement
388+
// in `TerminatorKind::Call` for the rationale behind this.
389+
self.current_local = Some(resume_arg.local);
459390

460391
self.super_operand(value, location);
461392
}
@@ -578,6 +509,7 @@ pub fn get_borrowed_locals_results<'mir, 'tcx>(
578509
)
579510
}
580511
}
512+
581513
let live_borrows = LiveBorrows::new(body, tcx, borrow_deps);
582514
let results =
583515
live_borrows.into_engine(tcx, body).pass_name("borrowed_locals").iterate_to_fixpoint();
@@ -763,6 +695,15 @@ where
763695
self.visit_rvalue(&assign.1, location);
764696
}
765697
_ => {
698+
if let Some(node_idx) =
699+
self.borrow_deps.locals_to_node_indexes.get(&lhs_place.local)
700+
{
701+
let node = self.borrow_deps.dep_graph.node(*node_idx);
702+
if let NodeKind::LocalWithRefs(_) = node.data {
703+
debug!("killing {:?}", lhs_place.local);
704+
self._trans.kill(lhs_place.local);
705+
}
706+
}
766707
self.super_assign(&assign.0, &assign.1, location);
767708
}
768709
}
@@ -828,18 +769,32 @@ where
828769

829770
#[instrument(skip(self), level = "debug")]
830771
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
831-
match terminator.kind {
832-
TerminatorKind::Call { destination, .. } => {
833-
debug!("killing {:?}", destination.local);
834-
self._trans.kill(destination.local);
772+
match &terminator.kind {
773+
TerminatorKind::Call { destination, args, .. } => {
774+
match destination.projection.as_slice() {
775+
&[] | &[ProjectionElem::OpaqueCast(_)] => {
776+
debug!("killing {:?}", destination.local);
777+
self._trans.kill(destination.local);
778+
779+
for arg in args {
780+
self.visit_operand(arg, location)
781+
}
782+
}
783+
_ => self.super_terminator(terminator, location),
784+
}
835785
}
836-
TerminatorKind::Yield { resume_arg, .. } => {
837-
debug!("killing {:?}", resume_arg.local);
838-
self._trans.kill(resume_arg.local);
786+
TerminatorKind::Yield { resume_arg, value, .. } => {
787+
match resume_arg.projection.as_slice() {
788+
&[] | &[ProjectionElem::OpaqueCast(_)] => {
789+
debug!("killing {:?}", resume_arg.local);
790+
self._trans.kill(resume_arg.local);
791+
792+
self.visit_operand(value, location)
793+
}
794+
_ => self.super_terminator(terminator, location),
795+
}
839796
}
840-
_ => {}
797+
_ => self.super_terminator(terminator, location),
841798
}
842-
843-
self.super_terminator(terminator, location);
844799
}
845800
}

compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ impl<'a, 'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'a, '
199199
for i in 0..self.body.local_decls().len() {
200200
let local = Local::from_usize(i);
201201
if borrowed_locals_at_loc.contains(local) {
202+
debug!("{:?} is borrowed", local);
202203
trans.gen(local);
203204
}
204205
}

tests/ui/async-await/drop-track-field-assign-nonsend.drop_tracking_mir.stderr

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ LL | assert_send(agent.handle());
55
| ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
66
|
77
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
8-
note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send`
9-
--> $DIR/drop-track-field-assign-nonsend.rs:19:21
8+
note: future is not `Send` as this value is used across an await
9+
--> $DIR/drop-track-field-assign-nonsend.rs:23:39
1010
|
11-
LL | async fn handle(&mut self) {
12-
| ^^^^^^^^^ has type `&mut Agent` which is not `Send`, because `Agent` is not `Send`
11+
LL | let mut info = self.info_result.clone();
12+
| -------- has type `InfoResult` which is not `Send`
13+
...
14+
LL | let _ = send_element(element).await;
15+
| ^^^^^ await occurs here, with `mut info` maybe used later
1316
note: required by a bound in `assert_send`
1417
--> $DIR/drop-track-field-assign-nonsend.rs:40:19
1518
|

tests/ui/async-await/field-assign-nonsend.drop_tracking_mir.stderr

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ LL | assert_send(agent.handle());
55
| ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
66
|
77
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
8-
note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send`
9-
--> $DIR/field-assign-nonsend.rs:19:21
8+
note: future is not `Send` as this value is used across an await
9+
--> $DIR/field-assign-nonsend.rs:23:39
1010
|
11-
LL | async fn handle(&mut self) {
12-
| ^^^^^^^^^ has type `&mut Agent` which is not `Send`, because `Agent` is not `Send`
11+
LL | let mut info = self.info_result.clone();
12+
| -------- has type `InfoResult` which is not `Send`
13+
...
14+
LL | let _ = send_element(element).await;
15+
| ^^^^^ await occurs here, with `mut info` maybe used later
1316
note: required by a bound in `assert_send`
1417
--> $DIR/field-assign-nonsend.rs:40:19
1518
|

0 commit comments

Comments
 (0)