Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
aaf1e47
Fix cargo version to our toolchain
JustusAdam Nov 7, 2025
b344633
Take analysis opts into account when hashing
JustusAdam Nov 7, 2025
836d9d8
Experimental rmcp tool matching
JustusAdam Nov 8, 2025
11c13a3
Extend guard to skip closures
JustusAdam Nov 9, 2025
a61f08e
moving auto markers
JustusAdam Nov 10, 2025
ddc6548
Additional resolution test
JustusAdam Nov 10, 2025
3d813ee
Better rendering for failure to prove instance safety
JustusAdam Nov 11, 2025
ba16f64
Don't complain about std markers when abstracting
JustusAdam Nov 11, 2025
232d072
Trying an interpreter for place mapping
JustusAdam Nov 12, 2025
2367de1
Debugging rmcp tool code
JustusAdam Nov 12, 2025
59ad506
Moving the interpreter, adding test cases
JustusAdam Nov 13, 2025
85e5fd1
Hooked the interpreter up to converter and added tests
JustusAdam Nov 13, 2025
57560ac
hacky float constants support
JustusAdam Nov 13, 2025
d456c26
Trying to add support module markers for crates
JustusAdam Nov 15, 2025
8f6c063
Adding test case for crate markers
JustusAdam Nov 15, 2025
00e1d63
Supporting marking impls for crate markers
JustusAdam Nov 18, 2025
d00e5c6
Handle markers for inline functions
JustusAdam Nov 18, 2025
17cc807
New test case for side effects
JustusAdam Nov 18, 2025
ec07c17
Only flatten canonical children
JustusAdam Nov 18, 2025
07bc69a
Fix the child filter
JustusAdam Nov 18, 2025
287984d
Fix vector test case
JustusAdam Nov 18, 2025
7d56f0c
Move side effect printing utility
JustusAdam Nov 18, 2025
5c3df54
Support for addressing slices
JustusAdam Nov 19, 2025
c71a3a7
Support slice impl as marker targets
JustusAdam Nov 19, 2025
9ddad0a
Simple marker assignment support for references
JustusAdam Nov 19, 2025
a9f0e18
Analysis option on command added
JustusAdam Nov 19, 2025
8c3cc6f
Fix argument name
JustusAdam Nov 19, 2025
89aa898
Fix an issue where markers overwrite each other
JustusAdam Nov 19, 2025
845d7c0
Fixed a bug we recurse to crates
JustusAdam Nov 19, 2025
434ac05
Better errors
JustusAdam Nov 20, 2025
833e162
Error if recursive marker used on non-module
JustusAdam Nov 21, 2025
2f6ff7d
Fix a bug in version flag parsing
JustusAdam Nov 28, 2025
d277e92
Handle derefs of `Unique`
JustusAdam Dec 2, 2025
bf19b46
Temporary utilities for debugging large graphs
JustusAdam Dec 2, 2025
ce162ea
Won't fix this for now
JustusAdam Dec 3, 2025
b3f0bbb
Support array markers
JustusAdam Dec 4, 2025
12ed41e
make deep chain failure a command line option
JustusAdam Dec 4, 2025
dbdacea
Allow elision of std markers at the reachability level
JustusAdam Dec 4, 2025
1f55b43
Sketch for infrastructure for sameness checking
JustusAdam Dec 6, 2025
01bae06
Sounder sameness checking
JustusAdam Dec 8, 2025
c412e8e
Added argument retrieval methods and `flows_to_unchanged`
JustusAdam Dec 8, 2025
ccc8eab
experiments with lifetimes on mutable variables
JustusAdam Dec 8, 2025
523e429
fix warnings
JustusAdam Dec 8, 2025
48b09dd
helpers for sameness test cases
JustusAdam Dec 9, 2025
18098d9
async arg test case
JustusAdam Jan 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion crates/flowistry_pdg/src/pdg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,13 +331,40 @@ pub enum Constant {
Uint(u64),
Char(char),
// Placeholder. Floats in the rust compiler are a bit weird so I'll skip them for now.
//Float(f64),
Float(FloatWrapper),
Bool(bool),
String(Intern<String>),
//Unknown(Intern<String>),
Zst(Intern<String>),
}

