Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 047e7ca

Browse files
committedAug 8, 2019
Correctly determine which terminators can have side effects
The previous code ignored that `Drop` terminators could execute custom `Drop` implementations which might mutate the environment. Now we correctly implement `has_side_effects` and tests that custom `Drop` impls are recorded as an indirect definition.
1 parent cfd9320 commit 047e7ca

File tree

4 files changed

+73
-8
lines changed

4 files changed

+73
-8
lines changed
 

‎src/librustc_mir/dataflow/impls/reaching_defs.rs

+36-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
use rustc::mir::visit::{PlaceContext, Visitor};
22
use rustc::mir::visit::{MutatingUseContext, NonMutatingUseContext, NonUseContext};
33
use rustc::mir::{self, Local, Location, Place, PlaceBase};
4+
use rustc::ty::{self, TyCtxt, ParamEnv};
45
use rustc_data_structures::bit_set::BitSet;
56
use rustc_data_structures::fx::FxHashMap;
67
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
8+
use rustc_target::spec::abi::Abi;
9+
710
use smallvec::SmallVec;
11+
use syntax::symbol::sym;
812

913
use super::{BitDenotation, GenKillSet, BottomValue};
1014

@@ -74,16 +78,41 @@ pub struct ReachingDefinitions {
7478
at_location: FxHashMap<Location, SmallVec<[DefIndex; 4]>>,
7579
}
7680

77-
fn is_call_with_side_effects(terminator: &mir::Terminator<'tcx>) -> bool {
78-
if let mir::TerminatorKind::Call { .. } = terminator.kind {
79-
true
80-
} else {
81-
false
81+
fn has_side_effects(
82+
tcx: TyCtxt<'tcx>,
83+
body: &mir::Body<'tcx>,
84+
param_env: ParamEnv<'tcx>,
85+
terminator: &mir::Terminator<'tcx>,
86+
) -> bool {
87+
match &terminator.kind {
88+
mir::TerminatorKind::Call { .. } => true,
89+
90+
// Types with special drop glue may mutate their environment.
91+
| mir::TerminatorKind::Drop { location: place, .. }
92+
| mir::TerminatorKind::DropAndReplace { location: place, .. }
93+
=> place.ty(body, tcx).ty.needs_drop(tcx, param_env),
94+
95+
| mir::TerminatorKind::Goto { .. }
96+
| mir::TerminatorKind::SwitchInt { .. }
97+
| mir::TerminatorKind::Resume
98+
| mir::TerminatorKind::Abort
99+
| mir::TerminatorKind::Return
100+
| mir::TerminatorKind::Unreachable
101+
| mir::TerminatorKind::Assert { .. }
102+
| mir::TerminatorKind::FalseEdges { .. }
103+
| mir::TerminatorKind::FalseUnwind { .. }
104+
=> false,
105+
106+
// FIXME: I don't know the semantics around these so assume that they may mutate their
107+
// environment.
108+
| mir::TerminatorKind::Yield { .. }
109+
| mir::TerminatorKind::GeneratorDrop
110+
=> true,
82111
}
83112
}
84113

85114
impl ReachingDefinitions {
86-
pub fn new(body: &mir::Body<'_>) -> Self {
115+
pub fn new<'tcx>(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
87116
let mut ret = ReachingDefinitions {
88117
all: IndexVec::new(),
89118
for_local_direct: IndexVec::from_elem(Vec::new(), &body.local_decls),
@@ -102,7 +131,7 @@ impl ReachingDefinitions {
102131
let blocks_with_side_effects = body
103132
.basic_blocks()
104133
.iter_enumerated()
105-
.filter(|(_, data)| is_call_with_side_effects(data.terminator()));
134+
.filter(|(_, data)| has_side_effects(tcx, body, param_env, data.terminator()));
106135

107136
for (block, data) in blocks_with_side_effects {
108137
let term_loc = Location { block, statement_index: data.statements.len() };

‎src/librustc_mir/transform/rustc_peek.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl MirPass for SanityCheck {
5959
|_bd, i| DebugFormatted::new(&i));
6060
let flow_reaching_defs =
6161
do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
62-
ReachingDefinitions::new(body),
62+
ReachingDefinitions::new(tcx, body, tcx.param_env(def_id)),
6363
|bd, i| DebugFormatted::new(&bd.get(i).location));
6464
let flow_use_def_chain =
6565
UseDefChain::new(body, &flow_reaching_defs, &flow_borrowed_locals);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#![feature(core_intrinsics, rustc_attrs)]
2+
use std::intrinsics::rustc_peek;
3+
4+
struct NoSideEffectsInDrop<'a>(&'a mut u32);
5+
struct SideEffectsInDrop<'a>(&'a mut u32);
6+
7+
impl Drop for SideEffectsInDrop<'_> {
8+
fn drop(&mut self) {
9+
*self.0 = 42
10+
}
11+
}
12+
13+
#[rustc_mir(rustc_peek_use_def_chain, stop_after_dataflow, borrowck_graphviz_postflow="flow.dot")]
14+
fn main() {
15+
let mut x;
16+
x=0;
17+
18+
NoSideEffectsInDrop(&mut x);
19+
SideEffectsInDrop(&mut x);
20+
21+
// The ";" on line 19 is the point at which `SideEffectsInDrop` is dropped.
22+
unsafe { rustc_peek(x); }
23+
//~^ ERROR rustc_peek: [16: "x=0", 19: ";"]
24+
25+
assert_eq!(x, 42);
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: rustc_peek: [16: "x=0", 19: ";"]
2+
--> $DIR/reaching-defs-drop.rs:22:14
3+
|
4+
LL | unsafe { rustc_peek(x); }
5+
| ^^^^^^^^^^^^^
6+
7+
error: stop_after_dataflow ended compilation
8+
9+
error: aborting due to 2 previous errors
10+

0 commit comments

Comments
 (0)
Please sign in to comment.