Skip to content

Commit

Permalink
Migrate auxiliary changes from cross crate (#160)
Browse files Browse the repository at this point in the history
## What Changed?

These are changes and fixes from the `cross-crate` branch that are
independent of cross crate analysis itself.

- Factor the local analysis from `construct.rs` into its own module
(`local_analysis.rs`).
- Factor out the function approximations into `approximations.rs`
- Additional test cases
- Full caching for child graphs independent of call strings
- Removal of `FnResolution`, instead expecting `Instance` everywhere.

## Why Does It Need To?

Expands the test suite and lines up test cases for as-yet-unfixed
issues. Makes `construct.rs` more readable and navigable.

## Checklist

- [x] Above description has been filled out so that upon quash merge we
have a
  good record of what changed.
- [ ] New functions, methods, types are documented. Old documentation is
updated
  if necessary
- [x] Documentation in Notion has been updated
- [x] Tests for new behaviors are provided
  - [ ] New test suites (if any) ave been added to the CI tests (in
`.github/workflows/rust.yml`) either as compiler test or integration
test.
*Or* justification for their omission from CI has been provided in this
PR
    description.
  • Loading branch information
JustusAdam authored Jul 20, 2024
1 parent b96d18d commit f7c6562
Show file tree
Hide file tree
Showing 33 changed files with 2,413 additions and 1,772 deletions.
10 changes: 6 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 9 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ flowistry = { git = "https://github.com/brownsys/flowistry", rev = "b9210041eb84
[profile.release]
debug = true

[replace]
# "rustc_utils:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_utils" }
# "rustc_plugin:0.7.4-nightly-2023-08-25" = { path = "../rustc_plugin/crates/rustc_plugin" }

"rustc_utils:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" }
"rustc_plugin:0.7.4-nightly-2023-08-25" = { git = "https://github.com/JustusAdam/rustc_plugin", rev = "d4fefb5c0344cdf4812b4877d5b03cb19a2c4672" }
[replace."rustc_utils:0.7.4-nightly-2023-08-25"]
# path = "../rustc_plugin/crates/rustc_utils"
git = "https://github.com/JustusAdam/rustc_plugin"
rev = "6cc378d254e0c4fdfdbec975f82302173bfa0586"

[replace."rustc_plugin:0.7.4-nightly-2023-08-25"]
# path = "../rustc_plugin/crates/rustc_plugin"
git = "https://github.com/JustusAdam/rustc_plugin"
rev = "6cc378d254e0c4fdfdbec975f82302173bfa0586"
4 changes: 4 additions & 0 deletions crates/flowistry_pdg/src/pdg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ impl CallString {
CallString::new(string)
}

pub fn push_front(self, loc: GlobalLocation) -> Self {
CallString::new([loc].into_iter().chain(self.0.iter().copied()).collect())
}

pub fn is_at_root(self) -> bool {
self.0.len() == 1
}
Expand Down
2 changes: 2 additions & 0 deletions crates/flowistry_pdg_construction/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ flowistry_pdg = { version = "0.1.0", path = "../flowistry_pdg", features = [
] }
#flowistry = { path = "../../../flowistry/crates/flowistry", default-features = false }
flowistry = { workspace = true }
serde = { workspace = true, features = ["derive"] }
strum = { workspace = true }

[dev-dependencies]
rustc_utils = { workspace = true, features = ["indexical", "test"] }
Expand Down
76 changes: 76 additions & 0 deletions crates/flowistry_pdg_construction/src/approximation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use log::trace;

use rustc_abi::VariantIdx;

use rustc_hir::def_id::DefId;
use rustc_index::IndexVec;
use rustc_middle::{
mir::{visit::Visitor, AggregateKind, Location, Operand, Place, Rvalue},
ty::TyKind,
};

use crate::local_analysis::LocalAnalysis;

pub(crate) type ApproximationHandler<'tcx, 'a> =
fn(&LocalAnalysis<'tcx, 'a>, &mut dyn Visitor<'tcx>, &[Operand<'tcx>], Place<'tcx>, Location);

impl<'tcx, 'a> LocalAnalysis<'tcx, 'a> {
/// Special case behavior for calls to functions used in desugaring async functions.
///
/// Ensures that functions like `Pin::new_unchecked` are not modularly-approximated.
pub(crate) fn can_approximate_async_functions(
&self,
def_id: DefId,
) -> Option<ApproximationHandler<'tcx, 'a>> {
let lang_items = self.tcx().lang_items();
if Some(def_id) == lang_items.new_unchecked_fn() {
Some(Self::approximate_new_unchecked)
} else if Some(def_id) == lang_items.into_future_fn()
// FIXME: better way to get retrieve this stdlib DefId?
|| self.tcx().def_path_str(def_id) == "<F as std::future::IntoFuture>::into_future"
{
Some(Self::approximate_into_future)
} else {
None
}
}

fn approximate_into_future(
&self,
vis: &mut dyn Visitor<'tcx>,
args: &[Operand<'tcx>],
destination: Place<'tcx>,
location: Location,
) {
trace!("Handling into_future as assign for {destination:?}");
let [op] = args else {
unreachable!();
};
vis.visit_assign(&destination, &Rvalue::Use(op.clone()), location);
}

fn approximate_new_unchecked(
&self,
vis: &mut dyn Visitor<'tcx>,
args: &[Operand<'tcx>],
destination: Place<'tcx>,
location: Location,
) {
let lang_items = self.tcx().lang_items();
let [op] = args else {
unreachable!();
};
let mut operands = IndexVec::new();
operands.push(op.clone());
let TyKind::Adt(adt_id, generics) = destination.ty(&self.mono_body, self.tcx()).ty.kind()
else {
unreachable!()
};
assert_eq!(adt_id.did(), lang_items.pin_type().unwrap());
let aggregate_kind =
AggregateKind::Adt(adt_id.did(), VariantIdx::from_u32(0), generics, None, None);
let rvalue = Rvalue::Aggregate(Box::new(aggregate_kind), operands);
trace!("Handling new_unchecked as assign for {destination:?}");
vis.visit_assign(&destination, &rvalue, location);
}
}
77 changes: 40 additions & 37 deletions crates/flowistry_pdg_construction/src/async_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ use rustc_middle::{
AggregateKind, BasicBlock, Body, Location, Operand, Place, Rvalue, Statement,
StatementKind, Terminator, TerminatorKind,
},
ty::{GenericArgsRef, TyCtxt},
ty::{GenericArgsRef, Instance, TyCtxt},
};

use crate::construct::{CallKind, PartialGraph};
use super::{
local_analysis::{CallKind, LocalAnalysis},
utils,
};

use super::construct::GraphConstructor;
use super::utils::{self, FnResolution};
/// Describe in which way a function is `async`.
///
/// Critically distinguishes between a normal `async fn` and an
/// `#[async_trait]`.
#[derive(Debug, Clone, Copy)]
pub enum AsyncType {
Fn,
Trait,
}

/// Stores ids that are needed to construct projections around async functions.
pub(crate) struct AsyncInfo {
Expand Down Expand Up @@ -143,20 +153,28 @@ fn get_async_generator<'tcx>(body: &Body<'tcx>) -> (LocalDefId, GenericArgsRef<'
(def_id.expect_local(), generic_args, location)
}

/// Try to interpret this function as an async function.
///
/// If this is an async function it returns the [`Instance`] of the generator,
/// the location where the generator is bound and the type of [`Asyncness`]
/// which in this case is guaranteed to satisfy [`Asyncness::is_async`].
pub fn determine_async<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
body: &Body<'tcx>,
) -> Option<(FnResolution<'tcx>, Location)> {
let (generator_def_id, args, loc) = if tcx.asyncness(def_id).is_async() {
get_async_generator(body)
) -> Option<(Instance<'tcx>, Location, AsyncType)> {
let ((generator_def_id, args, loc), asyncness) = if tcx.asyncness(def_id).is_async() {
(get_async_generator(body), AsyncType::Fn)
} else {
try_as_async_trait_function(tcx, def_id.to_def_id(), body)?
(
try_as_async_trait_function(tcx, def_id.to_def_id(), body)?,
AsyncType::Trait,
)
};
let param_env = tcx.param_env_reveal_all_normalized(def_id);
let generator_fn =
utils::try_resolve_function(tcx, generator_def_id.to_def_id(), param_env, args);
Some((generator_fn, loc))
utils::try_resolve_function(tcx, generator_def_id.to_def_id(), param_env, args)?;
Some((generator_fn, loc, asyncness))
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand All @@ -166,29 +184,13 @@ pub enum AsyncDeterminationResult<T> {
NotAsync,
}

impl<'tcx> GraphConstructor<'tcx> {
pub(crate) fn try_handle_as_async(&self) -> Option<PartialGraph<'tcx>> {
let (generator_fn, location) = determine_async(self.tcx, self.def_id, &self.body)?;

let calling_context = self.calling_context_for(generator_fn.def_id(), location);
let params = self.pdg_params_for_call(generator_fn);
Some(
GraphConstructor::new(
params,
Some(calling_context),
self.async_info.clone(),
&self.pdg_cache,
)
.construct_partial(),
)
}

impl<'tcx, 'mir> LocalAnalysis<'tcx, 'mir> {
pub(crate) fn try_poll_call_kind<'a>(
&'a self,
def_id: DefId,
original_args: &'a [Operand<'tcx>],
) -> AsyncDeterminationResult<CallKind<'tcx>> {
let lang_items = self.tcx.lang_items();
let lang_items = self.tcx().lang_items();
if lang_items.future_poll_fn() == Some(def_id) {
match self.find_async_args(original_args) {
Ok((fun, loc, args)) => {
Expand All @@ -205,7 +207,7 @@ impl<'tcx> GraphConstructor<'tcx> {
fn find_async_args<'a>(
&'a self,
args: &'a [Operand<'tcx>],
) -> Result<(FnResolution<'tcx>, Location, Place<'tcx>), String> {
) -> Result<(Instance<'tcx>, Location, Place<'tcx>), String> {
macro_rules! let_assert {
($p:pat = $e:expr, $($arg:tt)*) => {
let $p = $e else {
Expand Down Expand Up @@ -235,13 +237,13 @@ impl<'tcx> GraphConstructor<'tcx> {
..
},
..
}) = &self.body.stmt_at(get_def_for_op(&args[0])?),
}) = &self.mono_body.stmt_at(get_def_for_op(&args[0])?),
"Pinned assignment is not a call"
);
debug_assert!(new_pin_args.len() == 1);

let future_aliases = self
.aliases(self.tcx.mk_place_deref(new_pin_args[0].place().unwrap()))
.aliases(self.tcx().mk_place_deref(new_pin_args[0].place().unwrap()))
.collect_vec();
debug_assert!(future_aliases.len() == 1);
let future = *future_aliases.first().unwrap();
Expand All @@ -250,7 +252,7 @@ impl<'tcx> GraphConstructor<'tcx> {
Either::Left(Statement {
kind: StatementKind::Assign(box (_, Rvalue::Use(future2))),
..
}) = &self.body.stmt_at(get_def_for_op(&Operand::Move(future))?),
}) = &self.mono_body.stmt_at(get_def_for_op(&Operand::Move(future))?),
"Assignment to pin::new input is not a statement"
);

Expand All @@ -261,15 +263,15 @@ impl<'tcx> GraphConstructor<'tcx> {
..
},
..
}) = &self.body.stmt_at(get_def_for_op(future2)?),
}) = &self.mono_body.stmt_at(get_def_for_op(future2)?),
"Assignment to alias of pin::new input is not a call"
);