/// This is an unsafe wrapper around f64 in that it defines hash and equality
/// based on bit representation. This is not in line with float semantics.
///
/// But I really need this to be hash and eq, hence the hack.
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
#[serde(transparent)]
pub struct FloatWrapper(pub f64);

impl std::cmp::PartialEq for FloatWrapper {
fn eq(&self, other: &Self) -> bool {
unsafe {
std::mem::transmute::<&f64, &u64>(&self.0)
== std::mem::transmute::<&f64, &u64>(&other.0)
}
}
}

impl std::cmp::Eq for FloatWrapper {}

impl std::hash::Hash for FloatWrapper {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe {
std::mem::transmute::<&f64, &u64>(&self.0).hash(state);
}
}
}

impl Constant {
pub fn int(i: impl Into<i64>) -> Self {
Self::Int(i.into())
Expand Down Expand Up @@ -368,6 +395,7 @@ impl std::fmt::Display for Constant {
Self::Uint(u) => Display::fmt(u, f),
Self::Char(c) => Display::fmt(c, f),
Self::String(s) => Debug::fmt(s, f),
Self::Float(fl) => Display::fmt(&fl.0, f),
Self::Zst(s) => f.write_str(s),
//Self::Unknown(u) => write!(f, "Unsupported constant: {u}"),
}
Expand Down
133 changes: 100 additions & 33 deletions crates/flowistry_pdg_construction/src/analysis/async_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use rustc_abi::{FieldIdx, VariantIdx};
use rustc_hir::def_id::DefId;
use rustc_middle::{
mir::{
AggregateKind, BasicBlock, Body, Location, Operand, Place, Rvalue, Statement,
visit, AggregateKind, BasicBlock, Body, Location, Operand, Place, Rvalue, Statement,
StatementKind, Terminator, TerminatorKind,
},
ty::{GenericArgsRef, Instance, TyCtxt},
ty::{self, GenericArgsRef, Instance, TyCtxt},
};
use rustc_span::{source_map::Spanned, Span};

Expand All @@ -26,6 +26,17 @@ use crate::utils;
pub enum AsyncType {
Fn,
Trait,
Tool,
}

impl AsyncType {
pub fn describe(&self) -> &'static str {
match self {
AsyncType::Fn => "async function",
AsyncType::Trait => "async trait",
AsyncType::Tool => "async tool",
}
}
}

/// Context for a call to [`Future::poll`](std::future::Future::poll), when
Expand Down Expand Up @@ -70,37 +81,22 @@ pub fn try_as_async_trait_function<'tcx>(
if !has_async_trait_signature(tcx, def_id) {
return None;
}
let mut matching_statements =
body.basic_blocks
.iter_enumerated()
.flat_map(|(block, bbdat)| {
bbdat.statements.iter().enumerate().filter_map(
move |(statement_index, statement)| {
let (def_id, generics) = match_async_trait_assign(statement)?;
Some((
def_id,
generics,
Location {
block,
statement_index,
},
))
},
)
})
.collect::<Vec<_>>();
assert_eq!(matching_statements.len(), 1);
matching_statements.pop()
let (def_id, generics, loc, _) = find_coroutine_assign(body);
Some((def_id, generics, loc))
}

pub fn match_async_trait_assign<'tcx>(
statement: &Statement<'tcx>,
) -> Option<(DefId, GenericArgsRef<'tcx>)> {
pub fn match_coroutine_assign<'tcx, 'a>(
statement: &'a Statement<'tcx>,
) -> Option<(
DefId,
GenericArgsRef<'tcx>,
&'a rustc_index::IndexVec<FieldIdx, Operand<'tcx>>,
)> {
match &statement.kind {
StatementKind::Assign(box (
_,
Rvalue::Aggregate(box AggregateKind::Coroutine(def_id, generic_args), _args),
)) => Some((*def_id, *generic_args)),
Rvalue::Aggregate(box AggregateKind::Coroutine(def_id, generic_args), args),
)) => Some((*def_id, *generic_args, args)),
_ => None,
}
}
Expand All @@ -121,7 +117,6 @@ fn has_async_trait_signature(tcx: TyCtxt, def_id: DefId) -> bool {
}
}

