Skip to content

Commit c975eee

Browse files
committed
Move field-sensitivity logic out of transfer_function and into ModularMutationVisitor
1 parent b1bb853 commit c975eee

File tree

7 files changed

+108
-115
lines changed

7 files changed

+108
-115
lines changed

Makefile.toml

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
[config]
2+
skip_core_tasks = true
3+
default_to_workspace = false
4+
15
[tasks.watch-cargo]
26
script = "cargo watch -x 'install --path crates/flowistry_ide --debug --offline'"
37
install_crate = { crate_name = "cargo-watch" }
4-
workspace = false
58

69
[tasks.watch-js]
710
script = "cd ide && npm run watch"
8-
workspace = false
911

1012
[tasks.watch.run_task]
1113
name = ["watch-cargo", "watch-js"]

crates/flowistry/src/infoflow/analysis.rs

+25-58
Original file line numberDiff line numberDiff line change
@@ -135,22 +135,8 @@ impl<'a, 'tcx> FlowAnalysis<'a, 'tcx> {
135135
add_deps(mutated, &mut input_location_deps);
136136

137137
// Add deps of all inputs
138-
let mut children = Vec::new();
139-
for (place, elem) in inputs.iter() {
138+
for place in inputs.iter() {
140139
add_deps(*place, &mut input_location_deps);
141-
142-
// If the input is associated to a specific projection of the mutated
143-
// place, then save that input's dependencies with the projection
144-
if let Some(elem) = elem {
145-
let mut projection = mutated.projection.to_vec();
146-
projection.push(*elem);
147-
let mut child_deps = LocationOrArgSet::new(location_domain);
148-
add_deps(*place, &mut child_deps);
149-
children.push((
150-
Place::make(mutated.local, &projection, self.tcx),
151-
child_deps,
152-
));
153-
}
154140
}
155141

156142
// Add control dependencies
@@ -168,52 +154,33 @@ impl<'a, 'tcx> FlowAnalysis<'a, 'tcx> {
168154
}
169155
}
170156

171-
if children.len() > 0 {
172-
// In the special case of mutated = aggregate { x: .., y: .. }
173-
// then we ensure that deps(mutated.x) != deps(mutated)
174-
175-
// First, ensure that all children have the current in their deps.
176-
// See test struct_read_constant for where this is needed.
177-
for child in all_aliases.children(mutated) {
178-
state.insert(all_aliases.normalize(child), location);
179-
}
180-
181-
// Then for constructor arguments that were places, add dependencies of those places.
182-
for (child, deps) in children {
183-
state.union_into_row(all_aliases.normalize(child), &deps);
184-
}
185-
186-
// Finally add input_location_deps *JUST* to mutated, not conflicts of mutated.
187-
state.union_into_row(all_aliases.normalize(mutated), &input_location_deps);
188-
} else {
189-
// Union dependencies into all conflicting places of the mutated place
190-
let mut mutable_conflicts = all_aliases.conflicts(mutated).to_owned();
191-
192-
// Remove any conflicts that aren't actually mutable, e.g. if x : &T ends up
193-
// as an alias of y: &mut T. See test function_lifetime_alias_mut for an example.
194-
let ignore_mut =
195-
is_extension_active(|mode| mode.mutability_mode == MutabilityMode::IgnoreMut);
196-
if !ignore_mut {
197-
let body = self.body;
198-
let tcx = self.tcx;
199-
mutable_conflicts = mutable_conflicts
200-
.iter()
201-
.filter(|place| {
202-
place.iter_projections().all(|(sub_place, _)| {
203-
let ty = sub_place.ty(body.local_decls(), tcx).ty;
204-
!matches!(ty.ref_mutability(), Some(Mutability::Not))
205-
})
157+
// Union dependencies into all conflicting places of the mutated place
158+
let mut mutable_conflicts = all_aliases.conflicts(mutated).to_owned();
159+
160+
// Remove any conflicts that aren't actually mutable, e.g. if x : &T ends up
161+
// as an alias of y: &mut T. See test function_lifetime_alias_mut for an example.
162+
let ignore_mut =
163+
is_extension_active(|mode| mode.mutability_mode == MutabilityMode::IgnoreMut);
164+
if !ignore_mut {
165+
let body = self.body;
166+
let tcx = self.tcx;
167+
mutable_conflicts = mutable_conflicts
168+
.iter()
169+
.filter(|place| {
170+
place.iter_projections().all(|(sub_place, _)| {
171+
let ty = sub_place.ty(body.local_decls(), tcx).ty;
172+
!matches!(ty.ref_mutability(), Some(Mutability::Not))
206173
})
207-
.copied()
208-
.collect::<HashSet<_>>();
209-
};
174+
})
175+
.copied()
176+
.collect::<HashSet<_>>();
177+
};
210178

211-
debug!(" Mutated conflicting places: {mutable_conflicts:?}");
212-
debug!(" with deps {input_location_deps:?}");
179+
debug!(" Mutated conflicting places: {mutable_conflicts:?}");
180+
debug!(" with deps {input_location_deps:?}");
213181

214-
for place in mutable_conflicts.into_iter() {
215-
state.union_into_row(all_aliases.normalize(place), &input_location_deps);
216-
}
182+
for place in mutable_conflicts.into_iter() {
183+
state.union_into_row(all_aliases.normalize(place), &input_location_deps);
217184
}
218185
}
219186
}

crates/flowistry/src/infoflow/mutation.rs

+71-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Identifies the mutated places in a MIR instruction via modular approximation based on types.
22
33
use log::debug;
4-
use rustc_middle::mir::{visit::Visitor, *};
4+
use rustc_middle::{
5+
mir::{visit::Visitor, *},
6+
ty::TyKind,
7+
};
58

69
use crate::mir::{
710
aliases::Aliases,
@@ -22,9 +25,8 @@ pub struct Mutation<'a, 'tcx> {
2225
/// The place that is being mutated.
2326
pub mutated: Place<'tcx>,
2427

25-
/// The set of inputs to the mutating operation, each paired with
26-
/// an optional [`ProjectionElem::Field`] in the case of aggregate constructors.
27-
pub inputs: &'a [(Place<'tcx>, Option<PlaceElem<'tcx>>)],
28+
/// The set of inputs to the mutating operation.
29+
pub inputs: &'a [Place<'tcx>],
2830

2931
/// Where the mutation is occuring.
3032
pub location: Location,
@@ -63,19 +65,74 @@ where
6365
{
6466
fn visit_assign(
6567
&mut self,
66-
place: &Place<'tcx>,
68+
mutated: &Place<'tcx>,
6769
rvalue: &Rvalue<'tcx>,
6870
location: Location,
6971
) {
70-
debug!("Checking {location:?}: {place:?} = {rvalue:?}");
71-
let mut collector = PlaceCollector {
72-
places: Vec::new(),
73-
tcx: self.aliases.tcx,
74-
};
72+
debug!("Checking {location:?}: {mutated:?} = {rvalue:?}");
73+
let body = self.aliases.body;
74+
let tcx = self.aliases.tcx;
75+
76+
match rvalue {
77+
// In the case of _1 = aggregate { field1: op1, field2: op2, ... },
78+
// then destructure this into a series of mutations like
79+
// _1.field1 = op1, _1.field2 = op2, and so on.
80+
Rvalue::Aggregate(box AggregateKind::Adt(def_id, idx, substs, _, _), ops) => {
81+
let adt_def = tcx.adt_def(*def_id);
82+
let variant = adt_def.variant(*idx);
83+
if variant.fields.len() > 0 {
84+
let fields = variant.fields.iter().enumerate().zip(ops.iter()).map(
85+
|((i, field), input_op)| {
86+
let input_place = input_op.to_place();
87+
let field = PlaceElem::Field(Field::from_usize(i), field.ty(tcx, substs));
88+
(mutated.project_deeper(&[field], tcx), input_place)
89+
},
90+
);
91+
for (mutated, input) in fields {
92+
(self.f)(Mutation {
93+
mutated,
94+
inputs: input.as_ref().map(std::slice::from_ref).unwrap_or_default(),
95+
location,
96+
status: MutationStatus::Definitely,
97+
});
98+
}
99+
return;
100+
}
101+
}
102+
103+
// In the case of _1 = _2 where _2 : struct Foo { x: T, y: S, .. },
104+
// then destructure this into a series of mutations like
105+
// _1.x = _2.x, _1.y = _2.y, and so on.
106+
Rvalue::Use(Operand::Move(place) | Operand::Copy(place)) => {
107+
let place_ty = place.ty(&body.local_decls, tcx).ty;
108+
if let TyKind::Adt(adt_def, substs) = place_ty.kind() {
109+
if adt_def.is_struct() {
110+
let fields = adt_def.all_fields().enumerate().map(|(i, field_def)| {
111+
PlaceElem::Field(Field::from_usize(i), field_def.ty(tcx, substs))
112+
});
113+
for field in fields {
114+
let mutated_field = mutated.project_deeper(&[field], tcx);
115+
let input_field = place.project_deeper(&[field], tcx);
116+
(self.f)(Mutation {
117+
mutated: mutated_field,
118+
inputs: &[input_field],
119+
location,
120+
status: MutationStatus::Definitely,
121+
});
122+
}
123+
return;
124+
}
125+
}
126+
}
127+
128+
_ => {}
129+
}
130+
131+
let mut collector = PlaceCollector::default();
75132
collector.visit_rvalue(rvalue, location);
76133
(self.f)(Mutation {
77-
mutated: *place,
78-
inputs: &collector.places,
134+
mutated: *mutated,
135+
inputs: &collector.0,
79136
location,
80137
status: MutationStatus::Definitely,
81138
});
@@ -99,10 +156,7 @@ where
99156
.map(|(_, place)| place)
100157
.filter(|place| !async_hack.ignore_place(*place))
101158
.collect::<Vec<_>>();
102-
let arg_inputs = arg_places
103-
.iter()
104-
.map(|place| (*place, None))
105-
.collect::<Vec<_>>();
159+
let arg_inputs = arg_places.clone();
106160

107161
let ret_is_unit = destination
108162
.ty(self.aliases.body.local_decls(), tcx)
@@ -140,7 +194,7 @@ where
140194
if let Some(src) = value.to_place() {
141195
(self.f)(Mutation {
142196
mutated: *place,
143-
inputs: &[(src, None)],
197+
inputs: &[src],
144198
location,
145199
status: MutationStatus::Definitely,
146200
});

crates/flowistry/src/infoflow/recursive.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ impl<'tcx> FlowAnalysis<'_, 'tcx> {
201201
let parent_deps = return_state
202202
.rows()
203203
.filter(|(_, deps)| child_deps.is_superset(deps))
204-
.filter_map(|(row, _)| Some((translate_child_to_parent(row, false)?, None)))
204+
.filter_map(|(row, _)| translate_child_to_parent(row, false))
205205
.collect::<Vec<_>>();
206206

207207
debug!(

crates/flowistry/src/mir/utils.rs

+3-30
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,8 @@ impl PlaceRelation {
163163
}
164164
}
165165

166-
pub struct PlaceCollector<'tcx> {
167-
pub tcx: TyCtxt<'tcx>,
168-
pub places: Vec<(Place<'tcx>, Option<PlaceElem<'tcx>>)>,
169-
}
166+
#[derive(Default)]
167+
pub struct PlaceCollector<'tcx>(pub Vec<Place<'tcx>>);
170168

171169
impl<'tcx> Visitor<'tcx> for PlaceCollector<'tcx> {
172170
fn visit_place(
@@ -175,32 +173,7 @@ impl<'tcx> Visitor<'tcx> for PlaceCollector<'tcx> {
175173
_context: PlaceContext,
176174
_location: Location,
177175
) {
178-
self.places.push((*place, None));
179-
}
180-
181-
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
182-
match rvalue {
183-
Rvalue::Aggregate(box AggregateKind::Adt(def_id, idx, substs, _, _), ops) => {
184-
// In the case of _1 = aggregate { field1: op1, field2: op2, ... }
185-
// we want to remember which places correspond to which fields so the infoflow
186-
// analysis can be field-sensitive for constructors.
187-
let adt_def = self.tcx.adt_def(*def_id);
188-
let variant = adt_def.variant(*idx);
189-
let places = variant
190-
.fields
191-
.iter()
192-
.enumerate()
193-
.zip(ops.iter())
194-
.filter_map(|((i, field), op)| {
195-
let place = op.to_place()?;
196-
let field =
197-
ProjectionElem::Field(Field::from_usize(i), field.ty(self.tcx, substs));
198-
Some((place, Some(field)))
199-
});
200-
self.places.extend(places);
201-
}
202-
_ => self.super_rvalue(rvalue, location),
203-
}
176+
self.0.push(*place);
204177
}
205178
}
206179

crates/flowistry_ide/src/focus/direct_influence.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl<'a, 'tcx> DirectInfluence<'a, 'tcx> {
2828
}
2929
};
3030

31-
for (input, _) in inputs {
31+
for input in inputs {
3232
add(*input, Mutability::Not);
3333
}
3434

crates/rustc_plugin/src/lib.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::{
1212
env, fs,
1313
ops::Deref,
1414
path::{Path, PathBuf},
15-
process::{exit, Command},
15+
process::{exit, Command, Stdio},
1616
};
1717

1818
pub use cargo_metadata::camino::Utf8Path;
@@ -101,6 +101,7 @@ pub fn cli_main<T: RustcPlugin>(plugin: T) {
101101
let args = plugin.args(&target_dir);
102102

103103
let mut cmd = Command::new("cargo");
104+
cmd.stdout(Stdio::inherit()).stderr(Stdio::inherit());
104105

105106
let mut path = env::current_exe()
106107
.expect("current executable path invalid")
@@ -243,11 +244,7 @@ pub fn cli_main<T: RustcPlugin>(plugin: T) {
243244
cmd.args(flags);
244245
}
245246

246-
let exit_status = cmd
247-
.spawn()
248-
.expect("could not run cargo")
249-
.wait()
250-
.expect("failed to wait for cargo?");
247+
let exit_status = cmd.status().expect("failed to wait for cargo?");
251248

252249
exit(exit_status.code().unwrap_or(-1));
253250
}

0 commit comments

Comments
 (0)