let mut chase_target = Err(&into_future_args[0]);

while let Err(target) = chase_target {
let async_fn_call_loc = get_def_for_op(target)?;
let stmt = &self.body.stmt_at(async_fn_call_loc);
let stmt = &self.mono_body.stmt_at(async_fn_call_loc);
chase_target = match stmt {
Either::Right(Terminator {
kind:
Expand Down Expand Up @@ -308,11 +310,12 @@ impl<'tcx> GraphConstructor<'tcx> {
let (op, generics, calling_convention, async_fn_call_loc) = chase_target.unwrap();

let resolution = utils::try_resolve_function(
self.tcx,
self.tcx(),
op,
self.tcx.param_env_reveal_all_normalized(self.def_id),
self.tcx().param_env_reveal_all_normalized(self.def_id),
generics,
);
)
.ok_or_else(|| "Instance resolution failed".to_string())?;

Ok((resolution, async_fn_call_loc, calling_convention))
}
Expand Down
11 changes: 5 additions & 6 deletions crates/flowistry_pdg_construction/src/callback.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
//! CAllbacks to influence graph construction and their supporting types.
use flowistry_pdg::{rustc_portable::Location, CallString};

use crate::FnResolution;
use rustc_middle::ty::Instance;

pub trait CallChangeCallback<'tcx> {
fn on_inline(&self, info: CallInfo<'tcx>) -> CallChanges;

fn on_inline_miss(
&self,
_resolution: FnResolution<'tcx>,
_resolution: Instance<'tcx>,
_loc: Location,
_under_analysis: FnResolution<'tcx>,
_under_analysis: Instance<'tcx>,
_call_string: Option<CallString>,
_reason: InlineMissReason,
) {
Expand Down Expand Up @@ -50,11 +49,11 @@ impl Default for CallChanges {
/// Information about the function being called.
pub struct CallInfo<'tcx> {
/// The potentially-monomorphized resolution of the callee.
pub callee: FnResolution<'tcx>,
pub callee: Instance<'tcx>,

/// If the callee is an async closure created by an `async fn`, this is the
/// `async fn` item.
pub async_parent: Option<FnResolution<'tcx>>,
pub async_parent: Option<Instance<'tcx>>,

/// The call-stack up to the current call site.
pub call_string: CallString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_middle::{
ty::TyCtxt,
};

use crate::{async_support::AsyncInfo, construct::CallKind, utils};
use crate::{async_support::AsyncInfo, local_analysis::CallKind, utils};

pub enum CallingConvention<'tcx, 'a> {
Direct(&'a [Operand<'tcx>]),
Expand Down
Loading

0 comments on commit f7c6562

Please sign in to comment.