use rustc_middle::ty;
fn match_pin_box_dyn_ty(lang_items: &rustc_hir::LanguageItems, t: ty::Ty) -> bool {
let ty::TyKind::Adt(pin_ty, args) = t.kind() else {
return false;
Expand Down Expand Up @@ -166,6 +161,79 @@ fn get_async_generator<'tcx>(body: &Body<'tcx>) -> (DefId, GenericArgsRef<'tcx>,
(*def_id, generic_args, location)
}

// matches std::pin::Pin<std::boxed::Box<dyn std::future::Future<_>>
fn has_async_tool_signature(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
use rustc_hir::def::DefKind;
if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
return false;
}
let sig = tcx.fn_sig(def_id);
let lang_items = tcx.lang_items();
let ty::TyKind::Adt(adt_def, inner) = sig.skip_binder().output().skip_binder().kind() else {
return false;
};
if !lang_items.pin_type().is_some_and(|p| p == adt_def.did()) {
return false;
}
let [b_ty] = inner.as_slice() else {
return false;
};
let Some(f_ty) = b_ty.as_type().and_then(ty::Ty::boxed_ty) else {
return false;
};
let ty::TyKind::Dynamic(preds, _, ty::DynKind::Dyn) = f_ty.kind() else {
return false;
};
let Some(ty::ExistentialPredicate::Trait(t)) = preds.first().map(|b| b.skip_binder()) else {
return false;
};
lang_items.future_trait().is_some_and(|f| f == t.def_id)
}

pub fn find_coroutine_assign<'tcx, 'a>(
body: &'a Body<'tcx>,
) -> (
DefId,
GenericArgsRef<'tcx>,
Location,
&'a rustc_index::IndexVec<FieldIdx, Operand<'tcx>>,
) {
let mut matching_statements =
body.basic_blocks
.iter_enumerated()
.flat_map(|(block, bbdat)| {
bbdat.statements.iter().enumerate().filter_map(
move |(statement_index, statement)| {
let (def_id, generics, args) = match_coroutine_assign(statement)?;
Some((
def_id,
generics,
Location {
block,
statement_index,
},
args,
))
},
)
})
.collect::<Vec<_>>();
assert_eq!(matching_statements.len(), 1);
matching_statements.pop().unwrap()
}

fn try_as_async_tool<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
body: &Body<'tcx>,
) -> Option<(DefId, GenericArgsRef<'tcx>, Location)> {
if !has_async_tool_signature(tcx, def_id) {
return None;
}
let (def_id, gargs, loc, _) = find_coroutine_assign(body);
Some((def_id, gargs, loc))
}

/// Try to interpret this function as an async function.
///
/// If this is an async function it returns the [`Instance`] of the generator,
Expand All @@ -178,11 +246,10 @@ pub fn determine_async<'tcx>(
) -> Option<(Instance<'tcx>, Location, AsyncType)> {
let ((generator_def_id, args, loc), asyncness) = if is_async(tcx, def_id) {
(get_async_generator(body), AsyncType::Fn)
} else if let Some(g) = try_as_async_trait_function(tcx, def_id, body) {
(g, AsyncType::Trait)
} else {
(
try_as_async_trait_function(tcx, def_id, body)?,
AsyncType::Trait,
)
(try_as_async_tool(tcx, def_id, body)?, AsyncType::Tool)
};
let typing_env = body.typing_env(tcx).with_post_analysis_normalized(tcx);
let generator_fn = utils::try_resolve_function(tcx, generator_def_id, typing_env, args)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@

use std::{borrow::Cow, hash::Hash, rc::Rc};

use flowistry::mir::FlowistryInput;
use flowistry_pdg::{CallString, GlobalLocation, RichLocation};
use rustc_middle::{mir::Location, ty::Instance};
use rustc_middle::{
mir::{self, Location},
ty::Instance,
};

use crate::{analysis::global::partial_graph::NodeKey, DepNodeKind, MemoPdgConstructor};

Expand Down Expand Up @@ -80,6 +84,13 @@ impl<'tcx, 'c, K: Clone> VisitDriver<'tcx, 'c, K> {
},
)
}

