2019-11-11 11:48:17 -08:00
|
|
|
//! A solver for dataflow problems.
|
|
|
|
|
2020-08-27 23:02:46 -07:00
|
|
|
use std::borrow::BorrowMut;
|
2019-11-11 11:48:17 -08:00
|
|
|
use std::ffi::OsString;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
2020-04-27 23:26:11 +05:30
|
|
|
use rustc_ast as ast;
|
2019-11-11 11:48:17 -08:00
|
|
|
use rustc_data_structures::work_queue::WorkQueue;
|
2020-06-02 20:19:49 +03:00
|
|
|
use rustc_graphviz as dot;
|
2019-11-11 11:48:17 -08:00
|
|
|
use rustc_hir::def_id::DefId;
|
|
|
|
use rustc_index::bit_set::BitSet;
|
2020-08-27 23:02:46 -07:00
|
|
|
use rustc_index::vec::{Idx, IndexVec};
|
2020-03-22 12:09:40 -07:00
|
|
|
use rustc_middle::mir::{self, traversal, BasicBlock};
|
2021-01-05 19:53:07 +01:00
|
|
|
use rustc_middle::mir::{create_dump_file, dump_enabled};
|
2020-10-05 10:12:40 -07:00
|
|
|
use rustc_middle::ty::TyCtxt;
|
2019-11-11 11:48:17 -08:00
|
|
|
use rustc_span::symbol::{sym, Symbol};
|
|
|
|
|
2020-08-27 23:02:46 -07:00
|
|
|
use super::fmt::DebugWithContext;
|
2019-11-11 11:48:17 -08:00
|
|
|
use super::graphviz;
|
2020-03-22 12:09:40 -07:00
|
|
|
use super::{
|
2020-08-27 23:02:46 -07:00
|
|
|
visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
|
|
|
|
ResultsCursor, ResultsVisitor,
|
2020-03-22 12:09:40 -07:00
|
|
|
};
|
2019-11-11 11:48:17 -08:00
|
|
|
|
2020-03-22 12:09:40 -07:00
|
|
|
/// A dataflow analysis that has converged to fixpoint.
|
|
|
|
pub struct Results<'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
pub analysis: A,
|
2020-08-27 23:02:46 -07:00
|
|
|
pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
|
2020-03-22 12:09:40 -07:00
|
|
|
}
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
impl<'tcx, A> Results<'tcx, A>
|
2020-03-22 12:09:40 -07:00
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
/// Creates a `ResultsCursor` that can inspect these `Results`.
|
2021-12-14 12:02:45 -05:00
|
|
|
pub fn into_results_cursor<'mir>(
|
|
|
|
self,
|
|
|
|
body: &'mir mir::Body<'tcx>,
|
|
|
|
) -> ResultsCursor<'mir, 'tcx, A> {
|
2020-03-22 12:09:40 -07:00
|
|
|
ResultsCursor::new(body, self)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the dataflow state for the given block.
|
2020-08-27 23:02:46 -07:00
|
|
|
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
|
2020-03-22 12:09:40 -07:00
|
|
|
&self.entry_sets[block]
|
|
|
|
}
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
pub fn visit_with<'mir>(
|
2020-03-22 12:09:40 -07:00
|
|
|
&self,
|
|
|
|
body: &'mir mir::Body<'tcx>,
|
|
|
|
blocks: impl IntoIterator<Item = BasicBlock>,
|
2020-08-27 23:02:46 -07:00
|
|
|
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
|
2020-03-22 12:09:40 -07:00
|
|
|
) {
|
|
|
|
visit_results(body, blocks, self, vis)
|
|
|
|
}
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
pub fn visit_reachable_with<'mir>(
|
2020-07-08 09:47:14 -07:00
|
|
|
&self,
|
|
|
|
body: &'mir mir::Body<'tcx>,
|
2020-08-27 23:02:46 -07:00
|
|
|
vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
|
2020-07-08 09:47:14 -07:00
|
|
|
) {
|
|
|
|
let blocks = mir::traversal::reachable(body);
|
|
|
|
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
|
|
|
|
}
|
2020-03-22 12:09:40 -07:00
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:17 -08:00
|
|
|
/// A solver for dataflow problems.
|
|
|
|
pub struct Engine<'a, 'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &'a mir::Body<'tcx>,
|
|
|
|
dead_unwinds: Option<&'a BitSet<BasicBlock>>,
|
2020-08-27 23:02:46 -07:00
|
|
|
entry_sets: IndexVec<BasicBlock, A::Domain>,
|
2020-09-14 16:54:53 -07:00
|
|
|
pass_name: Option<&'static str>,
|
2019-11-11 11:48:17 -08:00
|
|
|
analysis: A,
|
|
|
|
|
|
|
|
/// Cached, cumulative transfer functions for each block.
|
2020-08-28 14:20:14 -07:00
|
|
|
//
|
|
|
|
// FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
|
|
|
|
// gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
|
|
|
|
// performance in practice. I've tried a few ways to avoid this, but they have downsides. See
|
|
|
|
// the message for the commit that added this FIXME for more information.
|
2020-08-27 23:02:46 -07:00
|
|
|
apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
|
2019-11-11 11:48:17 -08:00
|
|
|
}
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
impl<'a, 'tcx, A, D, T> Engine<'a, 'tcx, A>
|
2019-11-11 11:48:17 -08:00
|
|
|
where
|
2020-08-27 23:02:46 -07:00
|
|
|
A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
|
|
|
|
D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
|
|
|
|
T: Idx,
|
2019-11-11 11:48:17 -08:00
|
|
|
{
|
|
|
|
/// Creates a new `Engine` to solve a gen-kill dataflow problem.
|
2020-10-04 15:22:23 -07:00
|
|
|
pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
|
2020-01-20 15:15:37 -08:00
|
|
|
// If there are no back-edges in the control-flow graph, we only ever need to apply the
|
|
|
|
// transfer function for each block exactly once (assuming that we process blocks in RPO).
|
|
|
|
//
|
|
|
|
// In this case, there's no need to compute the block transfer functions ahead of time.
|
|
|
|
if !body.is_cfg_cyclic() {
|
2020-10-04 15:22:23 -07:00
|
|
|
return Self::new(tcx, body, analysis, None);
|
2020-01-20 15:15:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, compute and store the cumulative transfer function for each block.
|
|
|
|
|
2020-08-27 23:02:46 -07:00
|
|
|
let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
|
|
|
|
let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());
|
2019-11-11 11:48:17 -08:00
|
|
|
|
|
|
|
for (block, block_data) in body.basic_blocks().iter_enumerated() {
|
|
|
|
let trans = &mut trans_for_block[block];
|
2020-03-22 12:09:40 -07:00
|
|
|
A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
|
2019-11-11 11:48:17 -08:00
|
|
|
}
|
|
|
|
|
2020-08-27 23:02:46 -07:00
|
|
|
let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
|
|
|
|
trans_for_block[bb].apply(state.borrow_mut());
|
|
|
|
});
|
|
|
|
|
2020-10-04 15:22:23 -07:00
|
|
|
Self::new(tcx, body, analysis, Some(apply_trans as Box<_>))
|
2019-11-11 11:48:17 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-14 12:02:45 -05:00
|
|
|
impl<'a, 'tcx, A, D> Engine<'a, 'tcx, A>
|
2019-11-11 11:48:17 -08:00
|
|
|
where
|
2020-08-27 23:02:46 -07:00
|
|
|
A: Analysis<'tcx, Domain = D>,
|
|
|
|
D: Clone + JoinSemiLattice,
|
2019-11-11 11:48:17 -08:00
|
|
|
{
|
|
|
|
/// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
|
|
|
|
/// function.
|
|
|
|
///
|
|
|
|
/// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
|
|
|
|
/// better performance.
|
2020-10-04 15:22:23 -07:00
|
|
|
pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
|
|
|
|
Self::new(tcx, body, analysis, None)
|
2019-11-11 11:48:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn new(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &'a mir::Body<'tcx>,
|
|
|
|
analysis: A,
|
2020-08-27 23:02:46 -07:00
|
|
|
apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
|
2019-11-11 11:48:17 -08:00
|
|
|
) -> Self {
|
2020-08-27 23:02:46 -07:00
|
|
|
let bottom_value = analysis.bottom_value(body);
|
|
|
|
let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
|
2019-11-11 11:48:17 -08:00
|
|
|
analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
|
|
|
|
|
2020-08-27 23:02:46 -07:00
|
|
|
if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
|
2020-03-22 12:09:40 -07:00
|
|
|
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
|
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:17 -08:00
|
|
|
Engine {
|
|
|
|
analysis,
|
|
|
|
tcx,
|
|
|
|
body,
|
|
|
|
dead_unwinds: None,
|
2020-09-14 16:54:53 -07:00
|
|
|
pass_name: None,
|
2019-11-11 11:48:17 -08:00
|
|
|
entry_sets,
|
2020-08-27 23:02:46 -07:00
|
|
|
apply_trans_for_block,
|
2019-11-11 11:48:17 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-19 18:47:29 -08:00
|
|
|
/// Signals that we do not want dataflow state to propagate across unwind edges for these
|
|
|
|
/// `BasicBlock`s.
|
|
|
|
///
|
|
|
|
/// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
|
|
|
|
/// unwind during execution. Otherwise, your dataflow results will not be correct.
|
2019-11-11 11:48:17 -08:00
|
|
|
pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
|
|
|
|
self.dead_unwinds = Some(dead_unwinds);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-14 16:54:53 -07:00
|
|
|
/// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
|
|
|
|
///
|
|
|
|
/// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
|
|
|
|
/// to differentiate them. Otherwise, only the results for the latest run will be saved.
|
|
|
|
pub fn pass_name(mut self, name: &'static str) -> Self {
|
|
|
|
self.pass_name = Some(name);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-01-19 18:47:29 -08:00
|
|
|
/// Computes the fixpoint for this dataflow problem and returns it.
|
2020-08-27 23:02:46 -07:00
|
|
|
pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
|
|
|
|
where
|
|
|
|
A::Domain: DebugWithContext<A>,
|
|
|
|
{
|
2020-03-22 12:09:40 -07:00
|
|
|
let Engine {
|
|
|
|
analysis,
|
|
|
|
body,
|
|
|
|
dead_unwinds,
|
|
|
|
mut entry_sets,
|
|
|
|
tcx,
|
2020-08-27 23:02:46 -07:00
|
|
|
apply_trans_for_block,
|
2020-09-14 16:54:53 -07:00
|
|
|
pass_name,
|
2020-03-22 12:09:40 -07:00
|
|
|
..
|
|
|
|
} = self;
|
2019-11-11 11:48:17 -08:00
|
|
|
|
|
|
|
let mut dirty_queue: WorkQueue<BasicBlock> =
|
2020-03-22 12:09:40 -07:00
|
|
|
WorkQueue::with_none(body.basic_blocks().len());
|
2019-11-11 11:48:17 -08:00
|
|
|
|
2020-03-22 12:09:40 -07:00
|
|
|
if A::Direction::is_forward() {
|
|
|
|
for (bb, _) in traversal::reverse_postorder(body) {
|
|
|
|
dirty_queue.insert(bb);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Reverse post-order on the reverse CFG may generate a better iteration order for
|
|
|
|
// backward dataflow analyses, but probably not enough to matter.
|
|
|
|
for (bb, _) in traversal::postorder(body) {
|
|
|
|
dirty_queue.insert(bb);
|
|
|
|
}
|
2019-11-11 11:48:17 -08:00
|
|
|
}
|
|
|
|
|
2020-11-08 13:52:15 -08:00
|
|
|
// `state` is not actually used between iterations;
|
|
|
|
// this is just an optimization to avoid reallocating
|
|
|
|
// every iteration.
|
2020-08-27 23:02:46 -07:00
|
|
|
let mut state = analysis.bottom_value(body);
|
2019-11-11 11:48:17 -08:00
|
|
|
while let Some(bb) = dirty_queue.pop() {
|
2020-03-22 12:09:40 -07:00
|
|
|
let bb_data = &body[bb];
|
2019-11-11 11:48:17 -08:00
|
|
|
|
2020-11-08 13:52:15 -08:00
|
|
|
// Set the state to the entry state of the block.
|
|
|
|
// This is equivalent to `state = entry_sets[bb].clone()`,
|
|
|
|
// but it saves an allocation, thus improving compile times.
|
2020-08-27 23:02:46 -07:00
|
|
|
state.clone_from(&entry_sets[bb]);
|
2020-11-08 13:52:15 -08:00
|
|
|
|
|
|
|
// Apply the block transfer function, using the cached one if it exists.
|
2020-08-27 23:02:46 -07:00
|
|
|
match &apply_trans_for_block {
|
|
|
|
Some(apply) => apply(bb, &mut state),
|
2020-03-22 12:09:40 -07:00
|
|
|
None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
|
|
|
|
}
|
2019-11-11 11:48:17 -08:00
|
|
|
|
2020-03-22 12:09:40 -07:00
|
|
|
A::Direction::join_state_into_successors_of(
|
|
|
|
&analysis,
|
|
|
|
tcx,
|
|
|
|
body,
|
|
|
|
dead_unwinds,
|
|
|
|
&mut state,
|
2019-11-11 11:48:17 -08:00
|
|
|
(bb, bb_data),
|
2020-08-27 23:02:46 -07:00
|
|
|
|target: BasicBlock, state: &A::Domain| {
|
|
|
|
let set_changed = entry_sets[target].join(state);
|
2020-03-22 12:09:40 -07:00
|
|
|
if set_changed {
|
|
|
|
dirty_queue.insert(target);
|
|
|
|
}
|
|
|
|
},
|
2019-11-11 11:48:17 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let results = Results { analysis, entry_sets };
|
|
|
|
|
2020-10-04 15:22:23 -07:00
|
|
|
let res = write_graphviz_results(tcx, &body, &results, pass_name);
|
2019-11-11 11:48:17 -08:00
|
|
|
if let Err(e) = res {
|
2020-10-05 10:12:48 -07:00
|
|
|
error!("Failed to write graphviz dataflow results: {}", e);
|
2019-11-11 11:48:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
results
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Graphviz
|
|
|
|
|
|
|
|
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
|
|
|
|
/// `rustc_mir` attributes.
|
2021-12-14 12:02:45 -05:00
|
|
|
fn write_graphviz_results<'tcx, A>(
|
2019-11-11 11:48:17 -08:00
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &mir::Body<'tcx>,
|
|
|
|
results: &Results<'tcx, A>,
|
2020-09-14 16:54:53 -07:00
|
|
|
pass_name: Option<&'static str>,
|
2019-11-11 11:48:17 -08:00
|
|
|
) -> std::io::Result<()>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
2020-08-27 23:02:46 -07:00
|
|
|
A::Domain: DebugWithContext<A>,
|
2019-11-11 11:48:17 -08:00
|
|
|
{
|
2020-10-05 10:12:40 -07:00
|
|
|
use std::fs;
|
|
|
|
use std::io::{self, Write};
|
|
|
|
|
2020-10-04 15:22:23 -07:00
|
|
|
let def_id = body.source.def_id();
|
2022-02-19 00:48:49 +01:00
|
|
|
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
|
2020-03-28 14:09:12 -07:00
|
|
|
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
|
2022-02-19 00:48:49 +01:00
|
|
|
return Ok(());
|
2019-11-11 11:48:17 -08:00
|
|
|
};
|
|
|
|
|
2020-10-05 10:12:40 -07:00
|
|
|
let mut file = match attrs.output_path(A::NAME) {
|
|
|
|
Some(path) => {
|
|
|
|
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
|
|
|
|
if let Some(parent) = path.parent() {
|
|
|
|
fs::create_dir_all(parent)?;
|
|
|
|
}
|
|
|
|
io::BufWriter::new(fs::File::create(&path)?)
|
|
|
|
}
|
2020-03-28 14:09:12 -07:00
|
|
|
|
|
|
|
None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
|
|
|
|
&& dump_enabled(tcx, A::NAME, def_id) =>
|
|
|
|
{
|
2020-10-05 10:12:40 -07:00
|
|
|
create_dump_file(
|
|
|
|
tcx,
|
|
|
|
".dot",
|
|
|
|
None,
|
|
|
|
A::NAME,
|
|
|
|
&pass_name.unwrap_or("-----"),
|
|
|
|
body.source,
|
|
|
|
)?
|
2020-03-28 14:09:12 -07:00
|
|
|
}
|
|
|
|
|
2020-10-05 10:12:40 -07:00
|
|
|
_ => return Ok(()),
|
2019-11-11 11:48:17 -08:00
|
|
|
};
|
|
|
|
|
2020-08-27 23:02:46 -07:00
|
|
|
let style = match attrs.formatter {
|
|
|
|
Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
|
|
|
|
_ => graphviz::OutputStyle::AfterOnly,
|
2019-11-11 11:48:17 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut buf = Vec::new();
|
|
|
|
|
2020-10-04 15:22:23 -07:00
|
|
|
let graphviz = graphviz::Formatter::new(body, results, style);
|
2020-09-16 08:10:06 -07:00
|
|
|
let mut render_opts =
|
|
|
|
vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())];
|
2020-09-08 16:08:35 -07:00
|
|
|
if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
|
|
|
|
render_opts.push(dot::RenderOption::DarkTheme);
|
|
|
|
}
|
|
|
|
dot::render_opts(&graphviz, &mut buf, &render_opts)?;
|
2020-03-28 14:09:34 -07:00
|
|
|
|
2020-10-05 10:12:40 -07:00
|
|
|
file.write_all(&buf)?;
|
2020-03-28 14:09:34 -07:00
|
|
|
|
2019-11-11 11:48:17 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct RustcMirAttrs {
|
|
|
|
basename_and_suffix: Option<PathBuf>,
|
|
|
|
formatter: Option<Symbol>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RustcMirAttrs {
|
2021-12-14 12:02:45 -05:00
|
|
|
fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> {
|
2019-11-11 11:48:17 -08:00
|
|
|
let attrs = tcx.get_attrs(def_id);
|
|
|
|
|
|
|
|
let mut result = Ok(());
|
|
|
|
let mut ret = RustcMirAttrs::default();
|
|
|
|
|
|
|
|
let rustc_mir_attrs = attrs
|
2020-02-29 03:05:14 +01:00
|
|
|
.iter()
|
2021-07-29 12:00:41 -05:00
|
|
|
.filter(|attr| attr.has_name(sym::rustc_mir))
|
2019-11-11 11:48:17 -08:00
|
|
|
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
|
|
|
|
|
|
|
|
for attr in rustc_mir_attrs {
|
2020-08-02 13:17:20 +03:00
|
|
|
let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
|
2019-11-11 11:48:17 -08:00
|
|
|
Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
|
|
|
|
let path = PathBuf::from(s.to_string());
|
|
|
|
match path.file_name() {
|
|
|
|
Some(_) => Ok(path),
|
|
|
|
None => {
|
|
|
|
tcx.sess.span_err(attr.span(), "path must end in a filename");
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2020-08-02 13:17:20 +03:00
|
|
|
} else if attr.has_name(sym::borrowck_graphviz_format) {
|
2019-11-11 11:48:17 -08:00
|
|
|
Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
|
|
|
|
sym::gen_kill | sym::two_phase => Ok(s),
|
|
|
|
_ => {
|
|
|
|
tcx.sess.span_err(attr.span(), "unknown formatter");
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
};
|
|
|
|
|
|
|
|
result = result.and(attr_result);
|
|
|
|
}
|
|
|
|
|
|
|
|
result.map(|()| ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_field<T>(
|
|
|
|
field: &mut Option<T>,
|
2021-12-14 12:02:45 -05:00
|
|
|
tcx: TyCtxt<'_>,
|
2019-11-11 11:48:17 -08:00
|
|
|
attr: &ast::NestedMetaItem,
|
|
|
|
mapper: impl FnOnce(Symbol) -> Result<T, ()>,
|
|
|
|
) -> Result<(), ()> {
|
|
|
|
if field.is_some() {
|
|
|
|
tcx.sess
|
|
|
|
.span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));
|
|
|
|
|
|
|
|
return Err(());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(s) = attr.value_str() {
|
|
|
|
*field = Some(mapper(s)?);
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
tcx.sess
|
|
|
|
.span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the path where dataflow results should be written, or `None`
|
|
|
|
/// `borrowck_graphviz_postflow` was not specified.
|
|
|
|
///
|
|
|
|
/// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
|
|
|
|
///
|
|
|
|
/// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
|
|
|
|
fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
|
|
|
|
let mut ret = self.basename_and_suffix.as_ref().cloned()?;
|
|
|
|
let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
|
|
|
|
|
|
|
|
let mut file_name: OsString = analysis_name.into();
|
|
|
|
file_name.push("_");
|
|
|
|
file_name.push(suffix);
|
|
|
|
ret.set_file_name(file_name);
|
|
|
|
|
|
|
|
Some(ret)
|
|
|
|
}
|
|
|
|
}
|