pub fn current_body(&self) -> &'tcx mir::Body<'tcx> {
self.memo
.body_cache()
.get(self.current_function().def_id())
.body()
}
}

pub trait Visitor<'tcx, K: Hash + Eq + Clone> {
Expand Down
11 changes: 7 additions & 4 deletions crates/flowistry_pdg_construction/src/analysis/global/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::{
callback::DefaultCallback,
constants::PlaceOrConst,
source_access::{self, BodyCache, CachedBody},
utils::{manufacture_substs_for, try_resolve_function, TwoLevelCache},
utils::{manufacture_substs_for, try_resolve_function, PlaceConflictContext, TwoLevelCache},
CallChangeCallback,
};

Expand Down Expand Up @@ -84,6 +84,7 @@ pub struct MemoPdgConstructor<'tcx, K> {
pub(crate) body_cache: Rc<source_access::BodyCache<'tcx>>,
disable_cache: bool,
relaxed: bool,
pub(crate) place_conflict_context: PlaceConflictContext<'tcx>,
}

impl<'tcx, K: Default> MemoPdgConstructor<'tcx, K> {
Expand All @@ -105,6 +106,7 @@ impl<'tcx, K: Default> MemoPdgConstructor<'tcx, K> {
body_cache,
disable_cache: false,
relaxed: false,
place_conflict_context: PlaceConflictContext::new(tcx),
}
}
}
Expand All @@ -124,6 +126,7 @@ impl<'tcx, K> MemoPdgConstructor<'tcx, K> {
body_cache: Rc::new(BodyCache::new(tcx)),
disable_cache: false,
relaxed: false,
place_conflict_context: PlaceConflictContext::new(tcx),
}
}

Expand Down Expand Up @@ -658,7 +661,7 @@ enum Input<'tcx> {
},
}

struct GraphSizeEstimator {
pub struct GraphSizeEstimator {
nodes: usize,
edges: usize,
functions: usize,
Expand All @@ -667,7 +670,7 @@ struct GraphSizeEstimator {

#[allow(dead_code)]
impl GraphSizeEstimator {
fn new() -> Self {
pub fn new() -> Self {
Self {
nodes: 0,
edges: 0,
Expand All @@ -676,7 +679,7 @@ impl GraphSizeEstimator {
}
}

fn format_size(&self) -> String {
pub fn format_size(&self) -> String {
format!(
"nodes: {}, edges: {}, functions: {}, call_string_length: {}",
HumanInt(self.nodes),
Expand Down
20 changes: 17 additions & 3 deletions crates/flowistry_pdg_construction/src/analysis/local/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,11 @@ impl<'tcx, 'a, K> LocalAnalysis<'tcx, 'a, K> {
place.local == alias.local
} else {
trace!("Checking conflict status of {place:?} and {alias:?}");
utils::places_conflict(self.tcx(), &self.mono_body, *place, alias)
self.memo.place_conflict_context.places_conflict(
&self.mono_body,
*place,
alias,
)
}
});

Expand Down Expand Up @@ -366,7 +370,7 @@ impl<'tcx, 'a, K> LocalAnalysis<'tcx, 'a, K> {
}

fn fmt_fn(&self, def_id: DefId) -> String {
self.tcx().def_path_str(def_id)
format!("{} ({}:{})", self.tcx().def_path_str(def_id), def_id.krate.as_u32(), def_id.index.as_u32())
}
}

Expand Down Expand Up @@ -757,7 +761,17 @@ impl<'tcx, 'a, K: Hash + Eq + Clone> LocalAnalysis<'tcx, 'a, K> {
} else {
continue;
};
let src = final_state.get_place_node(*place, location.into()).unwrap();
let Some(src) = final_state.get_place_node(*place, location.into()) else {
let span = match location {
RichLocation::Location(l) => self.mono_body.source_info(*l).span,
_ => self.mono_body.span,
};
// CORNER CUTTING: we should investigate why this happens.
self.tcx()
.dcx()
.span_warn(span, format!("could not find reference to {place:?} here"));
continue;
};
let eloc = RichLocation::End;
let key = NodeKey::for_place(*place, eloc.into());
let prior = final_state.get_node(&key);
Expand Down
Loading
Loading