From 32ed3568c8fa4b45d4b1f11f0186d1a4c8d9f60c Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Thu, 27 Feb 2025 19:37:48 -0800 Subject: [PATCH 01/21] WIP --- src/borrow_pcg/action/actions.rs | 83 ++++++++- src/borrow_pcg/action/mod.rs | 94 +++++++---- src/borrow_pcg/borrow_pcg_edge.rs | 8 +- src/borrow_pcg/domain.rs | 18 +- src/borrow_pcg/edge/kind.rs | 16 +- src/borrow_pcg/graph/aliases.rs | 12 +- src/borrow_pcg/graph/mod.rs | 10 +- src/borrow_pcg/region_projection_member.rs | 66 +++++--- src/borrow_pcg/state/obtain.rs | 10 +- src/borrow_pcg/visitor/mod.rs | 8 +- src/borrow_pcg/visitor/stmt.rs | 12 +- src/combined_pcs/mod.rs | 2 + src/free_pcs/results/cursor.rs | 55 ++---- src/lib.rs | 157 +++++------------- src/visualization/graph_constructor.rs | 24 +-- src/visualization/mod.rs | 5 +- .../src/components/BorrowsAndActions.tsx | 24 +-- visualization/src/types.ts | 14 +- 18 files changed, 321 insertions(+), 297 deletions(-) diff --git a/src/borrow_pcg/action/actions.rs b/src/borrow_pcg/action/actions.rs index d5c5372d..10b46044 100644 --- a/src/borrow_pcg/action/actions.rs +++ b/src/borrow_pcg/action/actions.rs @@ -1,10 +1,79 @@ use crate::borrow_pcg::action::BorrowPCGAction; -use crate::validity_checks_enabled; +use crate::borrow_pcg::borrow_pcg_expansion::BorrowPCGExpansion; +use crate::borrow_pcg::graph::Conditioned; +use crate::borrow_pcg::path_condition::PathConditions; +use crate::borrow_pcg::region_projection_member::BlockEdge; +use crate::borrow_pcg::unblock_graph::BorrowPCGUnblockAction; +use crate::utils::json::ToJsonWithRepacker; +use crate::utils::PlaceRepacker; +use crate::{validity_checks_enabled, Weaken}; +use crate::rustc_interface::data_structures::fx::FxHashSet; -#[derive(Clone, Debug)] -#[derive(Default)] -pub(crate) struct BorrowPCGActions<'tcx>(Vec>); +use super::BorrowPCGActionKind; +#[derive(Clone, Debug, Default)] +pub struct BorrowPCGActions<'tcx>(pub(crate) Vec>); + +impl<'tcx> ToJsonWithRepacker<'tcx> for BorrowPCGActions<'tcx> { + fn to_json(&self, repacker: PlaceRepacker<'_, 'tcx>) -> serde_json::Value { + self.0 + .iter() + .map(|a| a.to_json(repacker)) + .collect::>() + .into() + } +} + +impl<'tcx> BorrowPCGActions<'tcx> { + /// Actions applied to the PCG, in the order they occurred. + pub fn actions(&self) -> &[BorrowPCGAction<'tcx>] { + &self.0 + } + + pub fn added_region_projection_members(&self) -> FxHashSet>> { + self.0 + .iter() + .filter_map(|action| match action.kind() { + BorrowPCGActionKind::AddBlockEdge(member, path_conditions) => { + Some(Conditioned::new(member.clone(), path_conditions.clone())) + } + _ => None, + }) + .collect() + } + + pub fn weakens(&self) -> FxHashSet> { + self.0 + .iter() + .filter_map(|action| match action.kind() { + BorrowPCGActionKind::Weaken(weaken) => Some(*weaken), + _ => None, + }) + .collect() + } + + pub fn unblock_actions(&self) -> Vec> { + self.0 + .iter() + .filter_map(|action| match action.kind() { + BorrowPCGActionKind::RemoveEdge(edge) => Some(edge.clone().into()), + _ => None, + }) + .collect() + } + + pub fn expands(&self) -> FxHashSet>> { + self.0 + .iter() + .filter_map(|action| match action.kind() { + BorrowPCGActionKind::InsertBorrowPCGExpansion(expansion, location) => Some( + Conditioned::new(expansion.clone(), PathConditions::AtBlock(location.block)), + ), + _ => None, + }) + .collect() + } +} impl<'tcx> BorrowPCGActions<'tcx> { pub(crate) fn is_empty(&self) -> bool { @@ -36,11 +105,13 @@ impl<'tcx> BorrowPCGActions<'tcx> { pub(crate) fn extend(&mut self, actions: BorrowPCGActions<'tcx>) { if validity_checks_enabled() { - if let (Some(a), Some(b)) = (self.last(), actions.first()) { assert_ne!( + if let (Some(a), Some(b)) = (self.last(), actions.first()) { + assert_ne!( a, b, "The last action ({:#?}) is the same as the first action in the list to extend with.", a, - ) } + ) + } } self.0.extend(actions.0); } diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index e2717d27..9d9069d3 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -3,7 +3,7 @@ use tracing::instrument; use super::borrow_pcg_edge::{BorrowPCGEdge, LocalNode, ToBorrowsEdge}; use super::borrow_pcg_expansion::BorrowPCGExpansion; use super::path_condition::PathConditions; -use super::region_projection_member::RegionProjectionMember; +use super::region_projection_member::BlockEdge; use super::state::BorrowsState; use crate::borrow_pcg::edge::abstraction::AbstractionType; use crate::combined_pcs::{PCGNode, PCGNodeLike}; @@ -15,7 +15,7 @@ use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::{Place, PlaceRepacker, SnapshotLocation}; use crate::{RestoreCapability, Weaken}; -pub(crate) mod actions; +pub mod actions; pub(crate) mod executed_actions; /// An action that is applied to a `BorrowsState` during the dataflow analysis @@ -28,13 +28,6 @@ pub struct BorrowPCGAction<'tcx> { } impl<'tcx> BorrowPCGAction<'tcx> { - pub(crate) fn debug_line_with_context(&self, repacker: PlaceRepacker<'_, 'tcx>) -> String { - if let Some(context) = &self.debug_context { - format!("{}: {}", context, self.debug_line(repacker)) - } else { - self.debug_line(repacker) - } - } pub(crate) fn debug_line(&self, repacker: PlaceRepacker<'_, 'tcx>) -> String { match &self.kind { BorrowPCGActionKind::AddAbstractionEdge(abstraction, path_conditions) => { @@ -59,14 +52,13 @@ impl<'tcx> BorrowPCGAction<'tcx> { BorrowPCGActionKind::RemoveEdge(borrow_pcgedge) => { format!("Remove Edge {}", borrow_pcgedge.to_short_string(repacker)) } - BorrowPCGActionKind::AddRegionProjectionMember( - region_projection_member, - path_conditions, - ) => format!( - "Add Region Projection Member: {}; path conditions: {}", - region_projection_member.to_short_string(repacker), - path_conditions - ), + BorrowPCGActionKind::AddBlockEdge(region_projection_member, path_conditions) => { + format!( + "Add Region Projection Member: {}; path conditions: {}", + region_projection_member.to_short_string(repacker), + path_conditions + ) + } BorrowPCGActionKind::InsertBorrowPCGExpansion(expansion, location) => format!( "Insert Expansion {} at {:?}", expansion.to_short_string(repacker), @@ -122,12 +114,12 @@ impl<'tcx> BorrowPCGAction<'tcx> { } pub(super) fn add_region_projection_member( - member: RegionProjectionMember<'tcx>, + member: BlockEdge<'tcx>, pc: PathConditions, context: impl Into, ) -> Self { BorrowPCGAction { - kind: BorrowPCGActionKind::AddRegionProjectionMember(member, pc), + kind: BorrowPCGActionKind::AddBlockEdge(member, pc), debug_context: Some(context.into()), } } @@ -167,7 +159,7 @@ pub enum BorrowPCGActionKind<'tcx> { MakePlaceOld(Place<'tcx>), SetLatest(Place<'tcx>, Location), RemoveEdge(BorrowPCGEdge<'tcx>), - AddRegionProjectionMember(RegionProjectionMember<'tcx>, PathConditions), + AddBlockEdge(BlockEdge<'tcx>, PathConditions), InsertBorrowPCGExpansion(BorrowPCGExpansion<'tcx>, Location), AddAbstractionEdge(AbstractionType<'tcx>, PathConditions), RenamePlace { @@ -176,16 +168,51 @@ pub enum BorrowPCGActionKind<'tcx> { }, } -impl<'tcx> ToJsonWithRepacker<'tcx> for BorrowPCGAction<'tcx> { - fn to_json(&self, _repacker: PlaceRepacker<'_, 'tcx>) -> serde_json::Value { - if let Some(context) = &self.debug_context { - format!("{}: {:?}", context, self.kind).into() - } else { - format!("{:?}", self).into() +impl<'tcx> DisplayWithRepacker<'tcx> for BorrowPCGActionKind<'tcx> { + fn to_short_string(&self, repacker: PlaceRepacker<'_, 'tcx>) -> String { + match self { + BorrowPCGActionKind::Weaken(weaken) => weaken.debug_line(repacker), + BorrowPCGActionKind::Restore(restore_capability) => { + restore_capability.debug_line(repacker) + } + BorrowPCGActionKind::MakePlaceOld(place) => { + format!("Make {} an old place", place.to_short_string(repacker)) + } + BorrowPCGActionKind::SetLatest(place, location) => format!( + "Set Latest of {} to {:?}", + place.to_short_string(repacker), + location + ), + BorrowPCGActionKind::RemoveEdge(borrow_pcgedge) => { + format!("Remove Edge {}", borrow_pcgedge.to_short_string(repacker)) + } + BorrowPCGActionKind::AddBlockEdge(block_edge, _) => { + format!("Add Block Edge: {}", block_edge.to_short_string(repacker),) + } + BorrowPCGActionKind::InsertBorrowPCGExpansion(borrow_pcgexpansion, location) => { + format!( + "Insert Expansion {} at {:?}", + borrow_pcgexpansion.to_short_string(repacker), + location + ) + } + BorrowPCGActionKind::AddAbstractionEdge(abstraction_type, _) => format!( + "Add Abstraction Edge: {}", + abstraction_type.to_short_string(repacker), + ), + BorrowPCGActionKind::RenamePlace { old, new } => { + format!("Rename {:?} to {:?}", old, new) + } } } } +impl<'tcx> ToJsonWithRepacker<'tcx> for BorrowPCGAction<'tcx> { + fn to_json(&self, repacker: PlaceRepacker<'_, 'tcx>) -> serde_json::Value { + self.kind.to_short_string(repacker).into() + } +} + impl<'tcx> BorrowsState<'tcx> { #[instrument(skip(self, repacker), fields(action))] #[must_use] @@ -203,8 +230,11 @@ impl<'tcx> BorrowsState<'tcx> { self.set_capability(input.into(), CapabilityKind::Lent, repacker); } for output in edge.outputs() { - changed |= - self.set_capability(output.to_pcg_node(repacker), CapabilityKind::Exclusive, repacker); + changed |= self.set_capability( + output.to_pcg_node(repacker), + CapabilityKind::Exclusive, + repacker, + ); } } changed |= self.insert(abstraction.to_borrow_pcg_edge(pc)); @@ -215,7 +245,9 @@ impl<'tcx> BorrowsState<'tcx> { if let Some(cap) = self.get_capability(restore_node) { assert!(cap < restore.capability(), "Current capability {:?} is not less than the capability to restore to {:?}", cap, restore.capability()); } - if !restore_node.is_owned(repacker) && !self.set_capability(restore_node, restore.capability(), repacker) { + if !restore_node.is_owned(repacker) + && !self.set_capability(restore_node, restore.capability(), repacker) + { tracing::error!( "Capability was already {:?} for {}", restore.capability(), @@ -239,7 +271,7 @@ impl<'tcx> BorrowsState<'tcx> { self.set_latest(place, location, repacker) } BorrowPCGActionKind::RemoveEdge(edge) => self.remove(&edge), - BorrowPCGActionKind::AddRegionProjectionMember(member, pc) => { + BorrowPCGActionKind::AddBlockEdge(member, pc) => { self.add_region_projection_member(member, pc, repacker) } BorrowPCGActionKind::InsertBorrowPCGExpansion(expansion, location) => { @@ -306,7 +338,7 @@ impl<'tcx> BorrowsState<'tcx> { /// capabilities for the place and projection fn add_region_projection_member( &mut self, - member: RegionProjectionMember<'tcx>, + member: BlockEdge<'tcx>, pc: PathConditions, repacker: PlaceRepacker<'_, 'tcx>, ) -> bool { diff --git a/src/borrow_pcg/borrow_pcg_edge.rs b/src/borrow_pcg/borrow_pcg_edge.rs index 540c7035..2f96e8c7 100644 --- a/src/borrow_pcg/borrow_pcg_edge.rs +++ b/src/borrow_pcg/borrow_pcg_edge.rs @@ -12,7 +12,7 @@ use super::{ has_pcs_elem::HasPcsElems, path_condition::{PathCondition, PathConditions}, region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, - region_projection_member::RegionProjectionMember, + region_projection_member::BlockEdge, }; use crate::borrow_pcg::edge::abstraction::AbstractionType; use crate::borrow_pcg::edge::borrow::BorrowEdge; @@ -413,7 +413,7 @@ edgedata_enum!( Borrow(BorrowEdge<'tcx>), BorrowPCGExpansion(BorrowPCGExpansion<'tcx>), Abstraction(AbstractionType<'tcx>), - RegionProjectionMember(RegionProjectionMember<'tcx>) + Block(BlockEdge<'tcx>) ); pub(crate) trait ToBorrowsEdge<'tcx> { @@ -447,11 +447,11 @@ impl<'tcx> ToBorrowsEdge<'tcx> for BorrowEdge<'tcx> { } } -impl<'tcx> ToBorrowsEdge<'tcx> for RegionProjectionMember<'tcx> { +impl<'tcx> ToBorrowsEdge<'tcx> for BlockEdge<'tcx> { fn to_borrow_pcg_edge(self, conditions: PathConditions) -> BorrowPCGEdge<'tcx> { BorrowPCGEdge { conditions, - kind: BorrowPCGEdgeKind::RegionProjectionMember(self), + kind: BorrowPCGEdgeKind::Block(self), } } } diff --git a/src/borrow_pcg/domain.rs b/src/borrow_pcg/domain.rs index 6fdd57fa..ecf5e66d 100644 --- a/src/borrow_pcg/domain.rs +++ b/src/borrow_pcg/domain.rs @@ -5,7 +5,7 @@ use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_checker::r#impl::BorrowCheckerImpl; use crate::borrow_pcg::path_condition::{PathCondition, PathConditions}; use crate::borrow_pcg::region_projection_member::{ - RegionProjectionMember, RegionProjectionMemberKind, + BlockEdge, BlockEdgeKind, }; use crate::borrow_pcg::state::BorrowsState; use crate::combined_pcs::EvalStmtPhase::*; @@ -15,7 +15,7 @@ use crate::utils::domain_data::DomainData; use crate::utils::eval_stmt_data::EvalStmtData; use crate::utils::maybe_remote::MaybeRemotePlace; use crate::utils::{Place, PlaceRepacker}; -use crate::{utils, BorrowsBridge}; +use crate::utils; use smallvec::smallvec; pub type AbstractionInputTarget<'tcx> = CGNode<'tcx>; @@ -112,12 +112,6 @@ impl std::fmt::Debug for BorrowsDomain<'_, '_> { } impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { - pub(crate) fn get_bridge(&self) -> (BorrowsBridge<'tcx>, BorrowsBridge<'tcx>) { - ( - self.actions.pre_operands.clone().into(), - self.actions.pre_main.clone().into(), - ) - } pub(crate) fn post_main_state(&self) -> &BorrowsState<'tcx> { self.data.states[PostMain].as_ref() } @@ -181,7 +175,7 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { )); let _ = entry_state.apply_action( BorrowPCGAction::add_region_projection_member( - RegionProjectionMember::new( + BlockEdge::new( smallvec![MaybeRemotePlace::place_assigned_to_local(local).into()], smallvec![RegionProjection::new( (*region).into(), @@ -190,7 +184,7 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { ) .unwrap() .into(),], - RegionProjectionMemberKind::FunctionInput, + BlockEdgeKind::FunctionInput, ), PathConditions::AtBlock((Location::START).block), "Introduce initial borrow", @@ -204,7 +198,7 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { let entry_state = Rc::>::make_mut(&mut self.data.entry_state); assert!(entry_state.apply_action( BorrowPCGAction::add_region_projection_member( - RegionProjectionMember::new( + BlockEdge::new( smallvec![RegionProjection::new( region, RemotePlace::new(local), @@ -215,7 +209,7 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { }) .to_pcg_node(self.repacker)], smallvec![region_projection.into()], - RegionProjectionMemberKind::Todo, + BlockEdgeKind::Todo, ), PathConditions::AtBlock((Location::START).block), "Initialize Local", diff --git a/src/borrow_pcg/edge/kind.rs b/src/borrow_pcg/edge/kind.rs index 47705e1b..6fac5eb9 100644 --- a/src/borrow_pcg/edge/kind.rs +++ b/src/borrow_pcg/edge/kind.rs @@ -3,7 +3,7 @@ use crate::borrow_pcg::edge::abstraction::AbstractionType; use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::borrow_pcg::has_pcs_elem::HasPcsElems; use crate::borrow_pcg::region_projection::RegionProjection; -use crate::borrow_pcg::region_projection_member::RegionProjectionMember; +use crate::borrow_pcg::region_projection_member::BlockEdge; use crate::utils::display::DisplayWithRepacker; use crate::utils::PlaceRepacker; use crate::utils::validity::HasValidityCheck; @@ -13,7 +13,7 @@ pub enum BorrowPCGEdgeKind<'tcx> { Borrow(BorrowEdge<'tcx>), BorrowPCGExpansion(BorrowPCGExpansion<'tcx>), Abstraction(AbstractionType<'tcx>), - RegionProjectionMember(RegionProjectionMember<'tcx>), + Block(BlockEdge<'tcx>), } impl<'tcx> HasValidityCheck<'tcx> for BorrowPCGEdgeKind<'tcx> { @@ -22,7 +22,7 @@ impl<'tcx> HasValidityCheck<'tcx> for BorrowPCGEdgeKind<'tcx> { BorrowPCGEdgeKind::Borrow(borrow) => borrow.check_validity(repacker), BorrowPCGEdgeKind::BorrowPCGExpansion(expansion) => expansion.check_validity(repacker), BorrowPCGEdgeKind::Abstraction(abstraction) => abstraction.check_validity(repacker), - BorrowPCGEdgeKind::RegionProjectionMember(member) => member.check_validity(repacker), + BorrowPCGEdgeKind::Block(member) => member.check_validity(repacker), } } } @@ -33,7 +33,7 @@ impl<'tcx> DisplayWithRepacker<'tcx> for BorrowPCGEdgeKind<'tcx> { BorrowPCGEdgeKind::Borrow(borrow) => borrow.to_short_string(repacker), BorrowPCGEdgeKind::BorrowPCGExpansion(expansion) => expansion.to_short_string(repacker), BorrowPCGEdgeKind::Abstraction(abstraction) => abstraction.to_short_string(repacker), - BorrowPCGEdgeKind::RegionProjectionMember(member) => member.to_short_string(repacker), + BorrowPCGEdgeKind::Block(member) => member.to_short_string(repacker), } } } @@ -41,7 +41,7 @@ impl<'tcx> DisplayWithRepacker<'tcx> for BorrowPCGEdgeKind<'tcx> { impl<'tcx> HasPcsElems> for BorrowPCGEdgeKind<'tcx> { fn pcs_elems(&mut self) -> Vec<&mut RegionProjection<'tcx>> { match self { - BorrowPCGEdgeKind::RegionProjectionMember(member) => member.pcs_elems(), + BorrowPCGEdgeKind::Block(member) => member.pcs_elems(), _ => vec![], } } @@ -50,13 +50,13 @@ impl<'tcx> HasPcsElems> for BorrowPCGEdgeKind<'tcx> { impl<'tcx, T> HasPcsElems for BorrowPCGEdgeKind<'tcx> where BorrowEdge<'tcx>: HasPcsElems, - RegionProjectionMember<'tcx>: HasPcsElems, + BlockEdge<'tcx>: HasPcsElems, BorrowPCGExpansion<'tcx>: HasPcsElems, AbstractionType<'tcx>: HasPcsElems, { fn pcs_elems(&mut self) -> Vec<&mut T> { match self { - BorrowPCGEdgeKind::RegionProjectionMember(member) => member.pcs_elems(), + BorrowPCGEdgeKind::Block(member) => member.pcs_elems(), BorrowPCGEdgeKind::Borrow(reborrow) => reborrow.pcs_elems(), BorrowPCGEdgeKind::BorrowPCGExpansion(deref_expansion) => deref_expansion.pcs_elems(), BorrowPCGEdgeKind::Abstraction(abstraction_edge) => abstraction_edge.pcs_elems(), @@ -71,4 +71,4 @@ impl BorrowPCGEdgeKind<'_> { _ => false, } } -} \ No newline at end of file +} diff --git a/src/borrow_pcg/graph/aliases.rs b/src/borrow_pcg/graph/aliases.rs index f83eb2b1..6a43008b 100644 --- a/src/borrow_pcg/graph/aliases.rs +++ b/src/borrow_pcg/graph/aliases.rs @@ -1,7 +1,7 @@ use crate::{ borrow_pcg::{ borrow_pcg_edge::LocalNode, edge::kind::BorrowPCGEdgeKind, region_projection::RegionIdx, - region_projection_member::RegionProjectionMemberKind, + region_projection_member::BlockEdgeKind, }, combined_pcs::{PCGNode, PCGNodeLike}, rustc_interface::data_structures::fx::FxHashSet, @@ -149,14 +149,14 @@ impl<'tcx> BorrowsGraph<'tcx> { } } } - BorrowPCGEdgeKind::RegionProjectionMember(region_projection_member) => { + BorrowPCGEdgeKind::Block(region_projection_member) => { match ®ion_projection_member.kind { - RegionProjectionMemberKind::DerefRegionProjection => { + BlockEdgeKind::DerefRegionProjection => { for input in region_projection_member.inputs() { extend(input.to_pcg_node(repacker), seen, &mut result, direct); } } - RegionProjectionMemberKind::FunctionInput => { + BlockEdgeKind::FunctionInput => { for input in region_projection_member.inputs() { match input { PCGNode::Place(MaybeRemotePlace::Remote(rp)) => { @@ -171,8 +171,8 @@ impl<'tcx> BorrowsGraph<'tcx> { } } } - RegionProjectionMemberKind::DerefBorrowOutlives => {} - RegionProjectionMemberKind::BorrowOutlives { toplevel } + BlockEdgeKind::DerefBorrowOutlives => {} + BlockEdgeKind::BorrowOutlives { toplevel } if !toplevel || direct => {} _ => { for input in region_projection_member.inputs() { diff --git a/src/borrow_pcg/graph/mod.rs b/src/borrow_pcg/graph/mod.rs index b576e2c8..6b024e8f 100644 --- a/src/borrow_pcg/graph/mod.rs +++ b/src/borrow_pcg/graph/mod.rs @@ -33,7 +33,7 @@ use super::{ path_condition::{PathCondition, PathConditions}, region_projection::RegionProjection, region_projection_member::{ - RegionProjectionMember, RegionProjectionMemberKind, RegionProjectionMemberOutputs, + BlockEdge, BlockEdgeKind, BlockEdgeOutputs, }, }; use crate::borrow_pcg::edge::abstraction::{ @@ -216,7 +216,7 @@ impl<'tcx> BorrowsGraph<'tcx> { // nodes (via `[RegionProjectionMember.projections]`) // These nodes may not have any edges in the coupling // graph but must be included anyway. - if let BorrowPCGEdgeKind::RegionProjectionMember(region_projection_member) = + if let BorrowPCGEdgeKind::Block(region_projection_member) = edge.kind() { let coupled_input_projections: Vec> = @@ -511,13 +511,13 @@ impl<'tcx> BorrowsGraph<'tcx> { // This node is not in the endpoint, so we create a corresponding // region projection member edge. let new_edge_kind = - BorrowPCGEdgeKind::RegionProjectionMember(RegionProjectionMember::new( + BorrowPCGEdgeKind::Block(BlockEdge::new( smallvec![node], rps.clone() .into_iter() .map(|rp| rp.try_to_local_node(repacker).unwrap()) - .collect::>(), - RegionProjectionMemberKind::Todo, + .collect::>(), + BlockEdgeKind::Todo, )); self.insert(BorrowPCGEdge::new(new_edge_kind, edge.conditions().clone())); } diff --git a/src/borrow_pcg/region_projection_member.rs b/src/borrow_pcg/region_projection_member.rs index fa62d680..78791b5d 100644 --- a/src/borrow_pcg/region_projection_member.rs +++ b/src/borrow_pcg/region_projection_member.rs @@ -14,19 +14,19 @@ use super::edge_data::EdgeData; use super::{has_pcs_elem::HasPcsElems, region_projection::RegionProjection}; use crate::utils::json::ToJsonWithRepacker; -pub(crate) type RegionProjectionMemberInputs<'tcx> = SmallVec<[PCGNode<'tcx>; 8]>; -pub(crate) type RegionProjectionMemberOutputs<'tcx> = SmallVec<[LocalNode<'tcx>; 8]>; +pub(crate) type BlockEdgeInputs<'tcx> = SmallVec<[PCGNode<'tcx>; 8]>; +pub(crate) type BlockEdgeOutputs<'tcx> = SmallVec<[LocalNode<'tcx>; 8]>; -/// A PCG hyperedge where at the nodes of at least one of the edge endpoints are -/// all region projections. +/// A generic PCG hyperedge. `outputs` blocks `inputs` #[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub struct RegionProjectionMember<'tcx> { - pub(crate) inputs: RegionProjectionMemberInputs<'tcx>, - pub(crate) outputs: RegionProjectionMemberOutputs<'tcx>, - pub(crate) kind: RegionProjectionMemberKind, +pub struct BlockEdge<'tcx> { + pub(crate) inputs: BlockEdgeInputs<'tcx>, + pub(crate) outputs: BlockEdgeOutputs<'tcx>, + /// Why this edge exists + pub(crate) kind: BlockEdgeKind, } -impl<'tcx> HasValidityCheck<'tcx> for RegionProjectionMember<'tcx> { +impl<'tcx> HasValidityCheck<'tcx> for BlockEdge<'tcx> { fn check_validity(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Result<(), String> { for input in self.inputs.iter() { input.check_validity(repacker)?; @@ -38,7 +38,7 @@ impl<'tcx> HasValidityCheck<'tcx> for RegionProjectionMember<'tcx> { } } -impl<'tcx> DisplayWithRepacker<'tcx> for RegionProjectionMember<'tcx> { +impl<'tcx> DisplayWithRepacker<'tcx> for BlockEdge<'tcx> { fn to_short_string(&self, repacker: PlaceRepacker<'_, 'tcx>) -> String { format!( "{} -> {}", @@ -57,7 +57,7 @@ impl<'tcx> DisplayWithRepacker<'tcx> for RegionProjectionMember<'tcx> { } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub enum RegionProjectionMemberKind { +pub enum BlockEdgeKind { Aggregate { field_idx: usize, target_rp_index: usize, @@ -84,9 +84,7 @@ pub enum RegionProjectionMemberKind { /// for `{y|'a} -> {x|'b}` of this kind is created if 'a outlives 'b. /// /// `toplevel` is true for edges to x↓'x, false otherwise. - BorrowOutlives { - toplevel: bool - }, + BorrowOutlives { toplevel: bool }, /// If e.g {x|'a} -> {y|'b} is a BorrowsOutlives, then {*x|'a} -> {*y|'b} is a DerefBorrowsOutlives /// (it's introduced if e.g. *y is expanded in the PCG) DerefBorrowOutlives, @@ -94,7 +92,27 @@ pub enum RegionProjectionMemberKind { Todo, } -impl<'tcx> ToJsonWithRepacker<'tcx> for RegionProjectionMember<'tcx> { +impl<'tcx> std::fmt::Display for BlockEdgeKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BlockEdgeKind::Aggregate { + field_idx, + target_rp_index, + } => write!(f, "Aggregate({field_idx}, {target_rp_index})"), + BlockEdgeKind::Borrow => write!(f, "Borrow"), + BlockEdgeKind::FunctionInput => write!(f, "FunctionInput"), + BlockEdgeKind::DerefRegionProjection => write!(f, "DerefRegionProjection"), + BlockEdgeKind::Ref => write!(f, "Ref"), + BlockEdgeKind::ContractRef => write!(f, "ContractRef"), + BlockEdgeKind::ConstRef => write!(f, "ConstRef"), + BlockEdgeKind::BorrowOutlives { toplevel } => write!(f, "BorrowOutlives({toplevel})"), + BlockEdgeKind::Todo => write!(f, "Todo"), + BlockEdgeKind::DerefBorrowOutlives => write!(f, "DerefBorrowOutlives"), + } + } +} + +impl<'tcx> ToJsonWithRepacker<'tcx> for BlockEdge<'tcx> { fn to_json(&self, repacker: PlaceRepacker<'_, 'tcx>) -> serde_json::Value { json!({ "inputs": self.inputs.iter().map(|p| p.to_json(repacker)).collect::>(), @@ -103,7 +121,7 @@ impl<'tcx> ToJsonWithRepacker<'tcx> for RegionProjectionMember<'tcx> { } } -impl<'tcx> EdgeData<'tcx> for RegionProjectionMember<'tcx> { +impl<'tcx> EdgeData<'tcx> for BlockEdge<'tcx> { fn blocked_by_nodes(&self, _repacker: PlaceRepacker<'_, 'tcx>) -> FxHashSet> { self.outputs.iter().cloned().collect() } @@ -121,14 +139,12 @@ impl<'tcx> EdgeData<'tcx> for RegionProjectionMember<'tcx> { } } -impl<'tcx> HasPcsElems> for RegionProjectionMember<'tcx> { +impl<'tcx> HasPcsElems> for BlockEdge<'tcx> { fn pcs_elems(&mut self) -> Vec<&mut RegionProjection<'tcx>> { self.inputs.iter_mut().flat_map(|p| p.pcs_elems()).collect() } } -impl<'tcx> HasPcsElems>> - for RegionProjectionMember<'tcx> -{ +impl<'tcx> HasPcsElems>> for BlockEdge<'tcx> { fn pcs_elems(&mut self) -> Vec<&mut RegionProjection<'tcx, MaybeOldPlace<'tcx>>> { self.outputs .iter_mut() @@ -137,7 +153,7 @@ impl<'tcx> HasPcsElems>> } } -impl<'tcx> HasPcsElems> for RegionProjectionMember<'tcx> { +impl<'tcx> HasPcsElems> for BlockEdge<'tcx> { fn pcs_elems(&mut self) -> Vec<&mut MaybeOldPlace<'tcx>> { self.inputs .iter_mut() @@ -147,7 +163,7 @@ impl<'tcx> HasPcsElems> for RegionProjectionMember<'tcx> { } } -impl<'tcx> RegionProjectionMember<'tcx> { +impl<'tcx> BlockEdge<'tcx> { /// Returns `true` iff the lifetime projections are mutable pub(crate) fn mutability(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Mutability { let mut_values = self @@ -163,9 +179,9 @@ impl<'tcx> RegionProjectionMember<'tcx> { } pub(crate) fn new( - inputs: RegionProjectionMemberInputs<'tcx>, - outputs: RegionProjectionMemberOutputs<'tcx>, - kind: RegionProjectionMemberKind, + inputs: BlockEdgeInputs<'tcx>, + outputs: BlockEdgeOutputs<'tcx>, + kind: BlockEdgeKind, ) -> Self { Self { inputs, diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index 39148450..61690ccb 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -6,7 +6,7 @@ use crate::borrow_pcg::graph::borrows_imgcat_debug; use crate::borrow_pcg::path_condition::PathConditions; use crate::borrow_pcg::region_projection::RegionProjection; use crate::borrow_pcg::region_projection_member::{ - RegionProjectionMember, RegionProjectionMemberKind, + BlockEdge, BlockEdgeKind, }; use crate::borrow_pcg::state::BorrowsState; use crate::borrow_pcg::unblock_graph::UnblockGraph; @@ -124,10 +124,10 @@ impl<'tcx> BorrowsState<'tcx> { for ra in place.region_projections(repacker) { self.record_and_apply_action( BorrowPCGAction::add_region_projection_member( - RegionProjectionMember::new( + BlockEdge::new( smallvec![borrow.value.blocked_place.into()], smallvec![ra.into()], - RegionProjectionMemberKind::ContractRef, + BlockEdgeKind::ContractRef, ), PathConditions::new(location.block), "Contract To", @@ -202,10 +202,10 @@ impl<'tcx> BorrowsState<'tcx> { // e.g t|'a let base_rp = RegionProjection::new((*region).into(), base, repacker).unwrap(); - let region_projection_member = RegionProjectionMember::new( + let region_projection_member = BlockEdge::new( smallvec![base_rp.to_pcg_node(repacker)], smallvec![target.into()], - RegionProjectionMemberKind::DerefRegionProjection, + BlockEdgeKind::DerefRegionProjection, ); self.record_and_apply_action( diff --git a/src/borrow_pcg/visitor/mod.rs b/src/borrow_pcg/visitor/mod.rs index c3da57c1..7be50df2 100644 --- a/src/borrow_pcg/visitor/mod.rs +++ b/src/borrow_pcg/visitor/mod.rs @@ -3,7 +3,7 @@ use smallvec::smallvec; use tracing::instrument; use crate::{ - borrow_pcg::region_projection_member::RegionProjectionMemberKind, + borrow_pcg::region_projection_member::BlockEdgeKind, combined_pcs::{LocalNodeLike, PCGError, PCGNodeLike, PCGUnsupportedError}, rustc_interface::{ borrowck::PoloniusOutput, @@ -26,7 +26,7 @@ use super::{ coupling_graph_constructor::BorrowCheckerInterface, path_condition::PathConditions, region_projection::{PCGRegion, RegionIdx, RegionProjection}, - region_projection_member::RegionProjectionMember, + region_projection_member::BlockEdge, }; use super::{domain::AbstractionOutputTarget, engine::BorrowsEngine}; use crate::borrow_pcg::action::actions::BorrowPCGActions; @@ -148,7 +148,7 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { source_proj: RegionProjection<'tcx, MaybeOldPlace<'tcx>>, target: Place<'tcx>, location: Location, - kind: impl Fn(PCGRegion) -> RegionProjectionMemberKind, + kind: impl Fn(PCGRegion) -> BlockEdgeKind, ) { for target_proj in target.region_projections(self.repacker).into_iter() { if self.outlives( @@ -156,7 +156,7 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { target_proj.region(self.repacker), ) { self.apply_action(BorrowPCGAction::add_region_projection_member( - RegionProjectionMember::new( + BlockEdge::new( smallvec![source_proj.to_pcg_node(self.repacker)], smallvec![target_proj.to_local_node(self.repacker)], kind(target_proj.region(self.repacker)), diff --git a/src/borrow_pcg/visitor/stmt.rs b/src/borrow_pcg/visitor/stmt.rs index 86831c1a..fd35ede3 100644 --- a/src/borrow_pcg/visitor/stmt.rs +++ b/src/borrow_pcg/visitor/stmt.rs @@ -4,7 +4,7 @@ use crate::{ action::BorrowPCGAction, path_condition::PathConditions, region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, - region_projection_member::{RegionProjectionMember, RegionProjectionMemberKind}, + region_projection_member::{BlockEdge, BlockEdgeKind}, state::obtain::ObtainReason, visitor::StatementStage, }, @@ -124,7 +124,7 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { source_proj, target, location, - |_| RegionProjectionMemberKind::Todo, + |_| BlockEdgeKind::Todo, ); } } @@ -135,7 +135,7 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { target.ty(self.repacker).ty.kind() { self.apply_action(BorrowPCGAction::add_region_projection_member( - RegionProjectionMember::new( + BlockEdge::new( smallvec![RegionProjection::new( (*const_region).into(), MaybeRemoteRegionProjectionBase::Const(c.const_), @@ -150,7 +150,7 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { ) .unwrap() .into()], - RegionProjectionMemberKind::ConstRef, + BlockEdgeKind::ConstRef, ), PathConditions::AtBlock(location.block), "Assign constant", @@ -177,7 +177,7 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { source_proj.into(), target, location, - |_| RegionProjectionMemberKind::Todo, + |_| BlockEdgeKind::Todo, ); } } @@ -206,7 +206,7 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { source_proj.into(), target, location, - |region| RegionProjectionMemberKind::BorrowOutlives { + |region| BlockEdgeKind::BorrowOutlives { toplevel: region == target_region, }, ); diff --git a/src/combined_pcs/mod.rs b/src/combined_pcs/mod.rs index 67ddd0ed..25692f88 100644 --- a/src/combined_pcs/mod.rs +++ b/src/combined_pcs/mod.rs @@ -7,7 +7,9 @@ mod engine; mod domain; mod node; +mod successor; pub use engine::*; pub use domain::*; pub use node::*; +pub use successor::*; diff --git a/src/free_pcs/results/cursor.rs b/src/free_pcs/results/cursor.rs index abfc0934..df943fef 100644 --- a/src/free_pcs/results/cursor.rs +++ b/src/free_pcs/results/cursor.rs @@ -8,7 +8,7 @@ use std::rc::Rc; use crate::{ borrow_pcg::{action::BorrowPCGActionKind, latest::Latest}, - combined_pcs::{EvalStmtPhase, PCGEngine, PCGError}, + combined_pcs::{EvalStmtPhase, PCGEngine, PCGError, PcgSuccessor}, rustc_interface::{ data_structures::fx::FxHashSet, dataflow::PCGAnalysis, @@ -19,7 +19,7 @@ use crate::{ }, mir_dataflow::ResultsCursor, }, - utils::{display::DebugLines, validity::HasValidityCheck, Place}, + utils::{display::DebugLines, validity::HasValidityCheck, Place} }; use crate::borrow_pcg::action::actions::BorrowPCGActions; @@ -32,7 +32,6 @@ use crate::{ CapabilitySummary, FreePlaceCapabilitySummary, RepackOp, RepackingBridgeSemiLattice, }, utils::PlaceRepacker, - BorrowsBridge, }; pub trait HasPcg<'mir, 'tcx> { @@ -110,16 +109,12 @@ impl<'mir, 'tcx> FreePcsAnalysis<'mir, 'tcx> { e })?; - let (extra_start, extra_middle) = curr_borrows.get_bridge(); - let result = PcgLocation { location, actions: curr_borrows.actions.clone(), states: curr_fpcg.data.states.0.clone(), repacks_start: repack_ops.start, repacks_middle: repack_ops.middle, - extra_start, - extra_middle, borrows: curr_borrows.data.states.0.clone(), }; @@ -127,7 +122,7 @@ impl<'mir, 'tcx> FreePcsAnalysis<'mir, 'tcx> { Ok(Some(result)) } - pub(crate) fn terminator(&mut self) -> FreePcsTerminator<'tcx> { + pub(crate) fn terminator(&mut self) -> PcgTerminator<'tcx> { let location = self.curr_stmt.unwrap(); assert!(location == self.end_stmt.unwrap()); self.curr_stmt = None; @@ -155,20 +150,12 @@ impl<'mir, 'tcx> FreePcsAnalysis<'mir, 'tcx> { let entry_set = self.cursor.results().entry_set_for_block(succ); let to = entry_set.get_curr_fpcg(); let to_borrows_state = entry_set.get_curr_borrow_pcg(); - PcgLocation { - location: Location { - block: succ, - statement_index: 0, - }, - actions: to_borrows_state.actions.clone(), - states: to.data.states.0.clone(), - repacks_start: from_fpcg_state.data.states[EvalStmtPhase::PostMain] + PcgSuccessor::new( + succ, + from_fpcg_state.data.states[EvalStmtPhase::PostMain] .bridge(&to.data.entry_state, rp) .unwrap(), - repacks_middle: Vec::new(), - borrows: to_borrows_state.data.states.0.clone(), - // TODO: It seems like extra_start should be similar to repacks_start - extra_start: { + { let mut actions = BorrowPCGActions::new(); let self_abstraction_edges = from_borrows_state.data.states [EvalStmtPhase::PostMain] @@ -191,13 +178,12 @@ impl<'mir, 'tcx> FreePcsAnalysis<'mir, 'tcx> { ); } } - actions.into() + actions.into_vec() }, - extra_middle: BorrowsBridge::new(), - } + ) }) .collect(); - FreePcsTerminator { succs } + PcgTerminator { succs } } /// Obtains the results of the dataflow analysis for all blocks. @@ -279,7 +265,7 @@ impl<'tcx> PcgBasicBlocks<'tcx> { pub struct PcgBasicBlock<'tcx> { pub statements: Vec>, - pub terminator: FreePcsTerminator<'tcx>, + pub terminator: PcgTerminator<'tcx>, } impl<'tcx> PcgBasicBlock<'tcx> { @@ -293,11 +279,8 @@ impl<'tcx> PcgBasicBlock<'tcx> { } } for term_succ in self.terminator.succs.iter() { - for line in term_succ.debug_lines(EvalStmtPhase::PostMain, repacker) { - result.push(format!( - "Terminator({:?}): {}", - term_succ.location.block, line - )); + for line in term_succ.debug_lines(repacker) { + result.push(format!("Terminator({:?}): {}", term_succ.block(), line)); } } result @@ -314,8 +297,6 @@ pub struct PcgLocation<'tcx> { /// Repacks in the middle of the statement pub repacks_middle: Vec>, pub states: CapabilitySummaries<'tcx>, - pub extra_start: BorrowsBridge<'tcx>, - pub extra_middle: BorrowsBridge<'tcx>, pub borrows: BorrowsStates<'tcx>, pub(crate) actions: EvalStmtData>, } @@ -365,12 +346,6 @@ impl<'tcx> PcgLocation<'tcx> { result.push(action.debug_line(repacker)); } result.extend(self.borrows[phase].debug_lines(repacker)); - for line in self.extra_start.debug_lines(repacker) { - result.push(format!("Extra Start: {}", line)); - } - for line in self.extra_middle.debug_lines(repacker) { - result.push(format!("Extra Middle: {}", line)); - } for line in self.repacks_start.debug_lines(repacker) { result.push(format!("Repacks Start: {}", line)); } @@ -382,6 +357,6 @@ impl<'tcx> PcgLocation<'tcx> { } #[derive(Debug)] -pub struct FreePcsTerminator<'tcx> { - pub succs: Vec>, +pub struct PcgTerminator<'tcx> { + pub succs: Vec>, } diff --git a/src/lib.rs b/src/lib.rs index 03c071fc..8e0369ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,20 +18,14 @@ pub mod utils; pub mod visualization; use borrow_pcg::{ - action::{BorrowPCGAction, BorrowPCGActionKind}, + action::BorrowPCGAction, borrow_pcg_edge::LocalNode, - borrow_pcg_expansion::BorrowPCGExpansion, - graph::Conditioned, latest::Latest, - path_condition::PathConditions, - region_projection_member::RegionProjectionMember, - unblock_graph::BorrowPCGUnblockAction, }; -use combined_pcs::{PCGContext, PCGEngine}; +use combined_pcs::{PCGContext, PCGEngine, PcgSuccessor}; use free_pcs::{CapabilityKind, PcgLocation, RepackOp}; use rustc_interface::{ borrowck::{self, BorrowSet, RegionInferenceContext}, - data_structures::fx::FxHashSet, dataflow::{compute_fixpoint, PCGAnalysis}, middle::{mir::Body, ty::TyCtxt}, }; @@ -130,92 +124,16 @@ impl<'tcx> ToJsonWithRepacker<'tcx> for Weaken<'tcx> { } } -#[derive(Clone, Debug)] -pub struct BorrowsBridge<'tcx> { - pub(crate) actions: Vec>, -} - -impl<'tcx> DebugLines> for BorrowsBridge<'tcx> { +impl<'tcx> DebugLines> for BorrowPCGActions<'tcx> { fn debug_lines(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec { - self.actions + self.0 .iter() .map(|action| action.debug_line(repacker)) .collect() } } -impl<'tcx> BorrowsBridge<'tcx> { - /// Actions applied to the PCG, in the order they occurred. - pub fn actions(&self) -> &[BorrowPCGAction<'tcx>] { - &self.actions - } - - pub fn added_region_projection_members( - &self, - ) -> FxHashSet>> { - self.actions - .iter() - .filter_map(|action| match action.kind() { - BorrowPCGActionKind::AddRegionProjectionMember(member, path_conditions) => { - Some(Conditioned::new(member.clone(), path_conditions.clone())) - } - _ => None, - }) - .collect() - } - - pub fn weakens(&self) -> FxHashSet> { - self.actions - .iter() - .filter_map(|action| match action.kind() { - BorrowPCGActionKind::Weaken(weaken) => Some(*weaken), - _ => None, - }) - .collect() - } - - pub fn unblock_actions(&self) -> Vec> { - self.actions - .iter() - .filter_map(|action| match action.kind() { - BorrowPCGActionKind::RemoveEdge(edge) => Some(edge.clone().into()), - _ => None, - }) - .collect() - } - - pub fn expands(&self) -> FxHashSet>> { - self.actions - .iter() - .filter_map(|action| match action.kind() { - BorrowPCGActionKind::InsertBorrowPCGExpansion(expansion, location) => Some( - Conditioned::new(expansion.clone(), PathConditions::AtBlock(location.block)), - ), - _ => None, - }) - .collect() - } -} - -impl<'tcx> From> for BorrowsBridge<'tcx> { - fn from(actions: BorrowPCGActions<'tcx>) -> Self { - Self { - actions: actions.into_vec(), - } - } -} - -impl<'tcx> BorrowsBridge<'tcx> { - pub(crate) fn new() -> Self { - Self { actions: vec![] } - } - pub(crate) fn to_json(&self, repacker: PlaceRepacker<'_, 'tcx>) -> serde_json::Value { - json!({ - "actions": self.actions.iter().map(|a| a.debug_line_with_context(repacker)).collect::>(), - }) - } -} use borrow_pcg::action::actions::BorrowPCGActions; use std::sync::Mutex; @@ -237,9 +155,30 @@ struct PCGStmtVisualizationData<'a, 'tcx> { latest: &'a Latest<'tcx>, free_pcg_repacks_start: &'a Vec>, free_pcg_repacks_middle: &'a Vec>, - borrows_bridge_start: &'a BorrowsBridge<'tcx>, - borrows_bridge_middle: &'a BorrowsBridge<'tcx>, - actions: EvalStmtData>>, + borrow_actions: &'a EvalStmtData>, +} + +struct PcgSuccessorVisualizationData<'a, 'tcx> { + owned_ops: &'a [RepackOp<'tcx>], + borrow_ops: &'a [BorrowPCGAction<'tcx>], +} + +impl<'tcx, 'a> From<&'a PcgSuccessor<'tcx>> for PcgSuccessorVisualizationData<'a, 'tcx> { + fn from(successor: &'a PcgSuccessor<'tcx>) -> Self { + Self { + owned_ops: successor.owned_ops(), + borrow_ops: successor.borrow_ops(), + } + } +} + +impl<'tcx> ToJsonWithRepacker<'tcx> for PcgSuccessorVisualizationData<'_, 'tcx> { + fn to_json(&self, repacker: PlaceRepacker<'_, 'tcx>) -> serde_json::Value { + json!({ + "owned_ops": self.owned_ops.iter().map(|r| r.to_json()).collect::>(), + "borrow_ops": self.borrow_ops.iter().map(|a| a.to_json(repacker)).collect::>(), + }) + } } impl<'tcx> ToJsonWithRepacker<'tcx> for PCGStmtVisualizationData<'_, 'tcx> { @@ -248,9 +187,7 @@ impl<'tcx> ToJsonWithRepacker<'tcx> for PCGStmtVisualizationData<'_, 'tcx> { "latest": self.latest.to_json(repacker), "free_pcg_repacks_start": self.free_pcg_repacks_start.iter().map(|r| r.to_json()).collect::>(), "free_pcg_repacks_middle": self.free_pcg_repacks_middle.iter().map(|r| r.to_json()).collect::>(), - "borrows_bridge_start": self.borrows_bridge_start.to_json(repacker), - "borrows_bridge_middle": self.borrows_bridge_middle.to_json(repacker), - "actions": self.actions.to_json(repacker), + "borrow_actions": self.borrow_actions.to_json(repacker), }) } } @@ -261,9 +198,7 @@ impl<'a, 'tcx> From<&'a PcgLocation<'tcx>> for PCGStmtVisualizationData<'a, 'tcx latest: &location.borrows.post_main.latest, free_pcg_repacks_start: &location.repacks_start, free_pcg_repacks_middle: &location.repacks_middle, - borrows_bridge_start: &location.extra_start, - borrows_bridge_middle: &location.extra_middle, - actions: location.actions.clone().map(|actions| actions.into_vec()), + borrow_actions: &location.actions, } } } @@ -349,11 +284,10 @@ pub fn run_combined_pcs<'mir, 'tcx>( // Iterate over each statement in the MIR for (block, _data) in mir.body().basic_blocks.iter_enumerated() { let pcs_block_option = fpcs_analysis.get_all_for_bb(block).unwrap(); - let pcs_block = if let Some(pcs_block) = pcs_block_option { - pcs_block - } else { + if pcs_block_option.is_none() { continue; - }; + } + let pcs_block = pcs_block_option.unwrap(); for (statement_index, statement) in pcs_block.statements.iter().enumerate() { if validity_checks_enabled() { statement.assert_validity(rp); @@ -368,19 +302,18 @@ pub fn run_combined_pcs<'mir, 'tcx>( let pcg_data_json = data.to_json(rp); std::fs::write(&pcg_data_file_path, pcg_data_json.to_string()) .expect("Failed to write pcg data to JSON file"); - - for succ in pcs_block.terminator.succs.iter() { - let data = PCGStmtVisualizationData::from(succ); - let pcg_data_file_path = format!( - "{}/block_{}_term_block_{}_pcg_data.json", - &dir_path, - block.index(), - succ.location.block.index() - ); - let pcg_data_json = data.to_json(rp); - std::fs::write(&pcg_data_file_path, pcg_data_json.to_string()) - .expect("Failed to write pcg data to JSON file"); - } + } + for succ in pcs_block.terminator.succs { + let data = PcgSuccessorVisualizationData::from(&succ); + let pcg_data_file_path = format!( + "{}/block_{}_term_block_{}_pcg_data.json", + &dir_path, + block.index(), + succ.block().index() + ); + let pcg_data_json = data.to_json(rp); + std::fs::write(&pcg_data_file_path, pcg_data_json.to_string()) + .expect("Failed to write pcg data to JSON file"); } } } diff --git a/src/visualization/graph_constructor.rs b/src/visualization/graph_constructor.rs index 664517ca..3d84d092 100644 --- a/src/visualization/graph_constructor.rs +++ b/src/visualization/graph_constructor.rs @@ -355,13 +355,11 @@ trait PlaceGrapher<'mir, 'tcx: 'mir> { let base_node = self.insert_local_node(deref_expansion.base()); for place in deref_expansion.expansion(self.repacker()) { let place = self.insert_local_node(place); - self.constructor() - .edges - .insert(GraphEdge::DerefExpansion { - source: base_node, - target: place, - path_conditions: format!("{}", edge.conditions()), - }); + self.constructor().edges.insert(GraphEdge::DerefExpansion { + source: base_node, + target: place, + path_conditions: format!("{}", edge.conditions()), + }); } } BorrowPCGEdgeKind::Borrow(reborrow) => { @@ -374,8 +372,6 @@ trait PlaceGrapher<'mir, 'tcx: 'mir> { assigned_region_projection, capabilities.get(assigned_rp_pcg_node), ); - let deref_place = reborrow.deref_place(self.repacker()); - let deref_place_node = self.insert_maybe_old_place(deref_place); self.constructor().edges.insert(GraphEdge::Borrow { borrowed_place, assigned_region_projection: assigned_rp_node, @@ -383,25 +379,21 @@ trait PlaceGrapher<'mir, 'tcx: 'mir> { region: format!("{:?}", reborrow.region), path_conditions: format!("{}", edge.conditions()), }); - self.constructor().edges.insert(GraphEdge::Alias { - blocked_place: borrowed_place, - blocking_place: deref_place_node, - }); } BorrowPCGEdgeKind::Abstraction(abstraction) => { self.constructor().insert_abstraction(abstraction); } - BorrowPCGEdgeKind::RegionProjectionMember(member) => { + BorrowPCGEdgeKind::Block(member) => { for input in member.inputs.iter() { let input_node = self.insert_pcg_node(*input); for output in member.outputs.iter() { let output_node = self.insert_local_node(*output); self.constructor() .edges - .insert(GraphEdge::RegionProjectionMember { + .insert(GraphEdge::Block { source: input_node, target: output_node, - kind: format!("{:?}", member.kind), + kind: format!("{}", member.kind), }); } } diff --git a/src/visualization/mod.rs b/src/visualization/mod.rs index e0403503..8a3b7cb7 100644 --- a/src/visualization/mod.rs +++ b/src/visualization/mod.rs @@ -137,6 +137,7 @@ enum NodeType { }, } +#[allow(dead_code)] #[derive(Clone, Debug, PartialEq, Eq, Hash)] enum GraphEdge { Abstract { @@ -164,7 +165,7 @@ enum GraphEdge { target: NodeId, path_conditions: String, }, - RegionProjectionMember { + Block { source: NodeId, target: NodeId, kind: String, @@ -223,7 +224,7 @@ impl GraphEdge { to: blocking.to_string(), options: EdgeOptions::directed(EdgeDirection::Forward), }, - GraphEdge::RegionProjectionMember { + GraphEdge::Block { source, target, kind, diff --git a/visualization/src/components/BorrowsAndActions.tsx b/visualization/src/components/BorrowsAndActions.tsx index 3debb5b3..6eed83cd 100644 --- a/visualization/src/components/BorrowsAndActions.tsx +++ b/visualization/src/components/BorrowsAndActions.tsx @@ -5,7 +5,7 @@ import { BorrowAction, Reborrow, MaybeOldPlace, - BorrowsBridge, + BorrowPCGActions, PlaceExpand, PCGNode, MaybeRemotePlace, @@ -117,10 +117,10 @@ function LocalPCGNodeDisplay({ node }: { node: LocalNode }) { return ; } -function BorrowsBridgeDisplay({ bridge }: { bridge: BorrowsBridge }) { +function BorrowsBridgeDisplay({ actions }: { actions: BorrowPCGActions }) { return (
    - {bridge.actions.map((action, index) => ( + {actions.map((action, index) => (
  • {action}
  • ))}
@@ -143,13 +143,17 @@ export default function PCGOps({ data }: { data: PCGStmtVisualizationData }) { }} >

Borrow PCG Bridge (Start)

- - {data.borrows_bridge_middle && ( - <> -

Borrow PCG Bridge (Mid)

- - - )} + {[ + "pre_operands" as const, + "post_operands" as const, + "pre_main" as const, + "post_main" as const, + ].map((key) => ( +
+
{key}
+ +
+ ))}

Repacks (Start)

    {data.free_pcg_repacks_start.map((repack, index) => ( diff --git a/visualization/src/types.ts b/visualization/src/types.ts index ae161eb9..f00c3d7b 100644 --- a/visualization/src/types.ts +++ b/visualization/src/types.ts @@ -109,9 +109,7 @@ export type BorrowPCGUnblockAction = { edge: BorrowPCGEdge; }; -export type BorrowsBridge = { - actions: string[]; -}; +export type BorrowPCGActions = string[]; export type PathData = { heap: Record; @@ -122,6 +120,12 @@ export type PCGStmtVisualizationData = { latest: Record; free_pcg_repacks_start: string[]; free_pcg_repacks_middle: string[]; - borrows_bridge_start: BorrowsBridge; - borrows_bridge_middle: BorrowsBridge; + borrow_actions: EvalStmtData; +}; + +type EvalStmtData = { + pre_operands: T; + post_operands: T; + pre_main: T; + post_main: T; }; From e808ec4d05930220e3e0e119bcab9ed0052ad898 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Thu, 27 Feb 2025 19:38:00 -0800 Subject: [PATCH 02/21] WIP --- src/combined_pcs/successor.rs | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/combined_pcs/successor.rs diff --git a/src/combined_pcs/successor.rs b/src/combined_pcs/successor.rs new file mode 100644 index 00000000..5d349099 --- /dev/null +++ b/src/combined_pcs/successor.rs @@ -0,0 +1,57 @@ +use serde_json::json; + +use crate::rustc_interface::middle::mir::BasicBlock; +use crate::utils::json::ToJsonWithRepacker; +use crate::utils::PlaceRepacker; +use crate::DebugLines; +use crate::{borrow_pcg::action::BorrowPCGAction, free_pcs::RepackOp}; + +#[derive(Debug)] +pub struct PcgSuccessor<'tcx> { + block: BasicBlock, + owned_ops: Vec>, + borrow_ops: Vec>, +} + +impl<'tcx> PcgSuccessor<'tcx> { + pub fn block(&self) -> BasicBlock { + self.block + } + pub fn owned_ops(&self) -> &[RepackOp<'tcx>] { + &self.owned_ops + } + pub fn borrow_ops(&self) -> &[BorrowPCGAction<'tcx>] { + &self.borrow_ops + } + pub(crate) fn new( + block: BasicBlock, + owned_ops: Vec>, + borrow_ops: Vec>, + ) -> Self { + Self { + block, + owned_ops, + borrow_ops, + } + } +} + +impl<'tcx> ToJsonWithRepacker<'tcx> for PcgSuccessor<'tcx> { + fn to_json(&self, repacker: PlaceRepacker<'_, 'tcx>) -> serde_json::Value { + json!({ + "block": self.block().index(), + "owned_ops": self.owned_ops().iter().map(|r| r.to_json()).collect::>(), + "borrow_ops": self.borrow_ops().iter().map(|a| a.to_json(repacker)).collect::>(), + }) + } +} + +impl<'tcx> DebugLines> for PcgSuccessor<'tcx> { + fn debug_lines(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec { + let mut result = Vec::new(); + result.push(format!("Block: {}", self.block().index())); + result.extend(self.owned_ops().iter().map(|r| format!("{:?}", r))); + result.extend(self.borrow_ops().iter().map(|a| a.debug_line(repacker))); + result + } +} From f4f417b71e13ee3177b552976d10efe5ba4c4272 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Thu, 27 Feb 2025 20:20:38 -0800 Subject: [PATCH 03/21] WIP --- .github/workflows/test.yml | 25 ++++++++++++++++++++++++- src/borrow_pcg/action/actions.rs | 4 ---- src/combined_pcs/successor.rs | 16 ++++++++++++---- src/free_pcs/results/cursor.rs | 15 ++++++++++----- src/lib.rs | 5 ++--- 5 files changed, 48 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ac3bb4f6..af36661e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -87,9 +87,32 @@ jobs: - name: Run unit tests run: cargo test --doc + flowistry_tests: + runs-on: ubuntu-latest + steps: + - name: Checkout pcs repository + uses: actions/checkout@v4 + with: + path: pcs + + - name: Checkout flowistry repository + uses: actions/checkout@v4 + with: + repository: zgrannan/flowistry + ref: zgrannan/pcg + path: flowistry + + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2024-09-15 + + - name: Run flowistry tests + working-directory: flowistry/crates/flowistry + run: cargo test + build-docker-image: runs-on: ubuntu-latest - needs: [check_warnings, clippy, test_files, bench_test_files, test_crates, unit_tests, doc_tests] + needs: [check_warnings, clippy, test_files, bench_test_files, test_crates, unit_tests, doc_tests, flowistry_tests] permissions: contents: read packages: write diff --git a/src/borrow_pcg/action/actions.rs b/src/borrow_pcg/action/actions.rs index 10b46044..a6bc8d2d 100644 --- a/src/borrow_pcg/action/actions.rs +++ b/src/borrow_pcg/action/actions.rs @@ -83,10 +83,6 @@ impl<'tcx> BorrowPCGActions<'tcx> { self.0.iter() } - pub(crate) fn into_vec(self) -> Vec> { - self.0 - } - pub(crate) fn new() -> Self { Self(vec![]) } diff --git a/src/combined_pcs/successor.rs b/src/combined_pcs/successor.rs index 5d349099..30a5ffab 100644 --- a/src/combined_pcs/successor.rs +++ b/src/combined_pcs/successor.rs @@ -1,16 +1,19 @@ use serde_json::json; +use crate::borrow_pcg::action::actions::BorrowPCGActions; +use crate::borrow_pcg::latest::Latest; +use crate::free_pcs::RepackOp; use crate::rustc_interface::middle::mir::BasicBlock; use crate::utils::json::ToJsonWithRepacker; use crate::utils::PlaceRepacker; use crate::DebugLines; -use crate::{borrow_pcg::action::BorrowPCGAction, free_pcs::RepackOp}; #[derive(Debug)] pub struct PcgSuccessor<'tcx> { block: BasicBlock, owned_ops: Vec>, - borrow_ops: Vec>, + borrow_ops: BorrowPCGActions<'tcx>, + latest: Latest<'tcx>, } impl<'tcx> PcgSuccessor<'tcx> { @@ -20,18 +23,23 @@ impl<'tcx> PcgSuccessor<'tcx> { pub fn owned_ops(&self) -> &[RepackOp<'tcx>] { &self.owned_ops } - pub fn borrow_ops(&self) -> &[BorrowPCGAction<'tcx>] { + pub fn borrow_ops(&self) -> &BorrowPCGActions<'tcx> { &self.borrow_ops } + pub fn latest(&self) -> &Latest<'tcx> { + &self.latest + } pub(crate) fn new( block: BasicBlock, owned_ops: Vec>, - borrow_ops: Vec>, + borrow_ops: BorrowPCGActions<'tcx>, + latest: Latest<'tcx>, ) -> Self { Self { block, owned_ops, borrow_ops, + latest, } } } diff --git a/src/free_pcs/results/cursor.rs b/src/free_pcs/results/cursor.rs index df943fef..c2186b44 100644 --- a/src/free_pcs/results/cursor.rs +++ b/src/free_pcs/results/cursor.rs @@ -19,7 +19,7 @@ use crate::{ }, mir_dataflow::ResultsCursor, }, - utils::{display::DebugLines, validity::HasValidityCheck, Place} + utils::{display::DebugLines, validity::HasValidityCheck, Place}, }; use crate::borrow_pcg::action::actions::BorrowPCGActions; @@ -111,7 +111,7 @@ impl<'mir, 'tcx> FreePcsAnalysis<'mir, 'tcx> { let result = PcgLocation { location, - actions: curr_borrows.actions.clone(), + borrow_pcg_actions: curr_borrows.actions.clone(), states: curr_fpcg.data.states.0.clone(), repacks_start: repack_ops.start, repacks_middle: repack_ops.middle, @@ -178,8 +178,9 @@ impl<'mir, 'tcx> FreePcsAnalysis<'mir, 'tcx> { ); } } - actions.into_vec() + actions }, + to_borrows_state.data.entry_state.latest.clone(), ) }) .collect(); @@ -298,7 +299,7 @@ pub struct PcgLocation<'tcx> { pub repacks_middle: Vec>, pub states: CapabilitySummaries<'tcx>, pub borrows: BorrowsStates<'tcx>, - pub(crate) actions: EvalStmtData>, + pub(crate) borrow_pcg_actions: EvalStmtData>, } impl<'tcx> DebugLines> for Vec> { @@ -314,6 +315,10 @@ impl<'tcx> HasValidityCheck<'tcx> for PcgLocation<'tcx> { } impl<'tcx> PcgLocation<'tcx> { + pub fn borrow_pcg_actions(&self, phase: EvalStmtPhase) -> &BorrowPCGActions<'tcx> { + &self.borrow_pcg_actions[phase] + } + pub fn aliases<'mir>( &self, place: impl Into>, @@ -342,7 +347,7 @@ impl<'tcx> PcgLocation<'tcx> { repacker: PlaceRepacker<'_, 'tcx>, ) -> Vec { let mut result = self.states[phase].debug_lines(repacker); - for action in self.actions[phase].iter() { + for action in self.borrow_pcg_actions[phase].iter() { result.push(action.debug_line(repacker)); } result.extend(self.borrows[phase].debug_lines(repacker)); diff --git a/src/lib.rs b/src/lib.rs index 8e0369ab..6a8990e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,6 @@ pub mod utils; pub mod visualization; use borrow_pcg::{ - action::BorrowPCGAction, borrow_pcg_edge::LocalNode, latest::Latest, }; @@ -160,7 +159,7 @@ struct PCGStmtVisualizationData<'a, 'tcx> { struct PcgSuccessorVisualizationData<'a, 'tcx> { owned_ops: &'a [RepackOp<'tcx>], - borrow_ops: &'a [BorrowPCGAction<'tcx>], + borrow_ops: &'a BorrowPCGActions<'tcx>, } impl<'tcx, 'a> From<&'a PcgSuccessor<'tcx>> for PcgSuccessorVisualizationData<'a, 'tcx> { @@ -198,7 +197,7 @@ impl<'a, 'tcx> From<&'a PcgLocation<'tcx>> for PCGStmtVisualizationData<'a, 'tcx latest: &location.borrows.post_main.latest, free_pcg_repacks_start: &location.repacks_start, free_pcg_repacks_middle: &location.repacks_middle, - borrow_actions: &location.actions, + borrow_actions: &location.borrow_pcg_actions, } } } From c569eb265cb3dcf2d6a62ffbe4fd3fd5b0caa72c Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Thu, 27 Feb 2025 20:23:18 -0800 Subject: [PATCH 04/21] Fix clippy --- src/borrow_pcg/region_projection_member.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/borrow_pcg/region_projection_member.rs b/src/borrow_pcg/region_projection_member.rs index 78791b5d..ba59fc1e 100644 --- a/src/borrow_pcg/region_projection_member.rs +++ b/src/borrow_pcg/region_projection_member.rs @@ -92,7 +92,7 @@ pub enum BlockEdgeKind { Todo, } -impl<'tcx> std::fmt::Display for BlockEdgeKind { +impl std::fmt::Display for BlockEdgeKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { BlockEdgeKind::Aggregate { From cfd3d8ec7d89624c05c827bbc2c5291969284748 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Thu, 27 Feb 2025 20:25:05 -0800 Subject: [PATCH 05/21] fix project deeper --- src/utils/place/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/place/mod.rs b/src/utils/place/mod.rs index b17de56b..e6adaea0 100644 --- a/src/utils/place/mod.rs +++ b/src/utils/place/mod.rs @@ -189,7 +189,7 @@ impl<'tcx> Place<'tcx> { /// extract from the ADT type the expected type of the projection and /// replace the type. /// - /// Returns `None` if the projection would be illegal + /// Returns an error if the projection would be illegal /// /// ``` fn project_deeper( @@ -201,7 +201,8 @@ impl<'tcx> Place<'tcx> { if matches!( elem, ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } - ) && base_ty.ty.builtin_index().is_none() + ) && !matches!(base_ty.ty.kind(), TyKind::Alias(..)) + && base_ty.ty.builtin_index().is_none() { return Err(PlaceProjectionError(format!( "Type is not indexable: {:?}", From 9476b5b7c2cbfdcd15901b6c54b3cdb37ef276e9 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Thu, 27 Feb 2025 20:28:41 -0800 Subject: [PATCH 06/21] Fix test --- test-files/37_demo_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-files/37_demo_loop.rs b/test-files/37_demo_loop.rs index 3a72b989..bcf78e21 100644 --- a/test-files/37_demo_loop.rs +++ b/test-files/37_demo_loop.rs @@ -8,7 +8,7 @@ impl List { let mut i = 0; let mut current = self; while i < n { -// PCG: Terminator(bb1): Extra Start: Add Abstraction Edge: Loop(bb1): [Remote(_1)] -> [current↓'?13]; path conditions: bb1 +// PCG: Terminator(bb1): Add Abstraction Edge: Loop(bb1): [Remote(_1)] -> [current↓'?13]; path conditions: bb1 // ~PCG: bb1[0] pre_operands: (*_12) at After(bb7[2]) -> current↓'?13 under conditions bb7 -> bb8,bb8 -> bb1, // PCG: bb1[0] pre_operands: Loop(bb1): [Remote(_1)] -> [current↓'?13] under conditions bb1 // PCG: bb1[0] pre_operands: Loop(bb1): [Remote(_1)↓'?11] -> [current↓'?13] under conditions bb1 From 0561a67193d81903e27daf8e77ad60e46b4c23c8 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Thu, 27 Feb 2025 20:46:48 -0800 Subject: [PATCH 07/21] move edge --- src/borrow_pcg/action/actions.rs | 2 +- src/borrow_pcg/action/mod.rs | 2 +- src/borrow_pcg/borrow_pcg_edge.rs | 2 +- src/borrow_pcg/domain.rs | 2 +- src/borrow_pcg/edge/kind.rs | 2 +- src/borrow_pcg/edge/mod.rs | 3 ++- src/borrow_pcg/{ => edge}/region_projection_member.rs | 6 +++--- src/borrow_pcg/graph/aliases.rs | 5 ++--- src/borrow_pcg/graph/mod.rs | 2 +- src/borrow_pcg/mod.rs | 1 - src/borrow_pcg/state/obtain.rs | 2 +- src/borrow_pcg/visitor/mod.rs | 4 ++-- src/borrow_pcg/visitor/stmt.rs | 2 +- 13 files changed, 17 insertions(+), 18 deletions(-) rename src/borrow_pcg/{ => edge}/region_projection_member.rs (97%) diff --git a/src/borrow_pcg/action/actions.rs b/src/borrow_pcg/action/actions.rs index a6bc8d2d..303dd0e2 100644 --- a/src/borrow_pcg/action/actions.rs +++ b/src/borrow_pcg/action/actions.rs @@ -2,7 +2,7 @@ use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_pcg_expansion::BorrowPCGExpansion; use crate::borrow_pcg::graph::Conditioned; use crate::borrow_pcg::path_condition::PathConditions; -use crate::borrow_pcg::region_projection_member::BlockEdge; +use crate::borrow_pcg::edge::region_projection_member::BlockEdge; use crate::borrow_pcg::unblock_graph::BorrowPCGUnblockAction; use crate::utils::json::ToJsonWithRepacker; use crate::utils::PlaceRepacker; diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index 9d9069d3..8c51fe7f 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -3,7 +3,7 @@ use tracing::instrument; use super::borrow_pcg_edge::{BorrowPCGEdge, LocalNode, ToBorrowsEdge}; use super::borrow_pcg_expansion::BorrowPCGExpansion; use super::path_condition::PathConditions; -use super::region_projection_member::BlockEdge; +use super::edge::region_projection_member::BlockEdge; use super::state::BorrowsState; use crate::borrow_pcg::edge::abstraction::AbstractionType; use crate::combined_pcs::{PCGNode, PCGNodeLike}; diff --git a/src/borrow_pcg/borrow_pcg_edge.rs b/src/borrow_pcg/borrow_pcg_edge.rs index 2f96e8c7..6647dc20 100644 --- a/src/borrow_pcg/borrow_pcg_edge.rs +++ b/src/borrow_pcg/borrow_pcg_edge.rs @@ -12,7 +12,7 @@ use super::{ has_pcs_elem::HasPcsElems, path_condition::{PathCondition, PathConditions}, region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, - region_projection_member::BlockEdge, + edge::region_projection_member::BlockEdge, }; use crate::borrow_pcg::edge::abstraction::AbstractionType; use crate::borrow_pcg::edge::borrow::BorrowEdge; diff --git a/src/borrow_pcg/domain.rs b/src/borrow_pcg/domain.rs index ecf5e66d..4e3cd8e3 100644 --- a/src/borrow_pcg/domain.rs +++ b/src/borrow_pcg/domain.rs @@ -4,7 +4,7 @@ use crate::borrow_pcg::action::actions::BorrowPCGActions; use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_checker::r#impl::BorrowCheckerImpl; use crate::borrow_pcg::path_condition::{PathCondition, PathConditions}; -use crate::borrow_pcg::region_projection_member::{ +use crate::borrow_pcg::edge::region_projection_member::{ BlockEdge, BlockEdgeKind, }; use crate::borrow_pcg::state::BorrowsState; diff --git a/src/borrow_pcg/edge/kind.rs b/src/borrow_pcg/edge/kind.rs index 6fac5eb9..b412a95f 100644 --- a/src/borrow_pcg/edge/kind.rs +++ b/src/borrow_pcg/edge/kind.rs @@ -3,7 +3,7 @@ use crate::borrow_pcg::edge::abstraction::AbstractionType; use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::borrow_pcg::has_pcs_elem::HasPcsElems; use crate::borrow_pcg::region_projection::RegionProjection; -use crate::borrow_pcg::region_projection_member::BlockEdge; +use crate::borrow_pcg::edge::region_projection_member::BlockEdge; use crate::utils::display::DisplayWithRepacker; use crate::utils::PlaceRepacker; use crate::utils::validity::HasValidityCheck; diff --git a/src/borrow_pcg/edge/mod.rs b/src/borrow_pcg/edge/mod.rs index 3d2bbbf1..3e07ba48 100644 --- a/src/borrow_pcg/edge/mod.rs +++ b/src/borrow_pcg/edge/mod.rs @@ -1,3 +1,4 @@ pub mod borrow; pub mod abstraction; -pub mod kind; \ No newline at end of file +pub mod kind; +pub mod region_projection_member; \ No newline at end of file diff --git a/src/borrow_pcg/region_projection_member.rs b/src/borrow_pcg/edge/region_projection_member.rs similarity index 97% rename from src/borrow_pcg/region_projection_member.rs rename to src/borrow_pcg/edge/region_projection_member.rs index ba59fc1e..c6e6a83e 100644 --- a/src/borrow_pcg/region_projection_member.rs +++ b/src/borrow_pcg/edge/region_projection_member.rs @@ -9,9 +9,9 @@ use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::validity::HasValidityCheck; use crate::utils::PlaceRepacker; -use super::borrow_pcg_edge::{BlockedNode, LocalNode}; -use super::edge_data::EdgeData; -use super::{has_pcs_elem::HasPcsElems, region_projection::RegionProjection}; +use crate::borrow_pcg::borrow_pcg_edge::{BlockedNode, LocalNode}; +use crate::borrow_pcg::edge_data::EdgeData; +use crate::borrow_pcg::{has_pcs_elem::HasPcsElems, region_projection::RegionProjection}; use crate::utils::json::ToJsonWithRepacker; pub(crate) type BlockEdgeInputs<'tcx> = SmallVec<[PCGNode<'tcx>; 8]>; diff --git a/src/borrow_pcg/graph/aliases.rs b/src/borrow_pcg/graph/aliases.rs index 6a43008b..c5eafea0 100644 --- a/src/borrow_pcg/graph/aliases.rs +++ b/src/borrow_pcg/graph/aliases.rs @@ -1,7 +1,7 @@ use crate::{ borrow_pcg::{ borrow_pcg_edge::LocalNode, edge::kind::BorrowPCGEdgeKind, region_projection::RegionIdx, - region_projection_member::BlockEdgeKind, + edge::region_projection_member::BlockEdgeKind, }, combined_pcs::{PCGNode, PCGNodeLike}, rustc_interface::data_structures::fx::FxHashSet, @@ -172,8 +172,7 @@ impl<'tcx> BorrowsGraph<'tcx> { } } BlockEdgeKind::DerefBorrowOutlives => {} - BlockEdgeKind::BorrowOutlives { toplevel } - if !toplevel || direct => {} + BlockEdgeKind::BorrowOutlives { toplevel } if !toplevel || direct => {} _ => { for input in region_projection_member.inputs() { extend(input.to_pcg_node(repacker), seen, &mut result, false); diff --git a/src/borrow_pcg/graph/mod.rs b/src/borrow_pcg/graph/mod.rs index 6b024e8f..7f42790c 100644 --- a/src/borrow_pcg/graph/mod.rs +++ b/src/borrow_pcg/graph/mod.rs @@ -32,7 +32,7 @@ use super::{ latest::Latest, path_condition::{PathCondition, PathConditions}, region_projection::RegionProjection, - region_projection_member::{ + edge::region_projection_member::{ BlockEdge, BlockEdgeKind, BlockEdgeOutputs, }, }; diff --git a/src/borrow_pcg/mod.rs b/src/borrow_pcg/mod.rs index 36958d45..0ecd4825 100644 --- a/src/borrow_pcg/mod.rs +++ b/src/borrow_pcg/mod.rs @@ -15,5 +15,4 @@ pub mod has_pcs_elem; pub mod latest; pub mod path_condition; pub mod region_projection; -pub mod region_projection_member; pub mod unblock_graph; diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index 61690ccb..b8c3b0fc 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -5,7 +5,7 @@ use crate::borrow_pcg::borrow_pcg_expansion::{BorrowExpansion, BorrowPCGExpansio use crate::borrow_pcg::graph::borrows_imgcat_debug; use crate::borrow_pcg::path_condition::PathConditions; use crate::borrow_pcg::region_projection::RegionProjection; -use crate::borrow_pcg::region_projection_member::{ +use crate::borrow_pcg::edge::region_projection_member::{ BlockEdge, BlockEdgeKind, }; use crate::borrow_pcg::state::BorrowsState; diff --git a/src/borrow_pcg/visitor/mod.rs b/src/borrow_pcg/visitor/mod.rs index 7be50df2..d9dd36fa 100644 --- a/src/borrow_pcg/visitor/mod.rs +++ b/src/borrow_pcg/visitor/mod.rs @@ -3,7 +3,7 @@ use smallvec::smallvec; use tracing::instrument; use crate::{ - borrow_pcg::region_projection_member::BlockEdgeKind, + borrow_pcg::edge::region_projection_member::BlockEdgeKind, combined_pcs::{LocalNodeLike, PCGError, PCGNodeLike, PCGUnsupportedError}, rustc_interface::{ borrowck::PoloniusOutput, @@ -26,7 +26,7 @@ use super::{ coupling_graph_constructor::BorrowCheckerInterface, path_condition::PathConditions, region_projection::{PCGRegion, RegionIdx, RegionProjection}, - region_projection_member::BlockEdge, + edge::region_projection_member::BlockEdge, }; use super::{domain::AbstractionOutputTarget, engine::BorrowsEngine}; use crate::borrow_pcg::action::actions::BorrowPCGActions; diff --git a/src/borrow_pcg/visitor/stmt.rs b/src/borrow_pcg/visitor/stmt.rs index fd35ede3..1117b55c 100644 --- a/src/borrow_pcg/visitor/stmt.rs +++ b/src/borrow_pcg/visitor/stmt.rs @@ -4,7 +4,7 @@ use crate::{ action::BorrowPCGAction, path_condition::PathConditions, region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, - region_projection_member::{BlockEdge, BlockEdgeKind}, + edge::region_projection_member::{BlockEdge, BlockEdgeKind}, state::obtain::ObtainReason, visitor::StatementStage, }, From 2a2cb30fa9783a8a163c701e394d5a140f4671e2 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Thu, 27 Feb 2025 20:48:17 -0800 Subject: [PATCH 08/21] rename --- src/borrow_pcg/action/actions.rs | 2 +- src/borrow_pcg/action/mod.rs | 2 +- src/borrow_pcg/borrow_pcg_edge.rs | 2 +- src/borrow_pcg/domain.rs | 2 +- src/borrow_pcg/edge/{region_projection_member.rs => block.rs} | 0 src/borrow_pcg/edge/kind.rs | 2 +- src/borrow_pcg/edge/mod.rs | 2 +- src/borrow_pcg/graph/aliases.rs | 2 +- src/borrow_pcg/graph/mod.rs | 2 +- src/borrow_pcg/state/obtain.rs | 2 +- src/borrow_pcg/visitor/mod.rs | 4 ++-- src/borrow_pcg/visitor/stmt.rs | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) rename src/borrow_pcg/edge/{region_projection_member.rs => block.rs} (100%) diff --git a/src/borrow_pcg/action/actions.rs b/src/borrow_pcg/action/actions.rs index 303dd0e2..d2fcee15 100644 --- a/src/borrow_pcg/action/actions.rs +++ b/src/borrow_pcg/action/actions.rs @@ -2,7 +2,7 @@ use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_pcg_expansion::BorrowPCGExpansion; use crate::borrow_pcg::graph::Conditioned; use crate::borrow_pcg::path_condition::PathConditions; -use crate::borrow_pcg::edge::region_projection_member::BlockEdge; +use crate::borrow_pcg::edge::block::BlockEdge; use crate::borrow_pcg::unblock_graph::BorrowPCGUnblockAction; use crate::utils::json::ToJsonWithRepacker; use crate::utils::PlaceRepacker; diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index 8c51fe7f..3f6663e2 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -3,7 +3,7 @@ use tracing::instrument; use super::borrow_pcg_edge::{BorrowPCGEdge, LocalNode, ToBorrowsEdge}; use super::borrow_pcg_expansion::BorrowPCGExpansion; use super::path_condition::PathConditions; -use super::edge::region_projection_member::BlockEdge; +use super::edge::block::BlockEdge; use super::state::BorrowsState; use crate::borrow_pcg::edge::abstraction::AbstractionType; use crate::combined_pcs::{PCGNode, PCGNodeLike}; diff --git a/src/borrow_pcg/borrow_pcg_edge.rs b/src/borrow_pcg/borrow_pcg_edge.rs index 6647dc20..abf0ef10 100644 --- a/src/borrow_pcg/borrow_pcg_edge.rs +++ b/src/borrow_pcg/borrow_pcg_edge.rs @@ -12,7 +12,7 @@ use super::{ has_pcs_elem::HasPcsElems, path_condition::{PathCondition, PathConditions}, region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, - edge::region_projection_member::BlockEdge, + edge::block::BlockEdge, }; use crate::borrow_pcg::edge::abstraction::AbstractionType; use crate::borrow_pcg::edge::borrow::BorrowEdge; diff --git a/src/borrow_pcg/domain.rs b/src/borrow_pcg/domain.rs index 4e3cd8e3..aea5290f 100644 --- a/src/borrow_pcg/domain.rs +++ b/src/borrow_pcg/domain.rs @@ -4,7 +4,7 @@ use crate::borrow_pcg::action::actions::BorrowPCGActions; use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_checker::r#impl::BorrowCheckerImpl; use crate::borrow_pcg::path_condition::{PathCondition, PathConditions}; -use crate::borrow_pcg::edge::region_projection_member::{ +use crate::borrow_pcg::edge::block::{ BlockEdge, BlockEdgeKind, }; use crate::borrow_pcg::state::BorrowsState; diff --git a/src/borrow_pcg/edge/region_projection_member.rs b/src/borrow_pcg/edge/block.rs similarity index 100% rename from src/borrow_pcg/edge/region_projection_member.rs rename to src/borrow_pcg/edge/block.rs diff --git a/src/borrow_pcg/edge/kind.rs b/src/borrow_pcg/edge/kind.rs index b412a95f..c6251392 100644 --- a/src/borrow_pcg/edge/kind.rs +++ b/src/borrow_pcg/edge/kind.rs @@ -3,7 +3,7 @@ use crate::borrow_pcg::edge::abstraction::AbstractionType; use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::borrow_pcg::has_pcs_elem::HasPcsElems; use crate::borrow_pcg::region_projection::RegionProjection; -use crate::borrow_pcg::edge::region_projection_member::BlockEdge; +use crate::borrow_pcg::edge::block::BlockEdge; use crate::utils::display::DisplayWithRepacker; use crate::utils::PlaceRepacker; use crate::utils::validity::HasValidityCheck; diff --git a/src/borrow_pcg/edge/mod.rs b/src/borrow_pcg/edge/mod.rs index 3e07ba48..d0cfe833 100644 --- a/src/borrow_pcg/edge/mod.rs +++ b/src/borrow_pcg/edge/mod.rs @@ -1,4 +1,4 @@ pub mod borrow; pub mod abstraction; pub mod kind; -pub mod region_projection_member; \ No newline at end of file +pub mod block; \ No newline at end of file diff --git a/src/borrow_pcg/graph/aliases.rs b/src/borrow_pcg/graph/aliases.rs index c5eafea0..c7439826 100644 --- a/src/borrow_pcg/graph/aliases.rs +++ b/src/borrow_pcg/graph/aliases.rs @@ -1,7 +1,7 @@ use crate::{ borrow_pcg::{ borrow_pcg_edge::LocalNode, edge::kind::BorrowPCGEdgeKind, region_projection::RegionIdx, - edge::region_projection_member::BlockEdgeKind, + edge::block::BlockEdgeKind, }, combined_pcs::{PCGNode, PCGNodeLike}, rustc_interface::data_structures::fx::FxHashSet, diff --git a/src/borrow_pcg/graph/mod.rs b/src/borrow_pcg/graph/mod.rs index 7f42790c..a9e089c7 100644 --- a/src/borrow_pcg/graph/mod.rs +++ b/src/borrow_pcg/graph/mod.rs @@ -32,7 +32,7 @@ use super::{ latest::Latest, path_condition::{PathCondition, PathConditions}, region_projection::RegionProjection, - edge::region_projection_member::{ + edge::block::{ BlockEdge, BlockEdgeKind, BlockEdgeOutputs, }, }; diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index b8c3b0fc..ada7eefc 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -5,7 +5,7 @@ use crate::borrow_pcg::borrow_pcg_expansion::{BorrowExpansion, BorrowPCGExpansio use crate::borrow_pcg::graph::borrows_imgcat_debug; use crate::borrow_pcg::path_condition::PathConditions; use crate::borrow_pcg::region_projection::RegionProjection; -use crate::borrow_pcg::edge::region_projection_member::{ +use crate::borrow_pcg::edge::block::{ BlockEdge, BlockEdgeKind, }; use crate::borrow_pcg::state::BorrowsState; diff --git a/src/borrow_pcg/visitor/mod.rs b/src/borrow_pcg/visitor/mod.rs index d9dd36fa..9ea7245e 100644 --- a/src/borrow_pcg/visitor/mod.rs +++ b/src/borrow_pcg/visitor/mod.rs @@ -3,7 +3,7 @@ use smallvec::smallvec; use tracing::instrument; use crate::{ - borrow_pcg::edge::region_projection_member::BlockEdgeKind, + borrow_pcg::edge::block::BlockEdgeKind, combined_pcs::{LocalNodeLike, PCGError, PCGNodeLike, PCGUnsupportedError}, rustc_interface::{ borrowck::PoloniusOutput, @@ -26,7 +26,7 @@ use super::{ coupling_graph_constructor::BorrowCheckerInterface, path_condition::PathConditions, region_projection::{PCGRegion, RegionIdx, RegionProjection}, - edge::region_projection_member::BlockEdge, + edge::block::BlockEdge, }; use super::{domain::AbstractionOutputTarget, engine::BorrowsEngine}; use crate::borrow_pcg::action::actions::BorrowPCGActions; diff --git a/src/borrow_pcg/visitor/stmt.rs b/src/borrow_pcg/visitor/stmt.rs index 1117b55c..80f05bff 100644 --- a/src/borrow_pcg/visitor/stmt.rs +++ b/src/borrow_pcg/visitor/stmt.rs @@ -4,7 +4,7 @@ use crate::{ action::BorrowPCGAction, path_condition::PathConditions, region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, - edge::region_projection_member::{BlockEdge, BlockEdgeKind}, + edge::block::{BlockEdge, BlockEdgeKind}, state::obtain::ObtainReason, visitor::StatementStage, }, From c32b5a96f84c3d326494b5c291a5ee79c4fe0247 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Fri, 28 Feb 2025 14:11:02 -0800 Subject: [PATCH 09/21] fix a small bug --- src/borrow_pcg/action/mod.rs | 13 +++-- src/borrow_pcg/borrow_pcg_edge.rs | 6 +-- src/borrow_pcg/borrow_pcg_expansion.rs | 37 +++++++++------ src/borrow_pcg/domain.rs | 14 +++--- src/borrow_pcg/graph/aliases.rs | 2 +- src/borrow_pcg/region_projection.rs | 6 +-- src/borrow_pcg/state/mod.rs | 25 +++++----- src/borrow_pcg/state/obtain.rs | 20 ++++---- src/borrow_pcg/visitor/mod.rs | 21 ++++++-- src/borrow_pcg/visitor/stmt.rs | 12 ++++- src/combined_pcs/domain.rs | 18 ++++--- src/free_pcs/impl/local.rs | 2 +- src/free_pcs/results/cursor.rs | 3 ++ src/lib.rs | 6 ++- src/main.rs | 2 +- src/utils/place/maybe_old.rs | 11 +++-- src/utils/place/mod.rs | 66 ++++++++++++++++---------- src/visualization/graph_constructor.rs | 14 +++--- src/visualization/mir_graph.rs | 18 +++---- tests/selected_crates.rs | 1 + 20 files changed, 178 insertions(+), 119 deletions(-) diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index 3f6663e2..4e5bb140 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -2,11 +2,11 @@ use tracing::instrument; use super::borrow_pcg_edge::{BorrowPCGEdge, LocalNode, ToBorrowsEdge}; use super::borrow_pcg_expansion::BorrowPCGExpansion; -use super::path_condition::PathConditions; use super::edge::block::BlockEdge; +use super::path_condition::PathConditions; use super::state::BorrowsState; use crate::borrow_pcg::edge::abstraction::AbstractionType; -use crate::combined_pcs::{PCGNode, PCGNodeLike}; +use crate::combined_pcs::{PCGError, PCGNode, PCGNodeLike}; use crate::free_pcs::CapabilityKind; use crate::rustc_interface::{ast::Mutability, middle::mir::Location}; use crate::utils::display::{DebugLines, DisplayWithRepacker}; @@ -215,12 +215,11 @@ impl<'tcx> ToJsonWithRepacker<'tcx> for BorrowPCGAction<'tcx> { impl<'tcx> BorrowsState<'tcx> { #[instrument(skip(self, repacker), fields(action))] - #[must_use] pub(crate) fn apply_action( &mut self, action: BorrowPCGAction<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> bool { + ) -> Result { let result = match action.kind { BorrowPCGActionKind::AddAbstractionEdge(abstraction, pc) => { let mut changed = false; @@ -297,7 +296,7 @@ impl<'tcx> BorrowsState<'tcx> { capability } else { // Presumably already expanded in another branch - return true; + return Ok(true); } } }; @@ -321,7 +320,7 @@ impl<'tcx> BorrowsState<'tcx> { } } - for p in expansion.expansion(repacker).iter() { + for p in expansion.expansion(repacker)?.iter() { _ = self.set_capability((*p).into(), expanded_capability, repacker); } } @@ -331,7 +330,7 @@ impl<'tcx> BorrowsState<'tcx> { self.change_pcs_elem(old, new, repacker) } }; - result + Ok(result) } /// Adds a region projection member to the graph and sets appropriate diff --git a/src/borrow_pcg/borrow_pcg_edge.rs b/src/borrow_pcg/borrow_pcg_edge.rs index abf0ef10..e4fbf721 100644 --- a/src/borrow_pcg/borrow_pcg_edge.rs +++ b/src/borrow_pcg/borrow_pcg_edge.rs @@ -14,7 +14,7 @@ use super::{ region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, edge::block::BlockEdge, }; -use crate::borrow_pcg::edge::abstraction::AbstractionType; +use crate::{borrow_pcg::edge::abstraction::AbstractionType, combined_pcs::PCGError}; use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::borrow_pcg::edge::kind::BorrowPCGEdgeKind; use crate::utils::place::maybe_old::MaybeOldPlace; @@ -199,8 +199,8 @@ impl<'tcx> HasPlace<'tcx> for LocalNode<'tcx> { &self, elem: mir::PlaceElem<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Option { - Some(match self { + ) -> Result { + Ok(match self { LocalNode::Place(p) => LocalNode::Place(p.project_deeper(elem, repacker)?), LocalNode::RegionProjection(rp) => { LocalNode::RegionProjection(rp.project_deeper(elem, repacker)?) diff --git a/src/borrow_pcg/borrow_pcg_expansion.rs b/src/borrow_pcg/borrow_pcg_expansion.rs index 04840367..656c864c 100644 --- a/src/borrow_pcg/borrow_pcg_expansion.rs +++ b/src/borrow_pcg/borrow_pcg_expansion.rs @@ -10,8 +10,8 @@ use super::{ region_projection::RegionProjection, }; use crate::utils::json::ToJsonWithRepacker; -use crate::utils::place::corrected::CorrectedPlace; use crate::utils::place::maybe_old::MaybeOldPlace; +use crate::{combined_pcs::PCGError, utils::place::corrected::CorrectedPlace}; use crate::{ combined_pcs::{PCGNode, PCGNodeLike}, edgedata_enum, @@ -178,12 +178,15 @@ impl ExpansionOfBorrowed<'_, P> { } impl<'tcx, P: PCGNodeLike<'tcx> + HasPlace<'tcx>> ExpansionOfBorrowed<'tcx, P> { - pub fn expansion<'slf>(&'slf self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec

    { + pub fn expansion<'slf>( + &'slf self, + repacker: PlaceRepacker<'_, 'tcx>, + ) -> Result, PCGError> { self.expansion .elems() .into_iter() - .map(move |p| self.base.project_deeper(p, repacker).unwrap()) - .collect() + .map(move |p| self.base.project_deeper(p, repacker)) + .collect::, _>>() } } @@ -216,9 +219,7 @@ impl<'tcx> EdgeData<'tcx> for ExpansionOfBorrowed<'tcx> { &self, repacker: PlaceRepacker<'_, 'tcx>, ) -> FxHashSet> { - self.expansion(repacker) - .into_iter() - .collect() + self.expansion(repacker).unwrap().into_iter().collect() } fn is_owned_expansion(&self) -> bool { @@ -340,6 +341,7 @@ impl<'tcx, T: PCGNodeLike<'tcx> + From> + HasPlace<'tcx>> "{{{}}} -> {{{}}}", self.base().to_short_string(repacker), self.expansion(repacker) + .unwrap() .iter() .map(|p| p.to_short_string(repacker)) .collect::>() @@ -413,19 +415,19 @@ where impl<'tcx, P: HasPlace<'tcx> + From> + PCGNodeLike<'tcx>> BorrowPCGExpansion<'tcx, P> { - pub fn expansion(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec

    { + pub fn expansion(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Result, PCGError> { match self { - BorrowPCGExpansion::FromOwned(owned) => vec![owned.expansion(repacker).into()], + BorrowPCGExpansion::FromOwned(owned) => Ok(vec![owned.expansion(repacker).into()]), BorrowPCGExpansion::FromBorrow(e) => e.expansion(repacker), } } pub fn blocked_by_nodes(&self, repacker: PlaceRepacker<'_, 'tcx>) -> FxHashSet

    { - self.expansion(repacker).into_iter().collect() + self.expansion(repacker).unwrap().into_iter().collect() } } -impl<'tcx, P: HasPlace<'tcx> + std::fmt::Debug + Copy + Into>> +impl<'tcx, P: PCGNodeLike<'tcx> + HasPlace<'tcx> + Into>> BorrowPCGExpansion<'tcx, P> { pub(crate) fn is_deref_of_borrow(&self, repacker: PlaceRepacker<'_, 'tcx>) -> bool { @@ -446,7 +448,7 @@ impl<'tcx, P: HasPlace<'tcx> + std::fmt::Debug + Copy + Into> base: P, expansion: BorrowExpansion<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Self { + ) -> Result { if let LocalNode::Place(p) = base.into() && p.is_owned(repacker) { @@ -456,7 +458,7 @@ impl<'tcx, P: HasPlace<'tcx> + std::fmt::Debug + Copy + Into> base, expansion ); - BorrowPCGExpansion::FromOwned(ExpansionOfOwned::new(p)) + Ok(BorrowPCGExpansion::FromOwned(ExpansionOfOwned::new(p))) } else { BorrowPCGExpansion::from_borrowed_base(base, expansion, repacker) } @@ -466,11 +468,16 @@ impl<'tcx, P: HasPlace<'tcx> + std::fmt::Debug + Copy + Into> base: P, expansion: BorrowExpansion<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Self { + ) -> Result { if let LocalNode::Place(p) = base.into() { assert!(!p.is_owned(repacker)); } - BorrowPCGExpansion::FromBorrow(ExpansionOfBorrowed { base, expansion }) + let borrow_expansion = ExpansionOfBorrowed { base, expansion }; + if let Err(e) = borrow_expansion.expansion(repacker) { + Err(e) + } else { + Ok(BorrowPCGExpansion::FromBorrow(borrow_expansion)) + } } } diff --git a/src/borrow_pcg/domain.rs b/src/borrow_pcg/domain.rs index aea5290f..34b16616 100644 --- a/src/borrow_pcg/domain.rs +++ b/src/borrow_pcg/domain.rs @@ -3,19 +3,17 @@ use std::rc::Rc; use crate::borrow_pcg::action::actions::BorrowPCGActions; use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_checker::r#impl::BorrowCheckerImpl; +use crate::borrow_pcg::edge::block::{BlockEdge, BlockEdgeKind}; use crate::borrow_pcg::path_condition::{PathCondition, PathConditions}; -use crate::borrow_pcg::edge::block::{ - BlockEdge, BlockEdgeKind, -}; use crate::borrow_pcg::state::BorrowsState; use crate::combined_pcs::EvalStmtPhase::*; use crate::combined_pcs::{PCGError, PCGErrorKind, PCGNodeLike}; use crate::free_pcs::CapabilityKind; +use crate::utils; use crate::utils::domain_data::DomainData; use crate::utils::eval_stmt_data::EvalStmtData; use crate::utils::maybe_remote::MaybeRemotePlace; use crate::utils::{Place, PlaceRepacker}; -use crate::utils; use smallvec::smallvec; pub type AbstractionInputTarget<'tcx> = CGNode<'tcx>; @@ -136,12 +134,16 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { #[allow(unused)] pub(crate) fn has_internal_error(&self) -> bool { self.error - .as_ref().is_some_and(|e| matches!(e.kind, PCGErrorKind::Internal(_))) + .as_ref() + .is_some_and(|e| matches!(e.kind, PCGErrorKind::Internal(_))) } pub(crate) fn has_error(&self) -> bool { self.error.is_some() } + pub(crate) fn error(&self) -> Option<&PCGError> { + self.error.as_ref() + } pub(crate) fn new( repacker: PlaceRepacker<'mir, 'tcx>, @@ -215,7 +217,7 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { "Initialize Local", ), self.repacker, - )); + ).unwrap()); } } diff --git a/src/borrow_pcg/graph/aliases.rs b/src/borrow_pcg/graph/aliases.rs index c7439826..b6c0304a 100644 --- a/src/borrow_pcg/graph/aliases.rs +++ b/src/borrow_pcg/graph/aliases.rs @@ -70,7 +70,7 @@ impl<'tcx> BorrowsGraph<'tcx> { } else { continue; }; - let local_node = if let Some(n) = local_node.project_deeper(proj, repacker) { + let local_node = if let Ok(n) = local_node.project_deeper(proj, repacker) { n } else { continue; diff --git a/src/borrow_pcg/region_projection.rs b/src/borrow_pcg/region_projection.rs index ef33d661..f9006b6f 100644 --- a/src/borrow_pcg/region_projection.rs +++ b/src/borrow_pcg/region_projection.rs @@ -8,7 +8,7 @@ use super::has_pcs_elem::HasPcsElems; use super::{ borrow_pcg_edge::LocalNode, coupling_graph_constructor::CGNode, visitor::extract_regions, }; -use crate::combined_pcs::PCGInternalError; +use crate::combined_pcs::{PCGError, PCGInternalError}; use crate::utils::json::ToJsonWithRepacker; use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::place::maybe_remote::MaybeRemotePlace; @@ -331,7 +331,7 @@ impl<'tcx, T: RegionProjectionBaseLike<'tcx> + HasPlace<'tcx>> HasPlace<'tcx> &self, elem: PlaceElem<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Option + ) -> Result where Self: Clone + HasValidityCheck<'tcx>, { @@ -340,7 +340,7 @@ impl<'tcx, T: RegionProjectionBaseLike<'tcx> + HasPlace<'tcx>> HasPlace<'tcx> self.base.project_deeper(elem, repacker)?, repacker, ) - .ok() + .map_err(|e| e.into()) } fn iter_projections(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec<(Self, PlaceElem<'tcx>)> { diff --git a/src/borrow_pcg/state/mod.rs b/src/borrow_pcg/state/mod.rs index 63813b38..4053e389 100644 --- a/src/borrow_pcg/state/mod.rs +++ b/src/borrow_pcg/state/mod.rs @@ -11,10 +11,10 @@ use super::{ latest::Latest, path_condition::{PathCondition, PathConditions}, }; -use crate::borrow_pcg::action::executed_actions::ExecutedActions; use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::place::maybe_remote::MaybeRemotePlace; +use crate::{borrow_pcg::action::executed_actions::ExecutedActions, combined_pcs::PCGError}; use crate::{ borrow_pcg::edge_data::EdgeData, combined_pcs::{PCGNode, PCGNodeLike}, @@ -135,11 +135,12 @@ impl<'tcx> BorrowsState<'tcx> { action: BorrowPCGAction<'tcx>, actions: &mut ExecutedActions<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) { - let changed = self.apply_action(action.clone(), repacker); + ) -> Result<(), PCGError> { + let changed = self.apply_action(action.clone(), repacker)?; if changed { actions.record(action); } + Ok(()) } pub(crate) fn contains>>( @@ -252,14 +253,13 @@ impl<'tcx> BorrowsState<'tcx> { Some(cap) } - #[must_use] pub(super) fn remove_edge_and_set_latest( &mut self, edge: impl BorrowPCGEdgeLike<'tcx>, location: Location, repacker: PlaceRepacker<'_, 'tcx>, context: &str, - ) -> ExecutedActions<'tcx> { + ) -> Result, PCGError> { let mut actions = ExecutedActions::new(); if !edge.is_shared_borrow() { for place in edge.blocked_places(repacker) { @@ -269,14 +269,14 @@ impl<'tcx> BorrowsState<'tcx> { BorrowPCGAction::set_latest(place, location, context), &mut actions, repacker, - ); + )?; } } } } let remove_edge_action = BorrowPCGAction::remove_edge(edge.clone().to_owned_edge(), context); - self.record_and_apply_action(remove_edge_action, &mut actions, repacker); + self.record_and_apply_action(remove_edge_action, &mut actions, repacker)?; let fg = self.graph().frozen_graph(); let to_restore = edge @@ -301,12 +301,12 @@ impl<'tcx> BorrowsState<'tcx> { BorrowPCGAction::restore_capability(local_node, restore_cap), &mut actions, repacker, - ); + )?; } } } } - actions + Ok(actions) } pub(crate) fn add_path_condition(&mut self, pc: PathCondition) -> bool { @@ -363,7 +363,6 @@ impl<'tcx> BorrowsState<'tcx> { self.latest.get(place) } - #[must_use] /// Removes leaves that are old or dead (based on the borrow checker). Note /// that the liveness calculation is performed based on what happened at the /// end of the *previous* statement. @@ -387,7 +386,7 @@ impl<'tcx> BorrowsState<'tcx> { repacker: PlaceRepacker<'mir, 'tcx>, location: Location, bc: &impl BorrowCheckerInterface<'tcx>, - ) -> ExecutedActions<'tcx> { + ) -> Result, PCGError> { let mut actions = ExecutedActions::new(); let prev_location = if location.statement_index == 0 { None @@ -442,7 +441,7 @@ impl<'tcx> BorrowsState<'tcx> { } let edges_to_trim = go(self, repacker, prev_location, bc); if edges_to_trim.is_empty() { - break actions; + break Ok(actions); } for edge in edges_to_trim { actions.extend(self.remove_edge_and_set_latest( @@ -450,7 +449,7 @@ impl<'tcx> BorrowsState<'tcx> { location, repacker, "Trim Old Leaves", - )); + )?); } let new_num_edges = self.graph.num_edges(); assert!(new_num_edges < num_edges_prev); diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index ada7eefc..d9a6ab86 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -2,12 +2,10 @@ use crate::borrow_pcg::action::executed_actions::ExecutedActions; use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_pcg_edge::BorrowPCGEdge; use crate::borrow_pcg::borrow_pcg_expansion::{BorrowExpansion, BorrowPCGExpansion}; +use crate::borrow_pcg::edge::block::{BlockEdge, BlockEdgeKind}; use crate::borrow_pcg::graph::borrows_imgcat_debug; use crate::borrow_pcg::path_condition::PathConditions; use crate::borrow_pcg::region_projection::RegionProjection; -use crate::borrow_pcg::edge::block::{ - BlockEdge, BlockEdgeKind, -}; use crate::borrow_pcg::state::BorrowsState; use crate::borrow_pcg::unblock_graph::UnblockGraph; use crate::combined_pcs::{PCGError, PCGNodeLike}; @@ -120,7 +118,7 @@ impl<'tcx> BorrowsState<'tcx> { location, repacker, &format!("Contract To {:?}", place), - )); + )?); for ra in place.region_projections(repacker) { self.record_and_apply_action( BorrowPCGAction::add_region_projection_member( @@ -134,7 +132,7 @@ impl<'tcx> BorrowsState<'tcx> { ), &mut actions, repacker, - ); + )?; } } } @@ -171,7 +169,7 @@ impl<'tcx> BorrowsState<'tcx> { location, repacker, &format!("Contract To {:?}", place), - )); + )?); } Ok(actions) @@ -216,7 +214,7 @@ impl<'tcx> BorrowsState<'tcx> { ), &mut actions, repacker, - ); + )?; } // We don't introduce an expansion if the place is owned, because @@ -226,14 +224,14 @@ impl<'tcx> BorrowsState<'tcx> { base.into(), BorrowExpansion::from_places(expansion.clone(), repacker), repacker, - ); + )?; let action = BorrowPCGAction::insert_borrow_pcg_expansion( expansion, location, "Ensure Deref Expansion (Place)", ); - self.record_and_apply_action(action, &mut actions, repacker); + self.record_and_apply_action(action, &mut actions, repacker)?; } for rp in base.region_projections(repacker) { @@ -251,7 +249,7 @@ impl<'tcx> BorrowsState<'tcx> { rp.into(), BorrowExpansion::from_places(dest_places, repacker), repacker, - ); + )?; self.record_and_apply_action( BorrowPCGAction::insert_borrow_pcg_expansion( expansion, @@ -260,7 +258,7 @@ impl<'tcx> BorrowsState<'tcx> { ), &mut actions, repacker, - ); + )?; } } } diff --git a/src/borrow_pcg/visitor/mod.rs b/src/borrow_pcg/visitor/mod.rs index 9ea7245e..0f72876b 100644 --- a/src/borrow_pcg/visitor/mod.rs +++ b/src/borrow_pcg/visitor/mod.rs @@ -24,9 +24,9 @@ use crate::{ use super::{ action::{BorrowPCGAction, BorrowPCGActionKind}, coupling_graph_constructor::BorrowCheckerInterface, + edge::block::BlockEdge, path_condition::PathConditions, region_projection::{PCGRegion, RegionIdx, RegionProjection}, - edge::block::BlockEdge, }; use super::{domain::AbstractionOutputTarget, engine::BorrowsEngine}; use crate::borrow_pcg::action::actions::BorrowPCGActions; @@ -105,6 +105,7 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { self.domain .post_state_mut() .apply_action(action, self.repacker) + .unwrap() } pub(super) fn preparing( @@ -353,7 +354,14 @@ impl<'tcx> Visitor<'tcx> for BorrowsVisitor<'tcx, '_, '_> { let post_state = self.domain.data.states.get_mut(PostMain); let actions = post_state.pack_old_and_dead_leaves(self.repacker, location, &self.domain.bc); - self.record_actions(actions); + match actions { + Ok(actions) => { + self.record_actions(actions); + } + Err(e) => { + self.domain.report_error(e); + } + } } self.super_terminator(terminator, location); if self.stage == StatementStage::Main && !self.preparing { @@ -392,7 +400,14 @@ impl<'tcx> Visitor<'tcx> for BorrowsVisitor<'tcx, '_, '_> { .states .get_mut(PostMain) .pack_old_and_dead_leaves(self.repacker, location, &self.domain.bc); - self.record_actions(actions); + match actions { + Ok(actions) => { + self.record_actions(actions); + } + Err(e) => { + self.domain.report_error(e); + } + } } self.super_statement(statement, location); diff --git a/src/borrow_pcg/visitor/stmt.rs b/src/borrow_pcg/visitor/stmt.rs index 80f05bff..84895408 100644 --- a/src/borrow_pcg/visitor/stmt.rs +++ b/src/borrow_pcg/visitor/stmt.rs @@ -2,9 +2,9 @@ use super::BorrowsVisitor; use crate::{ borrow_pcg::{ action::BorrowPCGAction, + edge::block::{BlockEdge, BlockEdgeKind}, path_condition::PathConditions, region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, - edge::block::{BlockEdge, BlockEdgeKind}, state::obtain::ObtainReason, visitor::StatementStage, }, @@ -29,7 +29,14 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { .data .get_mut(EvalStmtPhase::PostMain) .pack_old_and_dead_leaves(self.repacker, location, &self.domain.bc); - self.record_actions(actions); + match actions { + Ok(actions) => { + self.record_actions(actions); + } + Err(e) => { + self.domain.report_error(e); + } + } } StatementKind::Assign(box (target, _)) => { let target: utils::Place<'tcx> = (*target).into(); @@ -183,6 +190,7 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { } Rvalue::Ref(region, kind, blocked_place) => { let blocked_place: utils::Place<'tcx> = (*blocked_place).into(); + let blocked_place = blocked_place.with_inherent_region(self.repacker); if !target.ty(self.repacker).ty.is_ref() { self.domain.report_error(PCGError::unsupported( PCGUnsupportedError::AssignBorrowToNonReferenceType, diff --git a/src/combined_pcs/domain.rs b/src/combined_pcs/domain.rs index cad4f38c..de024782 100644 --- a/src/combined_pcs/domain.rs +++ b/src/combined_pcs/domain.rs @@ -22,9 +22,9 @@ use crate::{ }; use super::{PCGContext, PCGEngine}; +use crate::borrow_pcg::borrow_checker::r#impl::BorrowCheckerImpl; use crate::borrow_pcg::domain::BorrowsDomain; use crate::{free_pcs::FreePlaceCapabilitySummary, visualization::generate_dot_graph}; -use crate::borrow_pcg::borrow_checker::r#impl::BorrowCheckerImpl; #[derive(Copy, Clone)] pub struct DataflowIterationDebugInfo { @@ -184,6 +184,12 @@ pub struct PCGError { pub(crate) context: Vec, } +impl From for PCGError { + fn from(e: PCGUnsupportedError) -> Self { + Self::new(PCGErrorKind::Unsupported(e), vec![]) + } +} + impl PCGError { pub(crate) fn new(kind: PCGErrorKind, context: Vec) -> Self { Self { kind, context } @@ -234,15 +240,15 @@ impl From for PCGError { #[derive(Debug, Clone, PartialEq, Eq)] pub enum PCGUnsupportedError { AssignBorrowToNonReferenceType, - ClosuresCapturingBorrows, CastToRef, - UnsafePtrCast, - DerefUnsafePtr, - InlineAssembly, + ClosuresCapturingBorrows, Coroutines, - TwoPhaseBorrow, + DerefUnsafePtr, ExpansionOfAliasType, + InlineAssembly, Other(String), + TwoPhaseBorrow, + UnsafePtrCast, } #[derive(Clone, PartialEq, Eq)] diff --git a/src/free_pcs/impl/local.rs b/src/free_pcs/impl/local.rs index 031f1ffe..07240047 100644 --- a/src/free_pcs/impl/local.rs +++ b/src/free_pcs/impl/local.rs @@ -158,7 +158,7 @@ impl<'tcx> CapabilityProjections<'tcx> { ) -> std::result::Result>, PCGError> { assert!( !from.is_mut_ref(repacker.body(), repacker.tcx()), - "Mutable reference {:?} should be expanded in reborrowing dag, not PCS", + "Mutable reference {:?} should be expanded in borrow PCG, not owned PCG", from ); pcg_validity_assert!(!self.contains_key(&to)); diff --git a/src/free_pcs/results/cursor.rs b/src/free_pcs/results/cursor.rs index c2186b44..bedaa79e 100644 --- a/src/free_pcs/results/cursor.rs +++ b/src/free_pcs/results/cursor.rs @@ -104,6 +104,9 @@ impl<'mir, 'tcx> FreePcsAnalysis<'mir, 'tcx> { let prev_post_main = state.get_curr_fpcg().data.entry_state.clone(); let curr_fpcg = state.get_curr_fpcg(); let curr_borrows = state.get_curr_borrow_pcg(); + if let Some(e) = curr_borrows.error() { + return Err(e.clone()); + } let repack_ops = curr_fpcg.repack_ops(&prev_post_main).map_err(|mut e| { e.add_context(format!("At {:?}", location)); e diff --git a/src/lib.rs b/src/lib.rs index 6a8990e6..118ddaea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,7 +282,11 @@ pub fn run_combined_pcs<'mir, 'tcx>( // Iterate over each statement in the MIR for (block, _data) in mir.body().basic_blocks.iter_enumerated() { - let pcs_block_option = fpcs_analysis.get_all_for_bb(block).unwrap(); + let pcs_block_option = if let Ok(opt) = fpcs_analysis.get_all_for_bb(block) { + opt + } else { + continue + }; if pcs_block_option.is_none() { continue; } diff --git a/src/main.rs b/src/main.rs index 43be945f..1ac988a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -144,7 +144,7 @@ fn run_pcg_on_all_fns(tcx: TyCtxt<'_>) { if emit_pcg_annotations || check_pcg_annotations { let mut debug_lines = Vec::new(); for (idx, _) in body.body.basic_blocks.iter_enumerated() { - if let Some(block) = output.get_all_for_bb(idx).unwrap() { + if let Ok(Some(block)) = output.get_all_for_bb(idx) { debug_lines .extend(block.debug_lines(PlaceRepacker::new(&body.body, tcx))); } diff --git a/src/utils/place/maybe_old.rs b/src/utils/place/maybe_old.rs index e2467654..0802dc29 100644 --- a/src/utils/place/maybe_old.rs +++ b/src/utils/place/maybe_old.rs @@ -6,7 +6,7 @@ use crate::borrow_pcg::region_projection::{ RegionProjectionBaseLike, }; use crate::borrow_pcg::visitor::extract_regions; -use crate::combined_pcs::{LocalNodeLike, PCGNode, PCGNodeLike}; +use crate::combined_pcs::{LocalNodeLike, PCGError, PCGNode, PCGNodeLike}; use crate::rustc_interface::ast::Mutability; use crate::rustc_interface::index::IndexVec; use crate::rustc_interface::middle::mir; @@ -154,10 +154,13 @@ impl<'tcx> HasPlace<'tcx> for MaybeOldPlace<'tcx> { &self, elem: PlaceElem<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Option { + ) -> Result { let mut cloned = *self; - *cloned.place_mut() = self.place().project_deeper(elem, repacker).ok()?; - Some(cloned) + *cloned.place_mut() = self + .place() + .project_deeper(elem, repacker) + .map_err(PCGError::unsupported)?; + Ok(cloned) } fn iter_projections(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec<(Self, PlaceElem<'tcx>)> { diff --git a/src/utils/place/mod.rs b/src/utils/place/mod.rs index e6adaea0..39c6756f 100644 --- a/src/utils/place/mod.rs +++ b/src/utils/place/mod.rs @@ -11,6 +11,7 @@ use std::{ mem::discriminant, }; +use crate::combined_pcs::{PCGError, PCGUnsupportedError}; use derive_more::{Deref, DerefMut}; use crate::rustc_interface::{ @@ -27,7 +28,11 @@ use crate::rustc_interface::{ #[cfg(feature = "debug_info")] use super::debug_info::DebugInfo; -use super::{display::DisplayWithRepacker, validity::HasValidityCheck, PlaceRepacker}; +use super::{ + display::DisplayWithRepacker, + validity::{validity_checks_enabled, HasValidityCheck}, + PlaceRepacker, +}; use crate::utils::json::ToJsonWithRepacker; use crate::{ borrow_pcg::{ @@ -138,7 +143,7 @@ pub trait HasPlace<'tcx>: Sized { &self, elem: PlaceElem<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Option; + ) -> std::result::Result; fn iter_projections(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec<(Self, PlaceElem<'tcx>)>; } @@ -155,8 +160,8 @@ impl<'tcx> HasPlace<'tcx> for Place<'tcx> { &self, elem: PlaceElem<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Option { - self.project_deeper(elem, repacker).ok() + ) -> std::result::Result { + self.project_deeper(elem, repacker).map_err(|e| e.into()) } fn iter_projections(&self, _repacker: PlaceRepacker<'_, 'tcx>) -> Vec<(Self, PlaceElem<'tcx>)> { @@ -196,19 +201,9 @@ impl<'tcx> Place<'tcx> { &self, elem: PlaceElem<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> std::result::Result { + ) -> std::result::Result { let base_ty = self.ty(repacker); - if matches!( - elem, - ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } - ) && !matches!(base_ty.ty.kind(), TyKind::Alias(..)) - && base_ty.ty.builtin_index().is_none() - { - return Err(PlaceProjectionError(format!( - "Type is not indexable: {:?}", - base_ty - ))); - } + let corrected_elem = if let ProjectionElem::Field(field_idx, proj_ty) = elem { let expected_ty = match base_ty.ty.kind() { ty::TyKind::Adt(def, substs) => { @@ -225,10 +220,20 @@ impl<'tcx> Place<'tcx> { } else { elem }; - Ok(self + if matches!(corrected_elem, ProjectionElem::Index(..)) + && matches!(base_ty.ty.kind(), ty::TyKind::Alias(..)) + { + return Err(PCGUnsupportedError::ExpansionOfAliasType); + } + let place: Place<'tcx> = self .0 .project_deeper(&[corrected_elem], repacker.tcx()) - .into()) + .into(); + if validity_checks_enabled() { + // Sanity check: this function will panic if the type is invalid + let _ = place.ty(repacker); + } + Ok(place) } pub(crate) fn compare_projections( @@ -294,15 +299,24 @@ impl<'tcx> Place<'tcx> { /// from its local, and using types derived from the root place as the types associated /// with Field region projections. For more details see [`Place::project_deeper`]. pub fn with_inherent_region(self, repacker: PlaceRepacker<'_, 'tcx>) -> Self { - let mut proj_iter = self.iter_projections(repacker).into_iter(); - let mut place = if let Some((place, elem)) = proj_iter.next() { - place.project_deeper(elem, repacker).unwrap() - } else { - return self; - }; - for (_, elem) in proj_iter { - place = place.project_deeper(elem, repacker).unwrap(); + let mut proj_iter = self.iter_projections(repacker).into_iter().peekable(); + let mut place: Place<'tcx> = self.local.into(); + while let Some((_, elem)) = proj_iter.next() { + if let Ok(next_place) = place.project_deeper(elem, repacker) { + place = next_place; + } else { + // It might be that we cannot re-project the place. In particular this could happen if + // there are further projections from an alias type (we may not normalize alias types because + // doing so would erase regions). In this case we try to get the original place projected at this point + if let Some((next_place, _)) = proj_iter.peek() { + place = *next_place + } else { + // This is the last projection, so we just return the original place + return self; + } + } } + place } diff --git a/src/visualization/graph_constructor.rs b/src/visualization/graph_constructor.rs index 3d84d092..27b262e3 100644 --- a/src/visualization/graph_constructor.rs +++ b/src/visualization/graph_constructor.rs @@ -353,7 +353,7 @@ trait PlaceGrapher<'mir, 'tcx: 'mir> { match edge.kind() { BorrowPCGEdgeKind::BorrowPCGExpansion(deref_expansion) => { let base_node = self.insert_local_node(deref_expansion.base()); - for place in deref_expansion.expansion(self.repacker()) { + for place in deref_expansion.expansion(self.repacker()).unwrap() { let place = self.insert_local_node(place); self.constructor().edges.insert(GraphEdge::DerefExpansion { source: base_node, @@ -388,13 +388,11 @@ trait PlaceGrapher<'mir, 'tcx: 'mir> { let input_node = self.insert_pcg_node(*input); for output in member.outputs.iter() { let output_node = self.insert_local_node(*output); - self.constructor() - .edges - .insert(GraphEdge::Block { - source: input_node, - target: output_node, - kind: format!("{}", member.kind), - }); + self.constructor().edges.insert(GraphEdge::Block { + source: input_node, + target: output_node, + kind: format!("{}", member.kind), + }); } } for (i, &input1) in member.inputs.iter().enumerate() { diff --git a/src/visualization/mir_graph.rs b/src/visualization/mir_graph.rs index b6fd3066..30103c79 100644 --- a/src/visualization/mir_graph.rs +++ b/src/visualization/mir_graph.rs @@ -44,9 +44,9 @@ fn format_bin_op(op: &BinOp) -> String { BinOp::AddUnchecked => todo!(), BinOp::SubUnchecked => todo!(), BinOp::MulUnchecked => todo!(), - BinOp::BitXor => todo!(), + BinOp::BitXor => "^".to_string(), BinOp::BitAnd => "&".to_string(), - BinOp::BitOr => todo!(), + BinOp::BitOr => "|".to_string(), BinOp::Shl => "<<".to_string(), BinOp::ShlUnchecked => "<<".to_string(), BinOp::Shr => ">>".to_string(), @@ -173,9 +173,13 @@ fn format_stmt<'tcx>(stmt: &Statement<'tcx>, repacker: PlaceRepacker<'_, 'tcx>) format!("FakeRead({})", format_place(place, repacker)) } mir::StatementKind::SetDiscriminant { - place: _, - variant_index: _, - } => todo!(), + place, + variant_index, + } => format!( + "SetDiscriminant({} {:?})", + format_place(place, repacker), + variant_index + ), mir::StatementKind::Deinit(_) => todo!(), mir::StatementKind::StorageLive(local) => { format!("StorageLive({})", format_local(local, repacker)) @@ -187,9 +191,7 @@ fn format_stmt<'tcx>(stmt: &Statement<'tcx>, repacker: PlaceRepacker<'_, 'tcx>) mir::StatementKind::PlaceMention(place) => { format!("PlaceMention({})", format_place(place, repacker)) } - mir::StatementKind::AscribeUserType(_, _) => { - "AscribeUserType(...)".to_string() - } + mir::StatementKind::AscribeUserType(_, _) => "AscribeUserType(...)".to_string(), _ => todo!(), } } diff --git a/tests/selected_crates.rs b/tests/selected_crates.rs index f3927d7d..640d46b2 100644 --- a/tests/selected_crates.rs +++ b/tests/selected_crates.rs @@ -5,6 +5,7 @@ fn test_selected_crates() { // Create tmp directory if it doesn't exist std::fs::create_dir_all("tmp").unwrap(); + // common::run_on_crate("crc", "3.2.1", true); // common::run_on_crate("serde_with", "3.12.0", true); // common::run_on_crate("tonic", "0.12.3", true); // common::run_on_crate("http", "1.2.0", true); From 95d424306935b8a146946aaab1866c082b9239d5 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Fri, 28 Feb 2025 14:43:26 -0800 Subject: [PATCH 10/21] Fix some bugs --- src/borrow_pcg/action/mod.rs | 2 +- src/borrow_pcg/domain.rs | 4 +- src/borrow_pcg/state/mod.rs | 2 +- src/borrow_pcg/state/obtain.rs | 4 +- src/borrow_pcg/unblock_graph.rs | 14 ---- src/borrow_pcg/visitor/mod.rs | 2 +- src/borrow_pcg/visitor/stmt.rs | 2 +- src/combined_pcs/domain.rs | 1 + src/utils/place/mod.rs | 59 +++++++---------- src/visualization/graph_constructor.rs | 77 ++++++--------------- src/visualization/mod.rs | 19 +----- test-files/80_crc_bytewise.rs | 92 ++++++++++++++++++++++++++ 12 files changed, 148 insertions(+), 130 deletions(-) create mode 100644 test-files/80_crc_bytewise.rs diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index 4e5bb140..5a156f1a 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -113,7 +113,7 @@ impl<'tcx> BorrowPCGAction<'tcx> { } } - pub(super) fn add_region_projection_member( + pub(super) fn add_block_edge( member: BlockEdge<'tcx>, pc: PathConditions, context: impl Into, diff --git a/src/borrow_pcg/domain.rs b/src/borrow_pcg/domain.rs index 34b16616..024b7421 100644 --- a/src/borrow_pcg/domain.rs +++ b/src/borrow_pcg/domain.rs @@ -176,7 +176,7 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { self.repacker, )); let _ = entry_state.apply_action( - BorrowPCGAction::add_region_projection_member( + BorrowPCGAction::add_block_edge( BlockEdge::new( smallvec![MaybeRemotePlace::place_assigned_to_local(local).into()], smallvec![RegionProjection::new( @@ -199,7 +199,7 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { RegionProjection::new(region, arg_place, self.repacker).unwrap(); let entry_state = Rc::>::make_mut(&mut self.data.entry_state); assert!(entry_state.apply_action( - BorrowPCGAction::add_region_projection_member( + BorrowPCGAction::add_block_edge( BlockEdge::new( smallvec![RegionProjection::new( region, diff --git a/src/borrow_pcg/state/mod.rs b/src/borrow_pcg/state/mod.rs index 4053e389..dacb00cd 100644 --- a/src/borrow_pcg/state/mod.rs +++ b/src/borrow_pcg/state/mod.rs @@ -11,10 +11,10 @@ use super::{ latest::Latest, path_condition::{PathCondition, PathConditions}, }; -use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::place::maybe_remote::MaybeRemotePlace; use crate::{borrow_pcg::action::executed_actions::ExecutedActions, combined_pcs::PCGError}; +use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::{ borrow_pcg::edge_data::EdgeData, combined_pcs::{PCGNode, PCGNodeLike}, diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index d9a6ab86..2fe868ff 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -121,7 +121,7 @@ impl<'tcx> BorrowsState<'tcx> { )?); for ra in place.region_projections(repacker) { self.record_and_apply_action( - BorrowPCGAction::add_region_projection_member( + BorrowPCGAction::add_block_edge( BlockEdge::new( smallvec![borrow.value.blocked_place.into()], smallvec![ra.into()], @@ -207,7 +207,7 @@ impl<'tcx> BorrowsState<'tcx> { ); self.record_and_apply_action( - BorrowPCGAction::add_region_projection_member( + BorrowPCGAction::add_block_edge( region_projection_member, PathConditions::new(location.block), "Expand", diff --git a/src/borrow_pcg/unblock_graph.rs b/src/borrow_pcg/unblock_graph.rs index 6377ba8a..6aadabb7 100644 --- a/src/borrow_pcg/unblock_graph.rs +++ b/src/borrow_pcg/unblock_graph.rs @@ -9,7 +9,6 @@ use crate::utils::json::ToJsonWithRepacker; use crate::{ borrow_pcg::{edge_data::EdgeData, state::BorrowsState}, utils::PlaceRepacker, - visualization::generate_unblock_dot_graph, }; type UnblockEdge<'tcx> = BorrowPCGEdge<'tcx>; @@ -44,19 +43,6 @@ impl<'tcx> From> for BorrowPCGUnblockAction<'tcx> { } impl<'tcx> UnblockGraph<'tcx> { - pub(crate) fn edges(&self) -> impl Iterator> { - self.edges.iter() - } - - #[allow(unused)] - pub(crate) fn to_json(&self, repacker: PlaceRepacker<'_, 'tcx>) -> serde_json::Value { - let dot_graph = generate_unblock_dot_graph(&repacker, self).unwrap(); - serde_json::json!({ - "empty": self.is_empty(), - "dot_graph": dot_graph - }) - } - pub(crate) fn new() -> Self { Self { edges: HashSet::new(), diff --git a/src/borrow_pcg/visitor/mod.rs b/src/borrow_pcg/visitor/mod.rs index 0f72876b..c7fc87cf 100644 --- a/src/borrow_pcg/visitor/mod.rs +++ b/src/borrow_pcg/visitor/mod.rs @@ -156,7 +156,7 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { source_proj.region(self.repacker), target_proj.region(self.repacker), ) { - self.apply_action(BorrowPCGAction::add_region_projection_member( + self.apply_action(BorrowPCGAction::add_block_edge( BlockEdge::new( smallvec![source_proj.to_pcg_node(self.repacker)], smallvec![target_proj.to_local_node(self.repacker)], diff --git a/src/borrow_pcg/visitor/stmt.rs b/src/borrow_pcg/visitor/stmt.rs index 84895408..0442b88a 100644 --- a/src/borrow_pcg/visitor/stmt.rs +++ b/src/borrow_pcg/visitor/stmt.rs @@ -141,7 +141,7 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { if let ty::TyKind::Ref(target_region, _, _) = target.ty(self.repacker).ty.kind() { - self.apply_action(BorrowPCGAction::add_region_projection_member( + self.apply_action(BorrowPCGAction::add_block_edge( BlockEdge::new( smallvec![RegionProjection::new( (*const_region).into(), diff --git a/src/combined_pcs/domain.rs b/src/combined_pcs/domain.rs index de024782..7a2aea78 100644 --- a/src/combined_pcs/domain.rs +++ b/src/combined_pcs/domain.rs @@ -245,6 +245,7 @@ pub enum PCGUnsupportedError { Coroutines, DerefUnsafePtr, ExpansionOfAliasType, + IndexingNonIndexableType, InlineAssembly, Other(String), TwoPhaseBorrow, diff --git a/src/utils/place/mod.rs b/src/utils/place/mod.rs index 39c6756f..9d56edf7 100644 --- a/src/utils/place/mod.rs +++ b/src/utils/place/mod.rs @@ -11,10 +11,9 @@ use std::{ mem::discriminant, }; -use crate::combined_pcs::{PCGError, PCGUnsupportedError}; use derive_more::{Deref, DerefMut}; -use crate::rustc_interface::{ +use crate::{combined_pcs::{PCGError, PCGUnsupportedError}, rustc_interface::{ ast::Mutability, data_structures::fx::FxHasher, index::IndexVec, @@ -23,16 +22,12 @@ use crate::rustc_interface::{ ty::{self, Ty, TyCtxt, TyKind}, }, target::abi::VariantIdx, -}; +}}; #[cfg(feature = "debug_info")] use super::debug_info::DebugInfo; -use super::{ - display::DisplayWithRepacker, - validity::{validity_checks_enabled, HasValidityCheck}, - PlaceRepacker, -}; +use super::{display::DisplayWithRepacker, validity::HasValidityCheck, PlaceRepacker}; use crate::utils::json::ToJsonWithRepacker; use crate::{ borrow_pcg::{ @@ -161,7 +156,7 @@ impl<'tcx> HasPlace<'tcx> for Place<'tcx> { elem: PlaceElem<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, ) -> std::result::Result { - self.project_deeper(elem, repacker).map_err(|e| e.into()) + self.project_deeper(elem, repacker).map_err(PCGError::unsupported) } fn iter_projections(&self, _repacker: PlaceRepacker<'_, 'tcx>) -> Vec<(Self, PlaceElem<'tcx>)> { @@ -203,7 +198,13 @@ impl<'tcx> Place<'tcx> { repacker: PlaceRepacker<'_, 'tcx>, ) -> std::result::Result { let base_ty = self.ty(repacker); - + if matches!( + elem, + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } + ) && base_ty.ty.builtin_index().is_none() + { + return Err(PCGUnsupportedError::IndexingNonIndexableType); + } let corrected_elem = if let ProjectionElem::Field(field_idx, proj_ty) = elem { let expected_ty = match base_ty.ty.kind() { ty::TyKind::Adt(def, substs) => { @@ -220,20 +221,10 @@ impl<'tcx> Place<'tcx> { } else { elem }; - if matches!(corrected_elem, ProjectionElem::Index(..)) - && matches!(base_ty.ty.kind(), ty::TyKind::Alias(..)) - { - return Err(PCGUnsupportedError::ExpansionOfAliasType); - } - let place: Place<'tcx> = self + Ok(self .0 .project_deeper(&[corrected_elem], repacker.tcx()) - .into(); - if validity_checks_enabled() { - // Sanity check: this function will panic if the type is invalid - let _ = place.ty(repacker); - } - Ok(place) + .into()) } pub(crate) fn compare_projections( @@ -299,24 +290,22 @@ impl<'tcx> Place<'tcx> { /// from its local, and using types derived from the root place as the types associated /// with Field region projections. For more details see [`Place::project_deeper`]. pub fn with_inherent_region(self, repacker: PlaceRepacker<'_, 'tcx>) -> Self { - let mut proj_iter = self.iter_projections(repacker).into_iter().peekable(); - let mut place: Place<'tcx> = self.local.into(); - while let Some((_, elem)) = proj_iter.next() { + let mut proj_iter = self.iter_projections(repacker).into_iter(); + let mut place = if let Some((place, elem)) = proj_iter.next() { + place.project_deeper(elem, repacker).unwrap() + } else { + return self; + }; + for (_, elem) in proj_iter { if let Ok(next_place) = place.project_deeper(elem, repacker) { place = next_place; } else { - // It might be that we cannot re-project the place. In particular this could happen if - // there are further projections from an alias type (we may not normalize alias types because - // doing so would erase regions). In this case we try to get the original place projected at this point - if let Some((next_place, _)) = proj_iter.peek() { - place = *next_place - } else { - // This is the last projection, so we just return the original place - return self; - } + // We cannot normalize the place (probably due to indexing of an + // alias type that we cannot resolve). For now we just return the + // original place. + return self; } } - place } diff --git a/src/visualization/graph_constructor.rs b/src/visualization/graph_constructor.rs index 27b262e3..62e70134 100644 --- a/src/visualization/graph_constructor.rs +++ b/src/visualization/graph_constructor.rs @@ -5,7 +5,6 @@ use crate::{ graph::BorrowsGraph, region_projection::RegionProjection, state::BorrowsState, - unblock_graph::UnblockGraph, }, combined_pcs::{PCGNode, PCGNodeLike}, free_pcs::{CapabilityKind, CapabilityLocal, CapabilitySummary}, @@ -155,20 +154,24 @@ impl<'a, 'tcx> GraphConstructor<'a, 'tcx> { id } - fn insert_cg_node(&mut self, node: CGNode<'tcx>) -> NodeId { + fn insert_cg_node(&mut self, node: CGNode<'tcx>, capability: Option) -> NodeId { match node { - CGNode::RegionProjection(rp) => self.insert_region_projection_node(rp, None), - CGNode::RemotePlace(rp) => self.insert_remote_node(rp), + CGNode::RegionProjection(rp) => self.insert_region_projection_node(rp, capability), + CGNode::RemotePlace(rp) => self.insert_remote_node(rp, capability), } } - fn insert_abstraction(&mut self, region_abstraction: &AbstractionType<'tcx>) { + fn insert_abstraction( + &mut self, + region_abstraction: &AbstractionType<'tcx>, + capabilities: &impl CapabilityGetter<'tcx>, + ) { let mut input_nodes = BTreeSet::new(); let mut output_nodes = BTreeSet::new(); for edge in region_abstraction.edges() { for input in edge.inputs() { - let input = self.insert_cg_node(input); + let input = self.insert_cg_node(input, capabilities.get(input.into())); input_nodes.insert(input); } for output in edge.outputs() { @@ -219,7 +222,11 @@ impl<'a, 'tcx> GraphConstructor<'a, 'tcx> { self.region_clusters.insert(cluster); } - fn insert_remote_node(&mut self, remote_place: RemotePlace) -> NodeId { + fn insert_remote_node( + &mut self, + remote_place: RemotePlace, + capability: Option, + ) -> NodeId { if let Some(id) = self.remote_nodes.existing_id(&remote_place) { return id; } @@ -230,7 +237,7 @@ impl<'a, 'tcx> GraphConstructor<'a, 'tcx> { owned: false, label: format!("Target of input {:?}", remote_place.assigned_local()), location: None, - capability: None, + capability, ty: format!("{:?}", remote_place.ty(self.repacker)), }, }; @@ -264,53 +271,6 @@ impl<'a, 'tcx> GraphConstructor<'a, 'tcx> { } } -pub struct UnblockGraphConstructor<'a, 'tcx> { - unblock_graph: UnblockGraph<'tcx>, - constructor: GraphConstructor<'a, 'tcx>, -} - -impl<'a, 'tcx> UnblockGraphConstructor<'a, 'tcx> { - pub fn new(unblock_graph: UnblockGraph<'tcx>, repacker: PlaceRepacker<'a, 'tcx>) -> Self { - Self { - unblock_graph, - constructor: GraphConstructor::new(repacker), - } - } - - pub fn construct_graph(mut self) -> Graph { - for edge in self.unblock_graph.edges().cloned().collect::>() { - self.draw_borrow_pcg_edge(edge, &NullCapabilityGetter); - } - self.constructor.into_graph() - } -} - -impl<'mir, 'tcx> PlaceGrapher<'mir, 'tcx> for UnblockGraphConstructor<'mir, 'tcx> { - fn insert_maybe_old_place(&mut self, place: MaybeOldPlace<'tcx>) -> NodeId { - self.constructor - .insert_place_node(place.place(), place.location(), &NullCapabilityGetter) - } - - fn insert_maybe_remote_place(&mut self, place: MaybeRemotePlace<'tcx>) -> NodeId { - match place { - MaybeRemotePlace::Local(place) => self.insert_maybe_old_place(place), - MaybeRemotePlace::Remote(local) => self.constructor.insert_remote_node(local), - } - } - - fn constructor(&mut self) -> &mut GraphConstructor<'mir, 'tcx> { - &mut self.constructor - } - - fn repacker(&self) -> PlaceRepacker<'mir, 'tcx> { - self.constructor.repacker - } - - fn capability_getter(&self) -> impl CapabilityGetter<'tcx> + 'mir { - NullCapabilityGetter - } -} - trait PlaceGrapher<'mir, 'tcx: 'mir> { fn capability_getter(&self) -> impl CapabilityGetter<'tcx> + 'mir; fn insert_maybe_old_place(&mut self, place: MaybeOldPlace<'tcx>) -> NodeId { @@ -319,10 +279,12 @@ trait PlaceGrapher<'mir, 'tcx: 'mir> { constructor.insert_place_node(place.place(), place.location(), &capability_getter) } fn insert_maybe_remote_place(&mut self, place: MaybeRemotePlace<'tcx>) -> NodeId { + let capability_getter = self.capability_getter(); + let capability = capability_getter.get(place.to_pcg_node(self.repacker())); let constructor = self.constructor(); match place { MaybeRemotePlace::Local(place) => self.insert_maybe_old_place(place), - MaybeRemotePlace::Remote(local) => constructor.insert_remote_node(local), + MaybeRemotePlace::Remote(local) => constructor.insert_remote_node(local, capability), } } fn insert_pcg_node(&mut self, node: PCGNode<'tcx>) -> NodeId { @@ -381,7 +343,8 @@ trait PlaceGrapher<'mir, 'tcx: 'mir> { }); } BorrowPCGEdgeKind::Abstraction(abstraction) => { - self.constructor().insert_abstraction(abstraction); + self.constructor() + .insert_abstraction(abstraction, capabilities); } BorrowPCGEdgeKind::Block(member) => { for input in member.inputs.iter() { diff --git a/src/visualization/mod.rs b/src/visualization/mod.rs index 8a3b7cb7..926f2616 100644 --- a/src/visualization/mod.rs +++ b/src/visualization/mod.rs @@ -11,9 +11,9 @@ pub mod legend; pub mod mir_graph; use crate::{ - borrow_pcg::{graph::BorrowsGraph, state::BorrowsState, unblock_graph::UnblockGraph}, + rustc_interface::middle::mir::Location, + borrow_pcg::{graph::BorrowsGraph, state::BorrowsState}, free_pcs::{CapabilityKind, CapabilitySummary}, - rustc_interface, utils::{Place, PlaceRepacker, SnapshotLocation}, }; use std::{ @@ -24,13 +24,12 @@ use std::{ use dot::escape_html; use graph_constructor::BorrowsGraphConstructor; -use rustc_interface::middle::mir::Location; use self::{ dot_graph::{ DotEdge, DotFloatAttr, DotLabel, DotNode, DotStringAttr, EdgeDirection, EdgeOptions, }, - graph_constructor::{GraphCluster, PCSGraphConstructor, UnblockGraphConstructor}, + graph_constructor::{GraphCluster, PCSGraphConstructor}, }; pub fn place_id(place: &Place<'_>) -> String { @@ -266,18 +265,6 @@ impl Graph { } } -pub fn generate_unblock_dot_graph<'a, 'tcx: 'a>( - repacker: &PlaceRepacker<'a, 'tcx>, - unblock_graph: &UnblockGraph<'tcx>, -) -> io::Result { - let constructor = UnblockGraphConstructor::new(unblock_graph.clone(), *repacker); - let graph = constructor.construct_graph(); - let mut buf = vec![]; - let drawer = GraphDrawer::new(&mut buf); - drawer.draw(graph)?; - Ok(String::from_utf8(buf).unwrap()) -} - pub fn generate_dot_graph_str<'a, 'tcx: 'a>( repacker: PlaceRepacker<'a, 'tcx>, summary: &CapabilitySummary<'tcx>, diff --git a/test-files/80_crc_bytewise.rs b/test-files/80_crc_bytewise.rs new file mode 100644 index 00000000..b733235d --- /dev/null +++ b/test-files/80_crc_bytewise.rs @@ -0,0 +1,92 @@ +pub trait Implementation { + /// Associated data necessary for the implementation (e.g. lookup tables). + type Data; +} + +pub trait Width: Sized + 'static {} + +#[derive(Copy, Clone)] +pub struct Table {} + +#[derive(Clone)] +pub struct Crc { + pub algorithm: &'static Algorithm, + data: I::Data, +} + +type DefaultImpl = Table<1>; + +impl Width for u128 {} + +impl Implementation for Table { + type Data = [[W; 256]; L]; +} + +pub struct Algorithm { + /// The number of bit cells in the linear feedback shift register; the degree of the generator + /// polynomial, minus one. + pub width: u8, + /// The generator polynomial that sets the feedback tap positions of the shift register. The + /// least significant bit corresponds to the inward end of the shift register, and is always + /// set. The highest-order term is omitted. + pub poly: W, + /// The settings of the bit cells at the start of each calculation, before reading the first + /// message bit. The least significant bit corresponds to the inward end of the shift register. + pub init: W, + /// If equal to `false`, specifies that the characters of the message are read bit-by-bit, most + /// significant bit (MSB) first; if equal to `true`, the characters are read bit-by-bit, least + /// significant bit (LSB) first. Each sampled message bit is then XORed with the bit being + /// simultaneously shifted out of the register at the most significant end, and the result is + /// passed to the feedback taps. + pub refin: bool, + /// If equal to `false`, specifies that the contents of the register after reading the last + /// message bit are unreflected before presentation; if equal to `true`, it specifies that they + /// are reflected, character-by-character, before presentation. For the purpose of this + /// definition, the reflection is performed by swapping the content of each cell with that of + /// the cell an equal distance from the opposite end of the register; the characters of the CRC + /// are then true images of parts of the reflected register, the character containing the + /// original MSB always appearing first. + pub refout: bool, + /// The XOR value applied to the contents of the register after the last message bit has been + /// read and after the optional reflection. It has the same endianness as the CRC such that its + /// true image appears in the characters of the CRC. + pub xorout: W, + /// The contents of the register after initialising, reading the UTF-8 string `"123456789"` (as + /// 8-bit characters), optionally reflecting, and applying the final XOR. + pub check: W, + /// The contents of the register after initialising, reading an error-free codeword and + /// optionally reflecting the register (if [`refout`](Algorithm::refout)=`true`), but not + /// applying the final XOR. This is mathematically equivalent to initialising the register with + /// the xorout parameter, reflecting it as described (if [`refout`](Algorithm::refout)=`true`), + /// reading as many zero bits as there are cells in the register, and reflecting the result (if + /// [`refin`](Algorithm::refin)=`true`). The residue of a crossed-endian model is calculated + /// assuming that the characters of the received CRC are specially reflected before submitting + /// the codeword. + pub residue: W, +} + +const fn update_bytewise(mut crc: u128, reflect: bool, table: &[u128; 256], bytes: &[u8]) -> u128 { + let mut i = 0; + if reflect { + while i < bytes.len() { + let table_index = ((crc ^ bytes[i] as u128) & 0xFF) as usize; + crc = table[table_index] ^ (crc >> 8); + i += 1; + } + } else { + while i < bytes.len() { + let table_index = (((crc >> 120) ^ bytes[i] as u128) & 0xFF) as usize; + crc = table[table_index] ^ (crc << 8); + i += 1; + } + } + crc +} + +impl Crc> { + const fn update(&self, crc: u128, bytes: &[u8]) -> u128 { + update_bytewise(crc, self.algorithm.refin, &self.data[0], bytes) + } +} + +fn main(){} From aaf2f16597ff5f25afac68075997199ba78ae47c Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Fri, 28 Feb 2025 15:09:30 -0800 Subject: [PATCH 11/21] fix a permissions issue --- src/borrow_pcg/action/mod.rs | 2 +- src/borrow_pcg/graph/mod.rs | 13 +++++-------- src/borrow_pcg/state/mod.rs | 16 ++++++---------- src/borrow_pcg/visitor/stmt.rs | 7 +++++++ src/visualization/graph_constructor.rs | 21 ++++++++++++--------- test-files/02_list_zero.rs | 3 ++- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index 5a156f1a..d782e298 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -327,7 +327,7 @@ impl<'tcx> BorrowsState<'tcx> { updated } BorrowPCGActionKind::RenamePlace { old, new } => { - self.change_pcs_elem(old, new, repacker) + self.rename_place(old, new, repacker) } }; Ok(result) diff --git a/src/borrow_pcg/graph/mod.rs b/src/borrow_pcg/graph/mod.rs index a9e089c7..f96afc79 100644 --- a/src/borrow_pcg/graph/mod.rs +++ b/src/borrow_pcg/graph/mod.rs @@ -13,8 +13,7 @@ use crate::{ middle::mir::{self, BasicBlock, TerminatorEdges}, }, utils::{ - display::{DebugLines, DisplayDiff, DisplayWithRepacker}, - validity::HasValidityCheck, + display::{DebugLines, DisplayDiff, DisplayWithRepacker}, maybe_old::MaybeOldPlace, validity::HasValidityCheck }, validity_checks_enabled, }; @@ -725,19 +724,17 @@ impl<'tcx> BorrowsGraph<'tcx> { changed } - pub(crate) fn change_pcs_elem( + pub(crate) fn rename_place( &mut self, - old: T, - new: T, + old: MaybeOldPlace<'tcx>, + new: MaybeOldPlace<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, ) -> bool - where - BorrowPCGEdge<'tcx>: HasPcsElems, { self.mut_pcs_elems( |thing| { if *thing == old { - *thing = new.clone(); + *thing = new; true } else { false diff --git a/src/borrow_pcg/state/mod.rs b/src/borrow_pcg/state/mod.rs index dacb00cd..8373dd17 100644 --- a/src/borrow_pcg/state/mod.rs +++ b/src/borrow_pcg/state/mod.rs @@ -7,14 +7,13 @@ use super::{ coupling_graph_constructor::BorrowCheckerInterface, edge::kind::BorrowPCGEdgeKind, graph::{BorrowsGraph, FrozenGraphRef}, - has_pcs_elem::HasPcsElems, latest::Latest, path_condition::{PathCondition, PathConditions}, }; +use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::place::maybe_remote::MaybeRemotePlace; use crate::{borrow_pcg::action::executed_actions::ExecutedActions, combined_pcs::PCGError}; -use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::{ borrow_pcg::edge_data::EdgeData, combined_pcs::{PCGNode, PCGNodeLike}, @@ -226,16 +225,13 @@ impl<'tcx> BorrowsState<'tcx> { } #[must_use] - pub(crate) fn change_pcs_elem( + pub(crate) fn rename_place( &mut self, - old: T, - new: T, + old: MaybeOldPlace<'tcx>, + new: MaybeOldPlace<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> bool - where - BorrowPCGEdge<'tcx>: HasPcsElems, - { - self.graph.change_pcs_elem(old, new, repacker) + ) -> bool { + self.graph.rename_place(old, new, repacker) } fn min_blocked_by_capability( diff --git a/src/borrow_pcg/visitor/stmt.rs b/src/borrow_pcg/visitor/stmt.rs index 0442b88a..c21181a6 100644 --- a/src/borrow_pcg/visitor/stmt.rs +++ b/src/borrow_pcg/visitor/stmt.rs @@ -105,6 +105,13 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { if !target.is_owned(self.repacker) { state.set_capability(target.into(), CapabilityKind::Exclusive, self.repacker); } + for rp in target.region_projections(self.repacker) { + state.set_capability( + rp.to_pcg_node(self.repacker), + CapabilityKind::Exclusive, + self.repacker, + ); + } match rvalue { Rvalue::Aggregate( box (AggregateKind::Adt(..) | AggregateKind::Tuple | AggregateKind::Array(..)), diff --git a/src/visualization/graph_constructor.rs b/src/visualization/graph_constructor.rs index 62e70134..3c777c10 100644 --- a/src/visualization/graph_constructor.rs +++ b/src/visualization/graph_constructor.rs @@ -175,15 +175,13 @@ impl<'a, 'tcx> GraphConstructor<'a, 'tcx> { input_nodes.insert(input); } for output in edge.outputs() { - let output = self.insert_region_projection_node( - output.set_base(output.base.into(), self.repacker), - None, - ); + let output = output.set_base(output.base.into(), self.repacker); + let output = + self.insert_region_projection_node(output, capabilities.get(output.into())); output_nodes.insert(output); } for input in &input_nodes { for output in &output_nodes { - // TODO: Color or Label edges self.edges.insert(GraphEdge::Abstract { blocked: *input, blocking: *output, @@ -288,20 +286,25 @@ trait PlaceGrapher<'mir, 'tcx: 'mir> { } } fn insert_pcg_node(&mut self, node: PCGNode<'tcx>) -> NodeId { + let capabilities = self.capability_getter(); + let node_capability = capabilities.get(node); match node { PCGNode::Place(place) => self.insert_maybe_remote_place(place), - PCGNode::RegionProjection(rp) => { - self.constructor().insert_region_projection_node(rp, None) - } + PCGNode::RegionProjection(rp) => self + .constructor() + .insert_region_projection_node(rp, node_capability), } } fn insert_local_node(&mut self, node: LocalNode<'tcx>) -> NodeId { + let capabilities = self.capability_getter(); + let node_capability = capabilities.get(node.into()); match node { LocalNode::Place(place) => self.insert_maybe_old_place(place), LocalNode::RegionProjection(rp) => { let rp = rp.to_region_projection(self.repacker()); - self.constructor().insert_region_projection_node(rp, None) + self.constructor() + .insert_region_projection_node(rp, node_capability) } } } diff --git a/test-files/02_list_zero.rs b/test-files/02_list_zero.rs index 5e00ce6a..4737fc13 100644 --- a/test-files/02_list_zero.rs +++ b/test-files/02_list_zero.rs @@ -8,8 +8,9 @@ fn all_zero(mut l : &mut List) { while let List::Cons(el, tl) = l { *el = 0; + // PCG: bb4[7] post_main: l↓'?6: E l = tl } } -fn main() {} \ No newline at end of file +fn main() {} From 8fcfbf12b603355790546381546cda0640d2c92b Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Mon, 3 Mar 2025 09:01:13 -0800 Subject: [PATCH 12/21] WIP --- src/borrow_pcg/action/actions.rs | 33 +++- src/borrow_pcg/action/mod.rs | 221 +++++++++------------- src/borrow_pcg/borrow_pcg_capabilities.rs | 48 ++++- src/borrow_pcg/domain.rs | 42 ++-- src/borrow_pcg/state/mod.rs | 30 ++- src/borrow_pcg/state/obtain.rs | 54 +++--- src/borrow_pcg/visitor/mod.rs | 49 +++-- src/borrow_pcg/visitor/stmt.rs | 62 +++--- src/combined_pcs/engine.rs | 2 +- src/free_pcs/impl/place.rs | 30 +-- src/free_pcs/impl/triple.rs | 6 +- 11 files changed, 281 insertions(+), 296 deletions(-) diff --git a/src/borrow_pcg/action/actions.rs b/src/borrow_pcg/action/actions.rs index d2fcee15..e9ab0d69 100644 --- a/src/borrow_pcg/action/actions.rs +++ b/src/borrow_pcg/action/actions.rs @@ -1,13 +1,15 @@ use crate::borrow_pcg::action::BorrowPCGAction; +use crate::borrow_pcg::borrow_pcg_edge::BorrowPCGEdge; use crate::borrow_pcg::borrow_pcg_expansion::BorrowPCGExpansion; +use crate::borrow_pcg::edge::block::BlockEdge; +use crate::borrow_pcg::edge::kind::BorrowPCGEdgeKind; use crate::borrow_pcg::graph::Conditioned; use crate::borrow_pcg::path_condition::PathConditions; -use crate::borrow_pcg::edge::block::BlockEdge; use crate::borrow_pcg::unblock_graph::BorrowPCGUnblockAction; +use crate::rustc_interface::data_structures::fx::FxHashSet; use crate::utils::json::ToJsonWithRepacker; use crate::utils::PlaceRepacker; use crate::{validity_checks_enabled, Weaken}; -use crate::rustc_interface::data_structures::fx::FxHashSet; use super::BorrowPCGActionKind; @@ -30,13 +32,19 @@ impl<'tcx> BorrowPCGActions<'tcx> { &self.0 } - pub fn added_region_projection_members(&self) -> FxHashSet>> { + pub fn added_block_edges(&self) -> FxHashSet>> { self.0 .iter() .filter_map(|action| match action.kind() { - BorrowPCGActionKind::AddBlockEdge(member, path_conditions) => { - Some(Conditioned::new(member.clone(), path_conditions.clone())) - } + BorrowPCGActionKind::AddEdge { + edge: + BorrowPCGEdge { + kind: BorrowPCGEdgeKind::Block(edge), + conditions, + .. + }, + .. + } => Some(Conditioned::new(edge.clone(), conditions.clone())), _ => None, }) .collect() @@ -66,9 +74,16 @@ impl<'tcx> BorrowPCGActions<'tcx> { self.0 .iter() .filter_map(|action| match action.kind() { - BorrowPCGActionKind::InsertBorrowPCGExpansion(expansion, location) => Some( - Conditioned::new(expansion.clone(), PathConditions::AtBlock(location.block)), - ), + BorrowPCGActionKind::AddEdge { + edge, + .. + } => match edge.kind() { + BorrowPCGEdgeKind::BorrowPCGExpansion(expansion) => Some(Conditioned::new( + expansion.clone(), + edge.conditions().clone(), + )), + _ => None, + }, _ => None, }) .collect() diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index d782e298..c0705b91 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -3,6 +3,7 @@ use tracing::instrument; use super::borrow_pcg_edge::{BorrowPCGEdge, LocalNode, ToBorrowsEdge}; use super::borrow_pcg_expansion::BorrowPCGExpansion; use super::edge::block::BlockEdge; +use super::edge::kind::BorrowPCGEdgeKind; use super::path_condition::PathConditions; use super::state::BorrowsState; use crate::borrow_pcg::edge::abstraction::AbstractionType; @@ -30,13 +31,7 @@ pub struct BorrowPCGAction<'tcx> { impl<'tcx> BorrowPCGAction<'tcx> { pub(crate) fn debug_line(&self, repacker: PlaceRepacker<'_, 'tcx>) -> String { match &self.kind { - BorrowPCGActionKind::AddAbstractionEdge(abstraction, path_conditions) => { - format!( - "Add Abstraction Edge: {}; path conditions: {}", - abstraction.to_short_string(repacker), - path_conditions - ) - } + BorrowPCGActionKind::AddEdge { edge, .. } => edge.to_short_string(repacker), BorrowPCGActionKind::Weaken(weaken) => weaken.debug_line(repacker), BorrowPCGActionKind::Restore(restore_capability) => { restore_capability.debug_line(repacker) @@ -52,18 +47,6 @@ impl<'tcx> BorrowPCGAction<'tcx> { BorrowPCGActionKind::RemoveEdge(borrow_pcgedge) => { format!("Remove Edge {}", borrow_pcgedge.to_short_string(repacker)) } - BorrowPCGActionKind::AddBlockEdge(region_projection_member, path_conditions) => { - format!( - "Add Region Projection Member: {}; path conditions: {}", - region_projection_member.to_short_string(repacker), - path_conditions - ) - } - BorrowPCGActionKind::InsertBorrowPCGExpansion(expansion, location) => format!( - "Insert Expansion {} at {:?}", - expansion.to_short_string(repacker), - location - ), BorrowPCGActionKind::RenamePlace { old, new } => { format!("Rename {:?} to {:?}", old, new) } @@ -113,25 +96,13 @@ impl<'tcx> BorrowPCGAction<'tcx> { } } - pub(super) fn add_block_edge( - member: BlockEdge<'tcx>, - pc: PathConditions, - context: impl Into, - ) -> Self { - BorrowPCGAction { - kind: BorrowPCGActionKind::AddBlockEdge(member, pc), - debug_context: Some(context.into()), - } - } - - pub(super) fn insert_borrow_pcg_expansion( - expansion: BorrowPCGExpansion<'tcx, LocalNode<'tcx>>, - location: Location, - context: impl Into, - ) -> Self { + pub(super) fn add_edge(edge: BorrowPCGEdge<'tcx>, for_exclusive: bool) -> Self { BorrowPCGAction { - kind: BorrowPCGActionKind::InsertBorrowPCGExpansion(expansion, location), - debug_context: Some(context.into()), + kind: BorrowPCGActionKind::AddEdge { + edge, + for_exclusive, + }, + debug_context: None, } } @@ -159,9 +130,10 @@ pub enum BorrowPCGActionKind<'tcx> { MakePlaceOld(Place<'tcx>), SetLatest(Place<'tcx>, Location), RemoveEdge(BorrowPCGEdge<'tcx>), - AddBlockEdge(BlockEdge<'tcx>, PathConditions), - InsertBorrowPCGExpansion(BorrowPCGExpansion<'tcx>, Location), - AddAbstractionEdge(AbstractionType<'tcx>, PathConditions), + AddEdge { + edge: BorrowPCGEdge<'tcx>, + for_exclusive: bool, + }, RenamePlace { old: MaybeOldPlace<'tcx>, new: MaybeOldPlace<'tcx>, @@ -186,23 +158,17 @@ impl<'tcx> DisplayWithRepacker<'tcx> for BorrowPCGActionKind<'tcx> { BorrowPCGActionKind::RemoveEdge(borrow_pcgedge) => { format!("Remove Edge {}", borrow_pcgedge.to_short_string(repacker)) } - BorrowPCGActionKind::AddBlockEdge(block_edge, _) => { - format!("Add Block Edge: {}", block_edge.to_short_string(repacker),) - } - BorrowPCGActionKind::InsertBorrowPCGExpansion(borrow_pcgexpansion, location) => { - format!( - "Insert Expansion {} at {:?}", - borrow_pcgexpansion.to_short_string(repacker), - location - ) - } - BorrowPCGActionKind::AddAbstractionEdge(abstraction_type, _) => format!( - "Add Abstraction Edge: {}", - abstraction_type.to_short_string(repacker), - ), BorrowPCGActionKind::RenamePlace { old, new } => { format!("Rename {:?} to {:?}", old, new) } + BorrowPCGActionKind::AddEdge { + edge, + for_exclusive, + } => format!( + "Add Edge: {}; for exclusive: {}", + edge.to_short_string(repacker), + for_exclusive + ), } } } @@ -221,24 +187,6 @@ impl<'tcx> BorrowsState<'tcx> { repacker: PlaceRepacker<'_, 'tcx>, ) -> Result { let result = match action.kind { - BorrowPCGActionKind::AddAbstractionEdge(abstraction, pc) => { - let mut changed = false; - for edge in abstraction.edges() { - for input in edge.inputs() { - changed |= - self.set_capability(input.into(), CapabilityKind::Lent, repacker); - } - for output in edge.outputs() { - changed |= self.set_capability( - output.to_pcg_node(repacker), - CapabilityKind::Exclusive, - repacker, - ); - } - } - changed |= self.insert(abstraction.to_borrow_pcg_edge(pc)); - changed - } BorrowPCGActionKind::Restore(restore) => { let restore_node: PCGNode<'tcx> = restore.node().into(); if let Some(cap) = self.get_capability(restore_node) { @@ -270,87 +218,98 @@ impl<'tcx> BorrowsState<'tcx> { self.set_latest(place, location, repacker) } BorrowPCGActionKind::RemoveEdge(edge) => self.remove(&edge), - BorrowPCGActionKind::AddBlockEdge(member, pc) => { - self.add_region_projection_member(member, pc, repacker) - } - BorrowPCGActionKind::InsertBorrowPCGExpansion(expansion, location) => { - let updated = self.insert( - expansion - .clone() - .to_borrow_pcg_edge(PathConditions::new(location.block)), - ); - if updated { - let base = expansion.base(); - let expanded_capability = match &expansion { - BorrowPCGExpansion::FromOwned(expansion_of_owned) => { - match expansion_of_owned.base().ty(repacker).ty.ref_mutability() { - Some(Mutability::Mut) => CapabilityKind::Exclusive, - Some(Mutability::Not) => CapabilityKind::Read, - None => unreachable!(), - } - } - BorrowPCGExpansion::FromBorrow(expansion_of_borrowed) => { - if let Some(capability) = - self.get_capability(expansion_of_borrowed.base.into()) - { - capability - } else { - // Presumably already expanded in another branch - return Ok(true); - } - } - }; + BorrowPCGActionKind::AddEdge { + edge, + for_exclusive, + } => { + let mut changed = self.insert(edge.clone()); + match edge.kind { + BorrowPCGEdgeKind::Borrow(borrow_edge) => todo!(), + BorrowPCGEdgeKind::BorrowPCGExpansion(expansion) => { + if changed { + let base = expansion.base(); + let expanded_capability = match &expansion { + BorrowPCGExpansion::FromOwned(expansion_of_owned) => { + match expansion_of_owned.base().ty(repacker).ty.ref_mutability() + { + Some(Mutability::Mut) => CapabilityKind::Exclusive, + Some(Mutability::Not) => CapabilityKind::Read, + None => unreachable!(), + } + } + BorrowPCGExpansion::FromBorrow(expansion_of_borrowed) => { + if let Some(capability) = + self.get_capability(expansion_of_borrowed.base.into()) + { + capability + } else { + // Presumably already expanded in another branch + return Ok(true); + } + } + }; - // If the expansion is a deref of a borrow, its expansion should not - // change the capability to the base. We are allowed to have e.g. exclusive - // permission to `x: &'a mut T` and `*x` simultaneously. Intuitively, `*x` - // gets its permission from `x↓'a`. - if !expansion.is_deref_of_borrow(repacker) { - match expanded_capability { - CapabilityKind::Read => { - _ = self.set_capability( - base.into(), - CapabilityKind::Read, - repacker, - ); + // If the expansion is a deref of a borrow, its expansion should not + // change the capability to the base. We are allowed to have e.g. exclusive + // permission to `x: &'a mut T` and `*x` simultaneously. Intuitively, `*x` + // gets its permission from `x↓'a`. + if !expansion.is_deref_of_borrow(repacker) { + match expanded_capability { + CapabilityKind::Read => { + _ = self.set_capability( + base.into(), + CapabilityKind::Read, + repacker, + ); + } + _ => { + _ = self.remove_capability(base.into()); + } + } } - _ => { - _ = self.remove_capability(base.into()); + + for p in expansion.expansion(repacker)?.iter() { + _ = self.set_capability((*p).into(), expanded_capability, repacker); } } + changed } - - for p in expansion.expansion(repacker)?.iter() { - _ = self.set_capability((*p).into(), expanded_capability, repacker); + BorrowPCGEdgeKind::Abstraction(abstraction_type) => { + changed + } + BorrowPCGEdgeKind::Block(block_edge) => { + self.add_block_edge(block_edge, edge.conditions, for_exclusive, repacker) } } - updated - } - BorrowPCGActionKind::RenamePlace { old, new } => { - self.rename_place(old, new, repacker) } + BorrowPCGActionKind::RenamePlace { old, new } => self.rename_place(old, new, repacker), }; Ok(result) } /// Adds a region projection member to the graph and sets appropriate /// capabilities for the place and projection - fn add_region_projection_member( + fn add_block_edge( &mut self, member: BlockEdge<'tcx>, pc: PathConditions, + for_exclusive: bool, repacker: PlaceRepacker<'_, 'tcx>, ) -> bool { - let mut changed = self.insert(member.clone().to_borrow_pcg_edge(pc)); - let (input_cap, output_cap) = if member.mutability(repacker) == Mutability::Mut { - (CapabilityKind::Lent, CapabilityKind::Exclusive) - } else { - (CapabilityKind::Read, CapabilityKind::Read) - }; + let mut changed = false; for i in member.inputs.iter() { - changed |= self.set_capability(*i, input_cap, repacker); + if for_exclusive { + changed |= self.remove_capability(*i); + } else { + changed |= self.set_capability(*i, CapabilityKind::Read, repacker); + } } for o in member.outputs.iter() { + let output_cap = if for_exclusive { + CapabilityKind::Exclusive + } else { + CapabilityKind::Read + }; changed |= self.set_capability((*o).into(), output_cap, repacker); } changed diff --git a/src/borrow_pcg/borrow_pcg_capabilities.rs b/src/borrow_pcg/borrow_pcg_capabilities.rs index 6106777a..a29f075e 100644 --- a/src/borrow_pcg/borrow_pcg_capabilities.rs +++ b/src/borrow_pcg/borrow_pcg_capabilities.rs @@ -5,11 +5,12 @@ use crate::{ free_pcs::CapabilityKind, rustc_interface::data_structures::fx::FxHashMap, utils::{ - display::{DebugLines, DisplayWithRepacker}, - PlaceRepacker, + display::{DebugLines, DisplayWithRepacker}, maybe_old::MaybeOldPlace, Place, PlaceRepacker }, }; +use super::{has_pcs_elem::{HasPcsElems, MakePlaceOld}, latest::Latest}; + /// Tracks the capabilities of places in the borrow PCG. We don't store this /// information in the borrows graph directly to facilitate simpler logic for /// joins (in particular, to identify when capabilities to a place in the PCG @@ -33,6 +34,49 @@ impl<'tcx> BorrowPCGCapabilities<'tcx> { Self(Default::default()) } + pub(crate) fn make_place_old( + &mut self, + place: Place<'tcx>, + latest: &Latest<'tcx>, + repacker: PlaceRepacker<'_, 'tcx>, + ) -> bool { + let mut changed = false; + self.0 = self + .0 + .clone() + .into_iter() + .map(|(mut node, capability)| { + changed |= node.make_place_old(place, latest, repacker); + (node, capability) + }) + .collect(); + changed + } + + pub(crate) fn rename_place( + &mut self, + old: MaybeOldPlace<'tcx>, + new: MaybeOldPlace<'tcx>, + ) -> bool { + let mut changed = false; + self.0 = self + .0 + .clone() + .into_iter() + .map(|(mut node, capability)| { + let places = node.pcs_elems(); + for place in places { + if *place == old { + *place = new; + changed = true; + } + } + (node, capability) + }) + .collect(); + changed + } + /// Returns true iff the capability was changed. pub(super) fn insert(&mut self, node: PCGNode<'tcx>, capability: CapabilityKind) -> bool { self.0.insert(node, capability) != Some(capability) diff --git a/src/borrow_pcg/domain.rs b/src/borrow_pcg/domain.rs index 024b7421..3e7f6245 100644 --- a/src/borrow_pcg/domain.rs +++ b/src/borrow_pcg/domain.rs @@ -3,7 +3,9 @@ use std::rc::Rc; use crate::borrow_pcg::action::actions::BorrowPCGActions; use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_checker::r#impl::BorrowCheckerImpl; +use crate::borrow_pcg::borrow_pcg_edge::BorrowPCGEdge; use crate::borrow_pcg::edge::block::{BlockEdge, BlockEdgeKind}; +use crate::borrow_pcg::edge::kind::BorrowPCGEdgeKind; use crate::borrow_pcg::path_condition::{PathCondition, PathConditions}; use crate::borrow_pcg::state::BorrowsState; use crate::combined_pcs::EvalStmtPhase::*; @@ -176,20 +178,22 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { self.repacker, )); let _ = entry_state.apply_action( - BorrowPCGAction::add_block_edge( - BlockEdge::new( - smallvec![MaybeRemotePlace::place_assigned_to_local(local).into()], - smallvec![RegionProjection::new( - (*region).into(), - arg_place, - self.repacker - ) - .unwrap() - .into(),], - BlockEdgeKind::FunctionInput, + BorrowPCGAction::add_edge( + BorrowPCGEdge::new( + BorrowPCGEdgeKind::Block(BlockEdge::new( + smallvec![MaybeRemotePlace::place_assigned_to_local(local).into()], + smallvec![RegionProjection::new( + (*region).into(), + arg_place, + self.repacker + ) + .unwrap() + .into(),], + BlockEdgeKind::FunctionInput, + )), + PathConditions::AtBlock((Location::START).block), ), - PathConditions::AtBlock((Location::START).block), - "Introduce initial borrow", + true, ), self.repacker, ); @@ -199,9 +203,10 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { RegionProjection::new(region, arg_place, self.repacker).unwrap(); let entry_state = Rc::>::make_mut(&mut self.data.entry_state); assert!(entry_state.apply_action( - BorrowPCGAction::add_block_edge( - BlockEdge::new( - smallvec![RegionProjection::new( + BorrowPCGAction::add_edge( + BorrowPCGEdge::new( + BlockEdge::new( + smallvec![RegionProjection::new( region, RemotePlace::new(local), self.repacker, @@ -212,9 +217,10 @@ impl<'mir, 'tcx> BorrowsDomain<'mir, 'tcx> { .to_pcg_node(self.repacker)], smallvec![region_projection.into()], BlockEdgeKind::Todo, - ), + ).into(), PathConditions::AtBlock((Location::START).block), - "Initialize Local", + ), + true, ), self.repacker, ).unwrap()); diff --git a/src/borrow_pcg/state/mod.rs b/src/borrow_pcg/state/mod.rs index 8373dd17..bbf53aab 100644 --- a/src/borrow_pcg/state/mod.rs +++ b/src/borrow_pcg/state/mod.rs @@ -231,7 +231,9 @@ impl<'tcx> BorrowsState<'tcx> { new: MaybeOldPlace<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, ) -> bool { - self.graph.rename_place(old, new, repacker) + let mut changed = self.graph.rename_place(old, new, repacker); + changed |= Rc::<_>::make_mut(&mut self.capabilities).rename_place(old, new); + changed } fn min_blocked_by_capability( @@ -284,12 +286,7 @@ impl<'tcx> BorrowsState<'tcx> { if let Some(local_node) = node.as_local_node(repacker) { let blocked_cap = self.get_capability(node); - let restore_cap = match self.get_capability(node) { - Some(CapabilityKind::Lent | CapabilityKind::LentShared) => { - Some(CapabilityKind::Exclusive) - } - _ => self.min_blocked_by_capability(edge.kind(), repacker), - }; + let restore_cap = self.min_blocked_by_capability(edge.kind(), repacker); if let Some(restore_cap) = restore_cap { if blocked_cap.is_none_or(|bc| bc < restore_cap) { @@ -496,24 +493,18 @@ impl<'tcx> BorrowsState<'tcx> { BorrowKind::Mut { kind: MutBorrowKind::Default, } => { - if self.get_capability(blocked_place.into()) != Some(CapabilityKind::Lent) { - assert!(self.set_capability( - blocked_place.into(), - CapabilityKind::Lent, - repacker - )); - } + self.remove_capability(blocked_place.into()); } _ => { match self.get_capability(blocked_place.into()) { - Some(CapabilityKind::Exclusive | CapabilityKind::Lent) => { + Some(CapabilityKind::Exclusive) => { assert!(self.set_capability( blocked_place.into(), - CapabilityKind::LentShared, + CapabilityKind::Read, repacker )); } - Some(CapabilityKind::Read | CapabilityKind::LentShared) => { + Some(CapabilityKind::Read) => { // Do nothing, this just adds another shared borrow } None => { @@ -542,6 +533,9 @@ impl<'tcx> BorrowsState<'tcx> { place: Place<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, ) -> bool { - self.graph.make_place_old(place, &self.latest, repacker) + let mut changed = self.graph.make_place_old(place, &self.latest, repacker); + changed |= + Rc::<_>::make_mut(&mut self.capabilities).make_place_old(place, &self.latest, repacker); + changed } } diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index 2fe868ff..9826635e 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -3,6 +3,7 @@ use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_pcg_edge::BorrowPCGEdge; use crate::borrow_pcg::borrow_pcg_expansion::{BorrowExpansion, BorrowPCGExpansion}; use crate::borrow_pcg::edge::block::{BlockEdge, BlockEdgeKind}; +use crate::borrow_pcg::edge::kind::BorrowPCGEdgeKind; use crate::borrow_pcg::graph::borrows_imgcat_debug; use crate::borrow_pcg::path_condition::PathConditions; use crate::borrow_pcg::region_projection::RegionProjection; @@ -32,7 +33,7 @@ impl ObtainReason { BorrowKind::Fake(_) => unreachable!(), BorrowKind::Mut { kind } => match kind { MutBorrowKind::Default => CapabilityKind::Exclusive, - MutBorrowKind::TwoPhaseBorrow => CapabilityKind::LentShared, + MutBorrowKind::TwoPhaseBorrow => CapabilityKind::Read, MutBorrowKind::ClosureCapture => CapabilityKind::Exclusive, }, }, @@ -78,7 +79,7 @@ impl<'tcx> BorrowsState<'tcx> { } if !self.contains(place, repacker) { - let extra_acts = self.expand_to(place, repacker, location)?; + let extra_acts = self.expand_to(place, repacker, location, obtain_reason)?; actions.extend(extra_acts); } @@ -121,14 +122,17 @@ impl<'tcx> BorrowsState<'tcx> { )?); for ra in place.region_projections(repacker) { self.record_and_apply_action( - BorrowPCGAction::add_block_edge( - BlockEdge::new( - smallvec![borrow.value.blocked_place.into()], - smallvec![ra.into()], - BlockEdgeKind::ContractRef, + BorrowPCGAction::add_edge( + BorrowPCGEdge::new( + BlockEdge::new( + smallvec![borrow.value.blocked_place.into()], + smallvec![ra.into()], + BlockEdgeKind::ContractRef, + ) + .into(), + PathConditions::new(location.block), ), - PathConditions::new(location.block), - "Contract To", + true, ), &mut actions, repacker, @@ -183,6 +187,7 @@ impl<'tcx> BorrowsState<'tcx> { to_place: Place<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, location: Location, + obtain_reason: ObtainReason, ) -> Result, PCGError> { let mut actions = ExecutedActions::new(); @@ -200,17 +205,16 @@ impl<'tcx> BorrowsState<'tcx> { // e.g t|'a let base_rp = RegionProjection::new((*region).into(), base, repacker).unwrap(); - let region_projection_member = BlockEdge::new( + let block_edge = BlockEdge::new( smallvec![base_rp.to_pcg_node(repacker)], smallvec![target.into()], BlockEdgeKind::DerefRegionProjection, ); self.record_and_apply_action( - BorrowPCGAction::add_block_edge( - region_projection_member, - PathConditions::new(location.block), - "Expand", + BorrowPCGAction::add_edge( + BorrowPCGEdge::new(block_edge.into(), PathConditions::new(location.block)), + true, ), &mut actions, repacker, @@ -226,10 +230,13 @@ impl<'tcx> BorrowsState<'tcx> { repacker, )?; - let action = BorrowPCGAction::insert_borrow_pcg_expansion( - expansion, - location, - "Ensure Deref Expansion (Place)", + let action = BorrowPCGAction::add_edge( + BorrowPCGEdge::new( + BorrowPCGEdgeKind::BorrowPCGExpansion(expansion), + PathConditions::new(location.block), + ) + .into(), + true, ); self.record_and_apply_action(action, &mut actions, repacker)?; } @@ -251,10 +258,13 @@ impl<'tcx> BorrowsState<'tcx> { repacker, )?; self.record_and_apply_action( - BorrowPCGAction::insert_borrow_pcg_expansion( - expansion, - location, - "Ensure Deref Expansion (Region Projection)", + BorrowPCGAction::add_edge( + BorrowPCGEdge::new( + BorrowPCGEdgeKind::BorrowPCGExpansion(expansion), + PathConditions::new(location.block), + ) + .into(), + true, ), &mut actions, repacker, diff --git a/src/borrow_pcg/visitor/mod.rs b/src/borrow_pcg/visitor/mod.rs index c7fc87cf..0a45b3ae 100644 --- a/src/borrow_pcg/visitor/mod.rs +++ b/src/borrow_pcg/visitor/mod.rs @@ -23,6 +23,7 @@ use crate::{ use super::{ action::{BorrowPCGAction, BorrowPCGActionKind}, + borrow_pcg_edge::BorrowPCGEdge, coupling_graph_constructor::BorrowCheckerInterface, edge::block::BlockEdge, path_condition::PathConditions, @@ -156,14 +157,18 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { source_proj.region(self.repacker), target_proj.region(self.repacker), ) { - self.apply_action(BorrowPCGAction::add_block_edge( - BlockEdge::new( - smallvec![source_proj.to_pcg_node(self.repacker)], - smallvec![target_proj.to_local_node(self.repacker)], - kind(target_proj.region(self.repacker)), - ), - PathConditions::AtBlock(location.block), - "Outliving Projection", + self.apply_action(BorrowPCGAction::add_edge( + BorrowPCGEdge::new( + BlockEdge::new( + smallvec![source_proj.to_pcg_node(self.repacker)], + smallvec![target_proj.to_local_node(self.repacker)], + kind(target_proj.region(self.repacker)), + ) + .into(), + PathConditions::AtBlock(location.block), + ) + .into(), + true, )); } } @@ -253,18 +258,17 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { // No edges may be added e.g. if the inputs do not contain any borrows if !edges.is_empty() { - self.apply_action( - BorrowPCGActionKind::AddAbstractionEdge( - AbstractionType::FunctionCall(FunctionCallAbstraction::new( - location, - *func_def_id, - substs, - edges.clone(), - )), + self.apply_action(BorrowPCGAction::add_edge( + BorrowPCGEdge::new( + AbstractionType::FunctionCall( + FunctionCallAbstraction::new(location, *func_def_id, substs, edges.clone()) + .into(), + ) + .into(), PathConditions::AtBlock(location.block), - ) - .into(), - ); + ), + true, + )); } } @@ -325,13 +329,6 @@ impl<'tcx> Visitor<'tcx> for BorrowsVisitor<'tcx, '_, '_> { } else if self.stage == StatementStage::Operands && !self.preparing { if let Operand::Move(place) = operand { let place: utils::Place<'tcx> = (*place).into(); - for rp in place.region_projections(self.repacker) { - self.domain.post_state_mut().set_capability( - rp.to_pcg_node(self.repacker), - CapabilityKind::Write, - self.repacker, - ); - } if !place.is_owned(self.repacker) { self.domain.post_state_mut().set_capability( place.into(), diff --git a/src/borrow_pcg/visitor/stmt.rs b/src/borrow_pcg/visitor/stmt.rs index c21181a6..df792cef 100644 --- a/src/borrow_pcg/visitor/stmt.rs +++ b/src/borrow_pcg/visitor/stmt.rs @@ -1,12 +1,7 @@ use super::BorrowsVisitor; use crate::{ borrow_pcg::{ - action::BorrowPCGAction, - edge::block::{BlockEdge, BlockEdgeKind}, - path_condition::PathConditions, - region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, - state::obtain::ObtainReason, - visitor::StatementStage, + action::BorrowPCGAction, borrow_pcg_edge::BorrowPCGEdge, edge::block::{BlockEdge, BlockEdgeKind}, path_condition::PathConditions, region_projection::{MaybeRemoteRegionProjectionBase, RegionProjection}, state::obtain::ObtainReason, visitor::StatementStage }, combined_pcs::{EvalStmtPhase, PCGError, PCGNodeLike, PCGUnsupportedError}, free_pcs::CapabilityKind, @@ -81,10 +76,11 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { )); } } else { - panic!( - "No capability found for {}", - target.to_short_string(self.repacker) - ); + // TODO + // panic!( + // "No capability found for {}", + // target.to_short_string(self.repacker) + // ); } } } @@ -105,13 +101,6 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { if !target.is_owned(self.repacker) { state.set_capability(target.into(), CapabilityKind::Exclusive, self.repacker); } - for rp in target.region_projections(self.repacker) { - state.set_capability( - rp.to_pcg_node(self.repacker), - CapabilityKind::Exclusive, - self.repacker, - ); - } match rvalue { Rvalue::Aggregate( box (AggregateKind::Adt(..) | AggregateKind::Tuple | AggregateKind::Array(..)), @@ -148,26 +137,29 @@ impl<'tcx> BorrowsVisitor<'tcx, '_, '_> { if let ty::TyKind::Ref(target_region, _, _) = target.ty(self.repacker).ty.kind() { - self.apply_action(BorrowPCGAction::add_block_edge( - BlockEdge::new( - smallvec![RegionProjection::new( - (*const_region).into(), - MaybeRemoteRegionProjectionBase::Const(c.const_), - self.repacker, - ) - .unwrap() - .to_pcg_node(self.repacker)], - smallvec![RegionProjection::new( - (*target_region).into(), - target, - self.repacker, + self.apply_action(BorrowPCGAction::add_edge( + BorrowPCGEdge::new( + BlockEdge::new( + smallvec![RegionProjection::new( + (*const_region).into(), + MaybeRemoteRegionProjectionBase::Const(c.const_), + self.repacker, + ) + .unwrap() + .to_pcg_node(self.repacker)], + smallvec![RegionProjection::new( + (*target_region).into(), + target, + self.repacker, + ) + .unwrap() + .into()], + BlockEdgeKind::ConstRef, ) - .unwrap() - .into()], - BlockEdgeKind::ConstRef, + .into(), + PathConditions::AtBlock(location.block), ), - PathConditions::AtBlock(location.block), - "Assign constant", + true, )); } } diff --git a/src/combined_pcs/engine.rs b/src/combined_pcs/engine.rs index 1102adfa..e775da80 100644 --- a/src/combined_pcs/engine.rs +++ b/src/combined_pcs/engine.rs @@ -338,7 +338,7 @@ impl<'a, 'tcx> Analysis<'tcx> for PCGEngine<'a, 'tcx> { crate::free_pcs::CapabilityLocal::Unallocated => {} crate::free_pcs::CapabilityLocal::Allocated(capability_projections) => { for (root, kind) in capability_projections.iter_mut() { - if !borrows.contains((*root).into(), self.cgx.rp) && (kind.is_read() || kind.is_lent_exclusive()) { + if !borrows.contains((*root).into(), self.cgx.rp) && kind.is_read() { *kind = CapabilityKind::Exclusive; } } diff --git a/src/free_pcs/impl/place.rs b/src/free_pcs/impl/place.rs index 23555197..be219ab4 100644 --- a/src/free_pcs/impl/place.rs +++ b/src/free_pcs/impl/place.rs @@ -50,15 +50,6 @@ pub enum CapabilityKind { /// borrowed. Exclusive, - /// This place is mutably borrowed. - Lent, - - /// There are outstanding shared borrows to this place or to projections of - /// this place. The capability to the place can be restored to - /// [`CapabilityKind::Exclusive`] in exchange for the capabilities of its - /// projections when the borrows expire. - LentShared, - /// [`CapabilityKind::Exclusive`] for everything not through a dereference, /// [`CapabilityKind::Write`] for everything through a dereference. ShallowExclusive, @@ -70,8 +61,6 @@ impl Debug for CapabilityKind { CapabilityKind::Write => write!(f, "W"), CapabilityKind::Exclusive => write!(f, "E"), CapabilityKind::ShallowExclusive => write!(f, "e"), - CapabilityKind::Lent => write!(f, "L"), - CapabilityKind::LentShared => write!(f, "Ls"), } } } @@ -86,28 +75,18 @@ impl PartialOrd for CapabilityKind { (CapabilityKind::Exclusive, CapabilityKind::ShallowExclusive) => { Some(Ordering::Greater) } - (CapabilityKind::Exclusive, CapabilityKind::LentShared) => Some(Ordering::Greater), (CapabilityKind::ShallowExclusive, CapabilityKind::Exclusive) => Some(Ordering::Less), - (CapabilityKind::LentShared, CapabilityKind::Exclusive) => Some(Ordering::Less), // ShallowExclusive > Write (CapabilityKind::ShallowExclusive, CapabilityKind::Write) => Some(Ordering::Greater), (CapabilityKind::Write, CapabilityKind::ShallowExclusive) => Some(Ordering::Less), - // LentShared > {Lent, Read} - (CapabilityKind::LentShared, CapabilityKind::Lent) => Some(Ordering::Greater), - (CapabilityKind::LentShared, CapabilityKind::Read) => Some(Ordering::Greater), - (CapabilityKind::Lent, CapabilityKind::LentShared) => Some(Ordering::Less), - (CapabilityKind::Read, CapabilityKind::LentShared) => Some(Ordering::Less), - // Transitive relationships through ShallowExclusive (CapabilityKind::Exclusive, CapabilityKind::Write) => Some(Ordering::Greater), (CapabilityKind::Write, CapabilityKind::Exclusive) => Some(Ordering::Less), // Transitive relationships through LentShared - (CapabilityKind::Exclusive, CapabilityKind::Lent) => Some(Ordering::Greater), (CapabilityKind::Exclusive, CapabilityKind::Read) => Some(Ordering::Greater), - (CapabilityKind::Lent, CapabilityKind::Exclusive) => Some(Ordering::Less), (CapabilityKind::Read, CapabilityKind::Exclusive) => Some(Ordering::Less), // All other pairs are incomparable @@ -120,9 +99,6 @@ impl CapabilityKind { pub fn is_exclusive(self) -> bool { matches!(self, CapabilityKind::Exclusive) } - pub fn is_lent_exclusive(self) -> bool { - matches!(self, CapabilityKind::Lent) - } pub fn is_read(self) -> bool { matches!(self, CapabilityKind::Read) } @@ -162,8 +138,6 @@ mod tests { CapabilityKind::ShallowExclusive, CapabilityKind::Write, CapabilityKind::Read, - CapabilityKind::LentShared, - CapabilityKind::Lent, ]; // Add nodes for cap in caps { @@ -173,10 +147,8 @@ mod tests { // Add edges (a -> b means a is greater than b) let edges = [ (CapabilityKind::Exclusive, CapabilityKind::ShallowExclusive), - (CapabilityKind::Exclusive, CapabilityKind::LentShared), (CapabilityKind::ShallowExclusive, CapabilityKind::Write), - (CapabilityKind::LentShared, CapabilityKind::Lent), - (CapabilityKind::LentShared, CapabilityKind::Read), + (CapabilityKind::Exclusive, CapabilityKind::Read), ]; for (from, to) in edges { diff --git a/src/free_pcs/impl/triple.rs b/src/free_pcs/impl/triple.rs index 6115b072..a9cc3219 100644 --- a/src/free_pcs/impl/triple.rs +++ b/src/free_pcs/impl/triple.rs @@ -57,10 +57,6 @@ impl<'tcx> Condition<'tcx> { Self::new(place, CapabilityKind::Exclusive) } - fn lent>>(place: T) -> Condition<'tcx> { - Self::new(place, CapabilityKind::Lent) - } - fn write>>(place: T) -> Condition<'tcx> { Self::new(place, CapabilityKind::Write) } @@ -215,7 +211,7 @@ impl<'tcx> Visitor<'tcx> for TripleWalker<'tcx> { BorrowKind::Fake(..) => return, BorrowKind::Mut { .. } => Triple { pre: Condition::exclusive(*place), - post: Some(Condition::lent(*place)), + post: None, }, }; self.main_triples.push(triple); From 959bbb638e26c93f8e737b64acdf14f54991fd12 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Mon, 3 Mar 2025 09:31:44 -0800 Subject: [PATCH 13/21] WIP --- src/borrow_pcg/action/actions.rs | 6 +- src/borrow_pcg/action/mod.rs | 126 +++++++++++++++++-------------- src/free_pcs/impl/update.rs | 13 +--- src/free_pcs/results/cursor.rs | 13 ++-- test-files/37_demo_loop.rs | 2 +- test-files/58_aurel_pledge.rs | 2 +- test-files/61_hashset_union.rs | 2 +- 7 files changed, 83 insertions(+), 81 deletions(-) diff --git a/src/borrow_pcg/action/actions.rs b/src/borrow_pcg/action/actions.rs index e9ab0d69..622c4a4b 100644 --- a/src/borrow_pcg/action/actions.rs +++ b/src/borrow_pcg/action/actions.rs @@ -4,7 +4,6 @@ use crate::borrow_pcg::borrow_pcg_expansion::BorrowPCGExpansion; use crate::borrow_pcg::edge::block::BlockEdge; use crate::borrow_pcg::edge::kind::BorrowPCGEdgeKind; use crate::borrow_pcg::graph::Conditioned; -use crate::borrow_pcg::path_condition::PathConditions; use crate::borrow_pcg::unblock_graph::BorrowPCGUnblockAction; use crate::rustc_interface::data_structures::fx::FxHashSet; use crate::utils::json::ToJsonWithRepacker; @@ -74,10 +73,7 @@ impl<'tcx> BorrowPCGActions<'tcx> { self.0 .iter() .filter_map(|action| match action.kind() { - BorrowPCGActionKind::AddEdge { - edge, - .. - } => match edge.kind() { + BorrowPCGActionKind::AddEdge { edge, .. } => match edge.kind() { BorrowPCGEdgeKind::BorrowPCGExpansion(expansion) => Some(Conditioned::new( expansion.clone(), edge.conditions().clone(), diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index c0705b91..031e78c6 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -31,7 +31,9 @@ pub struct BorrowPCGAction<'tcx> { impl<'tcx> BorrowPCGAction<'tcx> { pub(crate) fn debug_line(&self, repacker: PlaceRepacker<'_, 'tcx>) -> String { match &self.kind { - BorrowPCGActionKind::AddEdge { edge, .. } => edge.to_short_string(repacker), + BorrowPCGActionKind::AddEdge { edge, .. } => { + format!("Add Edge: {}", edge.to_short_string(repacker)) + } BorrowPCGActionKind::Weaken(weaken) => weaken.debug_line(repacker), BorrowPCGActionKind::Restore(restore_capability) => { restore_capability.debug_line(repacker) @@ -221,75 +223,85 @@ impl<'tcx> BorrowsState<'tcx> { BorrowPCGActionKind::AddEdge { edge, for_exclusive, - } => { - let mut changed = self.insert(edge.clone()); - match edge.kind { - BorrowPCGEdgeKind::Borrow(borrow_edge) => todo!(), - BorrowPCGEdgeKind::BorrowPCGExpansion(expansion) => { - if changed { - let base = expansion.base(); - let expanded_capability = match &expansion { - BorrowPCGExpansion::FromOwned(expansion_of_owned) => { - match expansion_of_owned.base().ty(repacker).ty.ref_mutability() - { - Some(Mutability::Mut) => CapabilityKind::Exclusive, - Some(Mutability::Not) => CapabilityKind::Read, - None => unreachable!(), - } - } - BorrowPCGExpansion::FromBorrow(expansion_of_borrowed) => { - if let Some(capability) = - self.get_capability(expansion_of_borrowed.base.into()) - { - capability - } else { - // Presumably already expanded in another branch - return Ok(true); - } - } - }; + } => self.handle_add_edge(edge, for_exclusive, repacker)?, + BorrowPCGActionKind::RenamePlace { old, new } => self.rename_place(old, new, repacker), + }; + Ok(result) + } - // If the expansion is a deref of a borrow, its expansion should not - // change the capability to the base. We are allowed to have e.g. exclusive - // permission to `x: &'a mut T` and `*x` simultaneously. Intuitively, `*x` - // gets its permission from `x↓'a`. - if !expansion.is_deref_of_borrow(repacker) { - match expanded_capability { - CapabilityKind::Read => { - _ = self.set_capability( - base.into(), - CapabilityKind::Read, - repacker, - ); - } - _ => { - _ = self.remove_capability(base.into()); - } - } + fn handle_add_edge( + &mut self, + edge: BorrowPCGEdge<'tcx>, + for_exclusive: bool, + repacker: PlaceRepacker<'_, 'tcx>, + ) -> Result { + let changed = self.insert(edge.clone()); + Ok(match edge.kind { + BorrowPCGEdgeKind::Borrow(_) => todo!(), + BorrowPCGEdgeKind::BorrowPCGExpansion(expansion) => { + if changed { + let base = expansion.base(); + let expanded_capability = match &expansion { + BorrowPCGExpansion::FromOwned(expansion_of_owned) => { + match expansion_of_owned.base().ty(repacker).ty.ref_mutability() { + Some(Mutability::Mut) => CapabilityKind::Exclusive, + Some(Mutability::Not) => CapabilityKind::Read, + None => unreachable!(), } + } + BorrowPCGExpansion::FromBorrow(expansion_of_borrowed) => { + if let Some(capability) = + self.get_capability(expansion_of_borrowed.base.into()) + { + capability + } else { + return Ok(true); + } + } + }; - for p in expansion.expansion(repacker)?.iter() { - _ = self.set_capability((*p).into(), expanded_capability, repacker); + // If the expansion is a deref of a borrow, its expansion should not + // change the capability to the base. We are allowed to have e.g. exclusive + // permission to `x: &'a mut T` and `*x` simultaneously. Intuitively, `*x` + // gets its permission from `x↓'a`. + if !expansion.is_deref_of_borrow(repacker) { + match expanded_capability { + CapabilityKind::Read => { + _ = self.set_capability( + base.into(), + CapabilityKind::Read, + repacker, + ); + } + _ => { + _ = self.remove_capability(base.into()); } } - changed } - BorrowPCGEdgeKind::Abstraction(abstraction_type) => { - changed - } - BorrowPCGEdgeKind::Block(block_edge) => { - self.add_block_edge(block_edge, edge.conditions, for_exclusive, repacker) + + for p in expansion.expansion(repacker)?.iter() { + _ = self.set_capability((*p).into(), expanded_capability, repacker); } } + changed } - BorrowPCGActionKind::RenamePlace { old, new } => self.rename_place(old, new, repacker), - }; - Ok(result) + BorrowPCGEdgeKind::Abstraction(_) => changed, + BorrowPCGEdgeKind::Block(block_edge) => { + changed + || self.set_capabilities_for_block_edge( + block_edge, + edge.conditions, + for_exclusive, + repacker, + ) + } + }) } /// Adds a region projection member to the graph and sets appropriate /// capabilities for the place and projection - fn add_block_edge( + #[must_use] + fn set_capabilities_for_block_edge( &mut self, member: BlockEdge<'tcx>, pc: PathConditions, diff --git a/src/free_pcs/impl/update.rs b/src/free_pcs/impl/update.rs index 6b0fa91b..f66dc770 100644 --- a/src/free_pcs/impl/update.rs +++ b/src/free_pcs/impl/update.rs @@ -47,16 +47,7 @@ impl<'tcx> CapabilitySummary<'tcx> { Condition::Capability(place, cap) => { let cp = self[place.local].get_allocated_mut(); cp.repack(place, repacker)?; - if cp[&place].is_exclusive() && cap.is_write() { - // Requires write should deinit an exclusive - cp.insert(place, cap); - } else if cp[&place].is_lent_exclusive() && (cap.is_read() || cap.is_exclusive()) { - // This read will expire the loan, so we regain exclusive access - // TODO: If we expire borrows eagerly, perhaps we don't need this logic - cp.insert(place, CapabilityKind::Exclusive); - } else { - // TODO - } + cp.insert(place, cap); } Condition::Return => { let always_live = repacker.always_live_locals(); @@ -95,7 +86,7 @@ impl<'tcx> CapabilitySummary<'tcx> { } Condition::Capability(place, cap) => { match cap { - CapabilityKind::Read | CapabilityKind::Lent | CapabilityKind::LentShared => { + CapabilityKind::Read => { // TODO } CapabilityKind::Write => { diff --git a/src/free_pcs/results/cursor.rs b/src/free_pcs/results/cursor.rs index bedaa79e..1e71fa2c 100644 --- a/src/free_pcs/results/cursor.rs +++ b/src/free_pcs/results/cursor.rs @@ -7,7 +7,7 @@ use std::rc::Rc; use crate::{ - borrow_pcg::{action::BorrowPCGActionKind, latest::Latest}, + borrow_pcg::{action::BorrowPCGActionKind, borrow_pcg_edge::BorrowPCGEdge, latest::Latest}, combined_pcs::{EvalStmtPhase, PCGEngine, PCGError, PcgSuccessor}, rustc_interface::{ data_structures::fx::FxHashSet, @@ -173,10 +173,13 @@ impl<'mir, 'tcx> FreePcsAnalysis<'mir, 'tcx> { { if !self_abstraction_edges.contains(&abstraction) { actions.push( - BorrowPCGActionKind::AddAbstractionEdge( - abstraction.value.clone(), - abstraction.conditions, - ) + BorrowPCGActionKind::AddEdge { + edge: BorrowPCGEdge::new( + abstraction.value.clone().into(), + abstraction.conditions, + ), + for_exclusive: true, + } .into(), ); } diff --git a/test-files/37_demo_loop.rs b/test-files/37_demo_loop.rs index bcf78e21..65f76356 100644 --- a/test-files/37_demo_loop.rs +++ b/test-files/37_demo_loop.rs @@ -8,7 +8,7 @@ impl List { let mut i = 0; let mut current = self; while i < n { -// PCG: Terminator(bb1): Add Abstraction Edge: Loop(bb1): [Remote(_1)] -> [current↓'?13]; path conditions: bb1 +// PCG: Terminator(bb1): Add Edge: Loop(bb1): [Remote(_1)] -> [current↓'?13] under conditions bb1 // ~PCG: bb1[0] pre_operands: (*_12) at After(bb7[2]) -> current↓'?13 under conditions bb7 -> bb8,bb8 -> bb1, // PCG: bb1[0] pre_operands: Loop(bb1): [Remote(_1)] -> [current↓'?13] under conditions bb1 // PCG: bb1[0] pre_operands: Loop(bb1): [Remote(_1)↓'?11] -> [current↓'?13] under conditions bb1 diff --git a/test-files/58_aurel_pledge.rs b/test-files/58_aurel_pledge.rs index 92fce709..d30d2899 100644 --- a/test-files/58_aurel_pledge.rs +++ b/test-files/58_aurel_pledge.rs @@ -7,7 +7,7 @@ where 'l: 's fn basic_user() { let mut x = 42; let y = basic(&mut x); - // PCG: bb0[8] post_main: Add Abstraction Edge: FunctionCall(DefId(0:3 ~ 58_aurel_pledge[d8c8]::basic), ['?4, '?5]); path conditions: bb0 + // PCG: bb0[8] post_main: Add Edge: FunctionCall(DefId(0:3 ~ 58_aurel_pledge[d8c8]::basic), ['?4, '?5]) under conditions bb0 *y = 72; // PCG: bb1[4] pre_operands: Remove Edge FunctionCall(DefId(0:3 ~ 58_aurel_pledge[d8c8]::basic), ['?4, '?5]) under conditions bb0 -> bb1, drop(x); diff --git a/test-files/61_hashset_union.rs b/test-files/61_hashset_union.rs index 4343967d..7dc194be 100644 --- a/test-files/61_hashset_union.rs +++ b/test-files/61_hashset_union.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; fn union(set: &mut HashSet, other: &HashSet) -> bool { let orig_len = set.len(); - // PCG: bb8[1] post_main: Add Region Projection Member: (_12@Some).0↓'?18 -> el↓'?23; path conditions: bb8 + // PCG: bb8[1] post_main: Add Edge: (_12@Some).0↓'?18 -> el↓'?23 under conditions bb8 for el in other { set.insert(*el); } From c5eb584f5d56492210ef98b12e7c98ae25f78605 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Mon, 3 Mar 2025 09:32:37 -0800 Subject: [PATCH 14/21] WIP --- src/borrow_pcg/state/obtain.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index 9826635e..db241a39 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -81,6 +81,8 @@ impl<'tcx> BorrowsState<'tcx> { if !self.contains(place, repacker) { let extra_acts = self.expand_to(place, repacker, location, obtain_reason)?; actions.extend(extra_acts); + } else { + eprintln!("Already contains {:?}", place); } Ok(actions) From 93d1e03808b2c61c63f1e9b7fac396f10709fe3b Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Mon, 3 Mar 2025 11:22:24 -0800 Subject: [PATCH 15/21] Finish some refactoring --- src/borrow_pcg/action/mod.rs | 15 ++------- src/borrow_pcg/borrow_pcg_capabilities.rs | 9 +++-- src/borrow_pcg/borrow_pcg_edge.rs | 8 ----- src/borrow_pcg/edge/block.rs | 16 +-------- src/borrow_pcg/region_projection.rs | 23 ------------- src/borrow_pcg/state/mod.rs | 40 ++++++++--------------- src/borrow_pcg/state/obtain.rs | 11 ++----- src/borrow_pcg/visitor/mod.rs | 6 ++-- src/utils/place/maybe_old.rs | 14 -------- src/utils/place/maybe_remote.rs | 21 ------------ 10 files changed, 29 insertions(+), 134 deletions(-) diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index 031e78c6..85cee3e8 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -1,13 +1,11 @@ use tracing::instrument; -use super::borrow_pcg_edge::{BorrowPCGEdge, LocalNode, ToBorrowsEdge}; +use super::borrow_pcg_edge::{BorrowPCGEdge, LocalNode}; use super::borrow_pcg_expansion::BorrowPCGExpansion; use super::edge::block::BlockEdge; use super::edge::kind::BorrowPCGEdgeKind; -use super::path_condition::PathConditions; use super::state::BorrowsState; -use crate::borrow_pcg::edge::abstraction::AbstractionType; -use crate::combined_pcs::{PCGError, PCGNode, PCGNodeLike}; +use crate::combined_pcs::{PCGError, PCGNode}; use crate::free_pcs::CapabilityKind; use crate::rustc_interface::{ast::Mutability, middle::mir::Location}; use crate::utils::display::{DebugLines, DisplayWithRepacker}; @@ -287,13 +285,7 @@ impl<'tcx> BorrowsState<'tcx> { } BorrowPCGEdgeKind::Abstraction(_) => changed, BorrowPCGEdgeKind::Block(block_edge) => { - changed - || self.set_capabilities_for_block_edge( - block_edge, - edge.conditions, - for_exclusive, - repacker, - ) + changed || self.set_capabilities_for_block_edge(block_edge, for_exclusive, repacker) } }) } @@ -304,7 +296,6 @@ impl<'tcx> BorrowsState<'tcx> { fn set_capabilities_for_block_edge( &mut self, member: BlockEdge<'tcx>, - pc: PathConditions, for_exclusive: bool, repacker: PlaceRepacker<'_, 'tcx>, ) -> bool { diff --git a/src/borrow_pcg/borrow_pcg_capabilities.rs b/src/borrow_pcg/borrow_pcg_capabilities.rs index a29f075e..0c2ab585 100644 --- a/src/borrow_pcg/borrow_pcg_capabilities.rs +++ b/src/borrow_pcg/borrow_pcg_capabilities.rs @@ -5,11 +5,16 @@ use crate::{ free_pcs::CapabilityKind, rustc_interface::data_structures::fx::FxHashMap, utils::{ - display::{DebugLines, DisplayWithRepacker}, maybe_old::MaybeOldPlace, Place, PlaceRepacker + display::{DebugLines, DisplayWithRepacker}, + maybe_old::MaybeOldPlace, + Place, PlaceRepacker, }, }; -use super::{has_pcs_elem::{HasPcsElems, MakePlaceOld}, latest::Latest}; +use super::{ + has_pcs_elem::{HasPcsElems, MakePlaceOld}, + latest::Latest, +}; /// Tracks the capabilities of places in the borrow PCG. We don't store this /// information in the borrows graph directly to facilitate simpler logic for diff --git a/src/borrow_pcg/borrow_pcg_edge.rs b/src/borrow_pcg/borrow_pcg_edge.rs index e4fbf721..9cd99fc4 100644 --- a/src/borrow_pcg/borrow_pcg_edge.rs +++ b/src/borrow_pcg/borrow_pcg_edge.rs @@ -1,5 +1,4 @@ use rustc_interface::{ - ast::Mutability, data_structures::fx::FxHashSet, middle::mir::{self, BasicBlock, PlaceElem}, }; @@ -268,13 +267,6 @@ impl<'tcx> From> for PCGNode<'tcx> { pub type BlockedNode<'tcx> = PCGNode<'tcx>; impl<'tcx> PCGNode<'tcx> { - pub(crate) fn mutability(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Mutability { - match self { - PCGNode::Place(rp) => rp.mutability(repacker), - PCGNode::RegionProjection(rp) => rp.mutability(repacker), - } - } - pub(crate) fn as_cg_node(self) -> Option> { match self { PCGNode::Place(MaybeRemotePlace::Remote(remote_place)) => { diff --git a/src/borrow_pcg/edge/block.rs b/src/borrow_pcg/edge/block.rs index c6e6a83e..b421838b 100644 --- a/src/borrow_pcg/edge/block.rs +++ b/src/borrow_pcg/edge/block.rs @@ -2,8 +2,7 @@ use serde_json::json; use smallvec::SmallVec; use crate::combined_pcs::PCGNode; -use crate::pcg_validity_assert; -use crate::rustc_interface::{ast::Mutability, data_structures::fx::FxHashSet}; +use crate::rustc_interface::data_structures::fx::FxHashSet; use crate::utils::display::DisplayWithRepacker; use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::validity::HasValidityCheck; @@ -164,19 +163,6 @@ impl<'tcx> HasPcsElems> for BlockEdge<'tcx> { } impl<'tcx> BlockEdge<'tcx> { - /// Returns `true` iff the lifetime projections are mutable - pub(crate) fn mutability(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Mutability { - let mut_values = self - .inputs - .iter() - .map(|p| p.mutability(repacker)) - .collect::>(); - pcg_validity_assert!( - mut_values.windows(2).all(|w| w[0] == w[1]), - "All mutability values must be the same" - ); - mut_values[0] - } pub(crate) fn new( inputs: BlockEdgeInputs<'tcx>, diff --git a/src/borrow_pcg/region_projection.rs b/src/borrow_pcg/region_projection.rs index f9006b6f..8d615867 100644 --- a/src/borrow_pcg/region_projection.rs +++ b/src/borrow_pcg/region_projection.rs @@ -18,7 +18,6 @@ use crate::validity_checks_enabled; use crate::{ combined_pcs::{LocalNodeLike, PCGNode, PCGNodeLike}, rustc_interface::{ - ast::Mutability, data_structures::fx::FxHashSet, index::{Idx, IndexVec}, middle::{ @@ -477,28 +476,6 @@ impl<'tcx> RegionProjection<'tcx> { self.base.as_local_place().map(|p| p.local()) } - /// Returns `true` iff the place is a mutable reference, or if the place is - /// a mutable struct. If the place is a remote place, it is mutable iff the - /// corresponding input argument is a mutable reference. - pub(crate) fn mutability(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Mutability { - let place = match self.base { - MaybeRemoteRegionProjectionBase::Place(p) => p.related_local_place(), - MaybeRemoteRegionProjectionBase::Const(_) => { - return Mutability::Not; - } - }; - place.ref_mutability(repacker).unwrap_or_else(|| { - if let Ok(root_place) = - place.is_mutable(crate::utils::LocalMutationIsAllowed::Yes, repacker) - && root_place.is_local_mutation_allowed == crate::utils::LocalMutationIsAllowed::Yes - { - Mutability::Mut - } else { - Mutability::Not - } - }) - } - fn as_local_region_projection( &self, repacker: PlaceRepacker<'_, 'tcx>, diff --git a/src/borrow_pcg/state/mod.rs b/src/borrow_pcg/state/mod.rs index bbf53aab..c0bb120c 100644 --- a/src/borrow_pcg/state/mod.rs +++ b/src/borrow_pcg/state/mod.rs @@ -5,15 +5,14 @@ use super::{ BlockedNode, BorrowPCGEdge, BorrowPCGEdgeLike, BorrowPCGEdgeRef, LocalNode, ToBorrowsEdge, }, coupling_graph_constructor::BorrowCheckerInterface, - edge::kind::BorrowPCGEdgeKind, graph::{BorrowsGraph, FrozenGraphRef}, latest::Latest, path_condition::{PathCondition, PathConditions}, }; -use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::place::maybe_remote::MaybeRemotePlace; use crate::{borrow_pcg::action::executed_actions::ExecutedActions, combined_pcs::PCGError}; +use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::{ borrow_pcg::edge_data::EdgeData, combined_pcs::{PCGNode, PCGNodeLike}, @@ -236,21 +235,6 @@ impl<'tcx> BorrowsState<'tcx> { changed } - fn min_blocked_by_capability( - &self, - edge: &BorrowPCGEdgeKind<'tcx>, - repacker: PlaceRepacker<'_, 'tcx>, - ) -> Option { - let mut iter = edge.blocked_by_nodes(repacker).into_iter(); - let first_node = iter.next()?; - let mut cap = self.get_capability(first_node.into())?; - for node in iter { - let other_cap = self.get_capability(node.into())?; - cap = cap.minimum(other_cap)?; - } - Some(cap) - } - pub(super) fn remove_edge_and_set_latest( &mut self, edge: impl BorrowPCGEdgeLike<'tcx>, @@ -286,16 +270,18 @@ impl<'tcx> BorrowsState<'tcx> { if let Some(local_node) = node.as_local_node(repacker) { let blocked_cap = self.get_capability(node); - let restore_cap = self.min_blocked_by_capability(edge.kind(), repacker); + let restore_cap = if local_node.place().projects_shared_ref(repacker) { + CapabilityKind::Read + } else { + CapabilityKind::Exclusive + }; - if let Some(restore_cap) = restore_cap { - if blocked_cap.is_none_or(|bc| bc < restore_cap) { - self.record_and_apply_action( - BorrowPCGAction::restore_capability(local_node, restore_cap), - &mut actions, - repacker, - )?; - } + if blocked_cap.is_none_or(|bc| bc < restore_cap) { + self.record_and_apply_action( + BorrowPCGAction::restore_capability(local_node, restore_cap), + &mut actions, + repacker, + )?; } } } @@ -493,7 +479,7 @@ impl<'tcx> BorrowsState<'tcx> { BorrowKind::Mut { kind: MutBorrowKind::Default, } => { - self.remove_capability(blocked_place.into()); + let _ = self.remove_capability(blocked_place.into()); } _ => { match self.get_capability(blocked_place.into()) { diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index db241a39..904a419c 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -79,10 +79,8 @@ impl<'tcx> BorrowsState<'tcx> { } if !self.contains(place, repacker) { - let extra_acts = self.expand_to(place, repacker, location, obtain_reason)?; + let extra_acts = self.expand_to(place, repacker, location)?; actions.extend(extra_acts); - } else { - eprintln!("Already contains {:?}", place); } Ok(actions) @@ -189,7 +187,6 @@ impl<'tcx> BorrowsState<'tcx> { to_place: Place<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, location: Location, - obtain_reason: ObtainReason, ) -> Result, PCGError> { let mut actions = ExecutedActions::new(); @@ -236,8 +233,7 @@ impl<'tcx> BorrowsState<'tcx> { BorrowPCGEdge::new( BorrowPCGEdgeKind::BorrowPCGExpansion(expansion), PathConditions::new(location.block), - ) - .into(), + ), true, ); self.record_and_apply_action(action, &mut actions, repacker)?; @@ -264,8 +260,7 @@ impl<'tcx> BorrowsState<'tcx> { BorrowPCGEdge::new( BorrowPCGEdgeKind::BorrowPCGExpansion(expansion), PathConditions::new(location.block), - ) - .into(), + ), true, ), &mut actions, diff --git a/src/borrow_pcg/visitor/mod.rs b/src/borrow_pcg/visitor/mod.rs index 0a45b3ae..bb38b011 100644 --- a/src/borrow_pcg/visitor/mod.rs +++ b/src/borrow_pcg/visitor/mod.rs @@ -22,7 +22,7 @@ use crate::{ }; use super::{ - action::{BorrowPCGAction, BorrowPCGActionKind}, + action::BorrowPCGAction, borrow_pcg_edge::BorrowPCGEdge, coupling_graph_constructor::BorrowCheckerInterface, edge::block::BlockEdge, @@ -166,8 +166,7 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { ) .into(), PathConditions::AtBlock(location.block), - ) - .into(), + ), true, )); } @@ -262,7 +261,6 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { BorrowPCGEdge::new( AbstractionType::FunctionCall( FunctionCallAbstraction::new(location, *func_def_id, substs, edges.clone()) - .into(), ) .into(), PathConditions::AtBlock(location.block), diff --git a/src/utils/place/maybe_old.rs b/src/utils/place/maybe_old.rs index 0802dc29..c7b8f678 100644 --- a/src/utils/place/maybe_old.rs +++ b/src/utils/place/maybe_old.rs @@ -7,7 +7,6 @@ use crate::borrow_pcg::region_projection::{ }; use crate::borrow_pcg::visitor::extract_regions; use crate::combined_pcs::{LocalNodeLike, PCGError, PCGNode, PCGNodeLike}; -use crate::rustc_interface::ast::Mutability; use crate::rustc_interface::index::IndexVec; use crate::rustc_interface::middle::mir; use crate::rustc_interface::middle::mir::tcx::PlaceTy; @@ -224,19 +223,6 @@ impl<'tcx> MaybeOldPlace<'tcx> { .map(|rp| rp.set_base(rp.base.into(), repacker)) } - pub(crate) fn mutability(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Mutability { - let place: Place<'_> = self.place(); - place.ref_mutability(repacker).unwrap_or_else(|| { - if let Ok(root_place) = - place.is_mutable(crate::utils::LocalMutationIsAllowed::Yes, repacker) - && root_place.is_local_mutation_allowed == crate::utils::LocalMutationIsAllowed::Yes - { - Mutability::Mut - } else { - Mutability::Not - } - }) - } pub(crate) fn is_owned(&self, repacker: PlaceRepacker<'_, 'tcx>) -> bool { self.place().is_owned(repacker) } diff --git a/src/utils/place/maybe_remote.rs b/src/utils/place/maybe_remote.rs index d7439214..1ecf4b86 100644 --- a/src/utils/place/maybe_remote.rs +++ b/src/utils/place/maybe_remote.rs @@ -3,7 +3,6 @@ use crate::borrow_pcg::region_projection::{ MaybeRemoteRegionProjectionBase, PCGRegion, RegionIdx, RegionProjectionBaseLike, }; use crate::combined_pcs::{PCGNode, PCGNodeLike}; -use crate::rustc_interface::ast::Mutability; use crate::rustc_interface::index::IndexVec; use crate::rustc_interface::middle::{mir, ty}; use crate::utils::display::DisplayWithRepacker; @@ -86,12 +85,6 @@ impl std::fmt::Display for MaybeRemotePlace<'_> { } impl<'tcx> MaybeRemotePlace<'tcx> { - pub(crate) fn mutability(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Mutability { - match self { - MaybeRemotePlace::Local(p) => p.mutability(repacker), - MaybeRemotePlace::Remote(rp) => rp.mutability(repacker), - } - } pub fn place_assigned_to_local(local: mir::Local) -> Self { MaybeRemotePlace::Remote(RemotePlace { local }) @@ -184,18 +177,4 @@ impl RemotePlace { _ => todo!(), } } - - pub(crate) fn mutability(&self, repacker: PlaceRepacker<'_, '_>) -> Mutability { - let place: Place<'_> = self.local.into(); - place.ref_mutability(repacker).unwrap_or_else(|| { - if let Ok(root_place) = - place.is_mutable(crate::utils::LocalMutationIsAllowed::Yes, repacker) - && root_place.is_local_mutation_allowed == crate::utils::LocalMutationIsAllowed::Yes - { - Mutability::Mut - } else { - Mutability::Not - } - }) - } } From 4425ace28aa39ed50578bc7e4c0a0014bfa21144 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Mon, 3 Mar 2025 12:02:48 -0800 Subject: [PATCH 16/21] Change representation of abstraction edges --- src/borrow_pcg/edge/abstraction.rs | 54 +++++++++-------------- src/borrow_pcg/graph/aliases.rs | 4 +- src/borrow_pcg/state/mod.rs | 23 +++++----- src/borrow_pcg/visitor/mod.rs | 31 ++++++------- src/visualization/graph_constructor.rs | 61 +++++++++++++------------- 5 files changed, 77 insertions(+), 96 deletions(-) diff --git a/src/borrow_pcg/edge/abstraction.rs b/src/borrow_pcg/edge/abstraction.rs index b3ee68d6..369e03b0 100644 --- a/src/borrow_pcg/edge/abstraction.rs +++ b/src/borrow_pcg/edge/abstraction.rs @@ -83,15 +83,12 @@ pub struct FunctionCallAbstraction<'tcx> { location: Location, def_id: DefId, substs: GenericArgsRef<'tcx>, - edges: Vec>, + edge: AbstractionBlockEdge<'tcx>, } impl<'tcx> HasValidityCheck<'tcx> for FunctionCallAbstraction<'tcx> { fn check_validity(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Result<(), String> { - for edge in self.edges.iter() { - edge.check_validity(repacker)?; - } - Ok(()) + self.edge.check_validity(repacker) } } @@ -106,10 +103,7 @@ where AbstractionBlockEdge<'tcx>: HasPcsElems, { fn pcs_elems(&mut self) -> Vec<&mut T> { - self.edges - .iter_mut() - .flat_map(|edge| edge.pcs_elems()) - .collect() + self.edge.pcs_elems() } } @@ -125,22 +119,21 @@ impl<'tcx> FunctionCallAbstraction<'tcx> { self.location } - pub fn edges(&self) -> &Vec> { - &self.edges + pub fn edge(&self) -> &AbstractionBlockEdge<'tcx> { + &self.edge } pub fn new( location: Location, def_id: DefId, substs: GenericArgsRef<'tcx>, - edges: Vec>, + edge: AbstractionBlockEdge<'tcx>, ) -> Self { - assert!(!edges.is_empty()); Self { location, def_id, substs, - edges, + edge, } } } @@ -309,6 +302,11 @@ impl<'tcx> AbstractionType<'tcx> { pub(crate) fn is_function_call(&self) -> bool { matches!(self, AbstractionType::FunctionCall(_)) } + + pub(crate) fn is_loop(&self) -> bool { + matches!(self, AbstractionType::Loop(_)) + } + pub fn location(&self) -> Location { match self { AbstractionType::FunctionCall(c) => c.location, @@ -316,34 +314,22 @@ impl<'tcx> AbstractionType<'tcx> { } } - pub fn inputs(&self) -> Vec> { - self.edges() - .into_iter() - .flat_map(|edge| edge.inputs()) - .collect() + pub fn inputs(&self) -> BTreeSet> { + self.edge().inputs() } - pub fn outputs(&self) -> Vec> { - self.edges() - .into_iter() - .flat_map(|edge| edge.outputs()) - .collect() + pub fn outputs(&self) -> BTreeSet> { + self.edge().outputs() } - pub fn edges(&self) -> Vec> { + pub fn edge(&self) -> &AbstractionBlockEdge<'tcx> { match self { - AbstractionType::FunctionCall(c) => c.edges.clone(), - AbstractionType::Loop(c) => vec![c.edge.clone()], + AbstractionType::FunctionCall(c) => &c.edge, + AbstractionType::Loop(c) => &c.edge, } } pub fn has_input(&self, node: AbstractionInputTarget<'tcx>) -> bool { - match self { - AbstractionType::FunctionCall(function_call_abstraction) => function_call_abstraction - .edges() - .iter() - .any(|edge| edge.inputs.contains(&node)), - AbstractionType::Loop(loop_abstraction) => loop_abstraction.edge.inputs.contains(&node), - } + self.inputs().contains(&node) } } diff --git a/src/borrow_pcg/graph/aliases.rs b/src/borrow_pcg/graph/aliases.rs index b6c0304a..66048f43 100644 --- a/src/borrow_pcg/graph/aliases.rs +++ b/src/borrow_pcg/graph/aliases.rs @@ -143,11 +143,9 @@ impl<'tcx> BorrowsGraph<'tcx> { } }, BorrowPCGEdgeKind::Abstraction(abstraction_type) => { - for edge in abstraction_type.edges() { - for input in edge.inputs() { + for input in abstraction_type.inputs() { extend(input.to_pcg_node(repacker), seen, &mut result, false); } - } } BorrowPCGEdgeKind::Block(region_projection_member) => { match ®ion_projection_member.kind { diff --git a/src/borrow_pcg/state/mod.rs b/src/borrow_pcg/state/mod.rs index c0bb120c..6c5ac75a 100644 --- a/src/borrow_pcg/state/mod.rs +++ b/src/borrow_pcg/state/mod.rs @@ -9,10 +9,10 @@ use super::{ latest::Latest, path_condition::{PathCondition, PathConditions}, }; +use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::place::maybe_remote::MaybeRemotePlace; use crate::{borrow_pcg::action::executed_actions::ExecutedActions, combined_pcs::PCGError}; -use crate::borrow_pcg::edge::borrow::BorrowEdge; use crate::{ borrow_pcg::edge_data::EdgeData, combined_pcs::{PCGNode, PCGNodeLike}, @@ -243,17 +243,16 @@ impl<'tcx> BorrowsState<'tcx> { context: &str, ) -> Result, PCGError> { let mut actions = ExecutedActions::new(); - if !edge.is_shared_borrow() { - for place in edge.blocked_places(repacker) { - if let Some(place) = place.as_current_place() { - if place.has_location_dependent_value(repacker) { - self.record_and_apply_action( - BorrowPCGAction::set_latest(place, location, context), - &mut actions, - repacker, - )?; - } - } + for place in edge.blocked_places(repacker) { + if self.get_capability(place.into()) != Some(CapabilityKind::Read) + && let Some(place) = place.as_current_place() + && place.has_location_dependent_value(repacker) + { + self.record_and_apply_action( + BorrowPCGAction::set_latest(place, location, context), + &mut actions, + repacker, + )?; } } let remove_edge_action = diff --git a/src/borrow_pcg/visitor/mod.rs b/src/borrow_pcg/visitor/mod.rs index bb38b011..a725ef75 100644 --- a/src/borrow_pcg/visitor/mod.rs +++ b/src/borrow_pcg/visitor/mod.rs @@ -223,8 +223,6 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { return; } - let mut edges = vec![]; - for arg in args.iter() { let input_place: utils::Place<'tcx> = match arg.place() { Some(place) => place.into(), @@ -247,27 +245,26 @@ impl<'tcx, 'mir, 'state> BorrowsVisitor<'tcx, 'mir, 'state> { } let input_rp = input_place.region_projection(lifetime_idx, self.repacker); - edges.push(AbstractionBlockEdge::new( + let block_edge = AbstractionBlockEdge::new( vec![input_rp.into()].into_iter().collect(), vec![output].into_iter().collect(), + ); + self.apply_action(BorrowPCGAction::add_edge( + BorrowPCGEdge::new( + AbstractionType::FunctionCall(FunctionCallAbstraction::new( + location, + *func_def_id, + substs, + block_edge, + )) + .into(), + PathConditions::AtBlock(location.block), + ), + true, )); } } } - - // No edges may be added e.g. if the inputs do not contain any borrows - if !edges.is_empty() { - self.apply_action(BorrowPCGAction::add_edge( - BorrowPCGEdge::new( - AbstractionType::FunctionCall( - FunctionCallAbstraction::new(location, *func_def_id, substs, edges.clone()) - ) - .into(), - PathConditions::AtBlock(location.block), - ), - true, - )); - } } fn projections_borrowing_from_input_lifetime( diff --git a/src/visualization/graph_constructor.rs b/src/visualization/graph_constructor.rs index 3c777c10..021e8955 100644 --- a/src/visualization/graph_constructor.rs +++ b/src/visualization/graph_constructor.rs @@ -163,41 +163,42 @@ impl<'a, 'tcx> GraphConstructor<'a, 'tcx> { fn insert_abstraction( &mut self, - region_abstraction: &AbstractionType<'tcx>, + abstraction: &AbstractionType<'tcx>, capabilities: &impl CapabilityGetter<'tcx>, ) { let mut input_nodes = BTreeSet::new(); let mut output_nodes = BTreeSet::new(); - for edge in region_abstraction.edges() { - for input in edge.inputs() { - let input = self.insert_cg_node(input, capabilities.get(input.into())); - input_nodes.insert(input); - } - for output in edge.outputs() { - let output = output.set_base(output.base.into(), self.repacker); - let output = - self.insert_region_projection_node(output, capabilities.get(output.into())); - output_nodes.insert(output); - } - for input in &input_nodes { - for output in &output_nodes { - self.edges.insert(GraphEdge::Abstract { - blocked: *input, - blocking: *output, - }); - } + for input in abstraction.inputs() { + let input = self.insert_cg_node(input, capabilities.get(input.into())); + input_nodes.insert(input); + } + + for output in abstraction.outputs() { + let output = output.set_base(output.base.into(), self.repacker); + let output = + self.insert_region_projection_node(output, capabilities.get(output.into())); + output_nodes.insert(output); + } + + for input in &input_nodes { + for output in &output_nodes { + self.edges.insert(GraphEdge::Abstract { + blocked: *input, + blocking: *output, + }); } } - // Add undirected edges between all outputs - for output1 in output_nodes.iter() { - for output2 in output_nodes.iter() { - if output1 < output2 { - self.edges.insert(GraphEdge::Coupled { - source: *output1, - target: *output2, - }); + if abstraction.is_loop() { + for output1 in output_nodes.iter() { + for output2 in output_nodes.iter() { + if output1 < output2 { + self.edges.insert(GraphEdge::Coupled { + source: *output1, + target: *output2, + }); + } } } } @@ -206,10 +207,10 @@ impl<'a, 'tcx> GraphConstructor<'a, 'tcx> { let cluster = GraphCluster { id: format!( "c{:?}_{}", - region_abstraction.location().block, - region_abstraction.location().statement_index + abstraction.location().block, + abstraction.location().statement_index ), - label: format!("{:?}", region_abstraction.location()), + label: format!("{:?}", abstraction.location()), nodes: input_nodes .iter() .chain(output_nodes.iter()) From 70e1f9bb86adc79cfa8b67d3c3e74b733ad2d889 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Mon, 3 Mar 2025 12:23:17 -0800 Subject: [PATCH 17/21] Update bench results --- benchmark_results.txt | 156 ++++++++++++++++++++++-------------------- 1 file changed, 80 insertions(+), 76 deletions(-) diff --git a/benchmark_results.txt b/benchmark_results.txt index ebacbdf3..4de4a83c 100644 --- a/benchmark_results.txt +++ b/benchmark_results.txt @@ -1,76 +1,80 @@ -01_basic.rs: 34968039 -02_list_zero.rs: 70053253 -03_unnest.rs: 43597929 -04_chunks.rs: 176964110 -05_is_xid_start.rs: 157608674 -06_cursor.rs: 82603109 -07_fmt.rs: 113893560 -08_parse_parens.rs: 53412847 -09_punct.rs: 230866236 -10_members.rs: 52154913 -11_display.rs: 66995244 -12_jasper.rs: 72964745 -13_heap_visitor.rs: 45534187 -14_serde_err.rs: 1177956222 -15_bernoulli.rs: 87360186 -16_lookahead.rs: 47786189 -17_entry.rs: 69264999 -18_slice.rs: 73555243 -19_lifetime_projection.rs: 44045847 -20_eager.rs: 40610549 -21_jasper2.rs: 56334749 -22_itoa_format.rs: 60404888 -23_lazy_poll.rs: 62278649 -24_style_render_reset.rs: 130708067 -25_parse_str.rs: 49090477 -26_ref_in_struct.rs: 49707914 -27_aurel.rs: 41897423 -28_ranged_slice.rs: 69415629 -29_prusti_issue_25.rs: 53350295 -30_prusti_issue_738-5.rs: 46353602 -31_prusti_timeout.rs: 89731326 -32_deflate64_inputbuffer_advance.rs: 72150609 -33_hashtable_shrink_to_fit.rs: 52780759 -34_pair_into_value.rs: 28661275 -35_miniz_oxide_new_output_buffer.rs: 172834003 -36_gimli_endian_slice.rs: 175436046 -37_demo_loop.rs: 100616823 -38_regex_autonoma_iter_pattern_match_ids.rs: 92853701 -39_cfi.rs: 94986175 -40_object_archive.rs: 214359685 -41_itertools_k_smallest.rs: 123396982 -42_dfa_dense.rs: 1831140835 -43_der_length.rs: 108278567 -44_rand_weighted_index.rs: 1484102108 -45_closure.rs: 42535919 -46_demo_cond_mut_borrow.rs: 47288702 -47_demo_choose.rs: 42442155 -48_flowistry_basic.rs: 47176136 -49_flowistry_recurse.rs: 29545326 -50_flowistry_pointer_deep.rs: 45827808 -51_flowistry_recurse_not_all_args.rs: 47886235 -52_flowistry_recurse_parent_privacy.rs: 47916918 -53_flowistry_time_calculation.rs: 60403002 -54_flowistry_struct_mut_ptr.rs: 47053017 -55_prusti_ownership2.rs: 62021458 -56_deep2.rs: 43843116 -57_slice_ptr_elem_write.rs: 44741017 -58_aurel_pledge.rs: 48160151 -59_struct_ptrs_deep.rs: 67423773 -60_pointer_reborrow_nested.rs: 45327225 -61_hashset_union.rs: 92624936 -62_tuple_ptr_write_field.rs: 46632674 -63_flowistry_function_lifetime_alias_mut.rs: 42616510 -64_flowistry_enum_write_branch_read_whole.rs: 52566418 -65_flowistry_recurse_project_dst.rs: 29401200 -66_polonius_borrow_cycle.rs: 68185242 -67_proc_macro2_trailing_backslash.rs: 195056034 -68_miniz_push_dict_out.rs: 146766404 -69_http_header_map.rs: 303860359 -70_tonic_decode_chunk.rs: 178031184 -71_serde_with_deserialize.rs: 73795834 -72_flowistry_enum_write_branch_read_branch.rs: 53696963 -73_flowistry_aliases_basic.rs: 47139640 -74_aliases_projection.rs: 66027574 -75_flowistry_recurse_simple.rs: 43665578 -76_slice_write.rs: 62143206 +01_basic.rs: 33592205 +02_list_zero.rs: 69597752 +03_unnest.rs: 42278965 +04_chunks.rs: 278769167 +05_is_xid_start.rs: 156335460 +06_cursor.rs: 84804190 +07_fmt.rs: 130456216 +08_parse_parens.rs: 52748098 +09_punct.rs: 315849070 +10_members.rs: 51212674 +11_display.rs: 65561249 +12_jasper.rs: 71560828 +13_heap_visitor.rs: 44142817 +14_serde_err.rs: 1382095214 +15_bernoulli.rs: 85770215 +16_lookahead.rs: 47002588 +17_entry.rs: 68801720 +18_slice.rs: 93160688 +19_lifetime_projection.rs: 42700710 +20_eager.rs: 39297543 +21_jasper2.rs: 54834147 +22_itoa_format.rs: 59049771 +23_lazy_poll.rs: 60992136 +24_style_render_reset.rs: 129773514 +25_parse_str.rs: 47781393 +26_ref_in_struct.rs: 48195606 +27_aurel.rs: 40614823 +28_ranged_slice.rs: 67820050 +29_prusti_issue_25.rs: 51702585 +30_prusti_issue_738-5.rs: 44922541 +31_prusti_timeout.rs: 88323218 +32_deflate64_inputbuffer_advance.rs: 71718403 +33_hashtable_shrink_to_fit.rs: 51452994 +34_pair_into_value.rs: 27341778 +35_miniz_oxide_new_output_buffer.rs: 183722867 +36_gimli_endian_slice.rs: 184775878 +37_demo_loop.rs: 101000408 +38_regex_autonoma_iter_pattern_match_ids.rs: 115099620 +39_cfi.rs: 97415290 +40_object_archive.rs: 210389480 +41_itertools_k_smallest.rs: 138570314 +42_dfa_dense.rs: 3237520682 +43_der_length.rs: 106437370 +44_rand_weighted_index.rs: 5821986260 +45_closure.rs: 41287801 +46_demo_cond_mut_borrow.rs: 45889648 +47_demo_choose.rs: 42798929 +48_flowistry_basic.rs: 46241722 +49_flowistry_recurse.rs: 28163412 +50_flowistry_pointer_deep.rs: 44441482 +51_flowistry_recurse_not_all_args.rs: 46452142 +52_flowistry_recurse_parent_privacy.rs: 46516496 +53_flowistry_time_calculation.rs: 70900133 +54_flowistry_struct_mut_ptr.rs: 45608046 +55_prusti_ownership2.rs: 60621580 +56_deep2.rs: 42460608 +57_slice_ptr_elem_write.rs: 43348943 +58_aurel_pledge.rs: 47029848 +59_struct_ptrs_deep.rs: 65808800 +60_pointer_reborrow_nested.rs: 43924419 +61_hashset_union.rs: 113164584 +62_tuple_ptr_write_field.rs: 45258768 +63_flowistry_function_lifetime_alias_mut.rs: 41314957 +64_flowistry_enum_write_branch_read_whole.rs: 50954487 +65_flowistry_recurse_project_dst.rs: 28017171 +66_polonius_borrow_cycle.rs: 66464337 +67_proc_macro2_trailing_backslash.rs: 282975580 +68_miniz_push_dict_out.rs: 157409974 +69_http_header_map.rs: 411277516 +70_tonic_decode_chunk.rs: 178938245 +71_serde_with_deserialize.rs: 76144256 +72_flowistry_enum_write_branch_read_branch.rs: 52160257 +73_flowistry_aliases_basic.rs: 46202084 +74_aliases_projection.rs: 66075982 +75_flowistry_recurse_simple.rs: 42256545 +76_slice_write.rs: 61342614 +77_flowistry_interior_mutability_not_observable.rs: 65683636 +78_interior_mutability_observable.rs: 46875188 +79_add_overflow.rs: 69546402 +80_crc_bytewise.rs: 211820594 From 6017ad1723fc2dff92a920b7d84e16fb77c33e74 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Mon, 3 Mar 2025 16:50:00 -0800 Subject: [PATCH 18/21] WIP --- src/borrow_pcg/action/mod.rs | 25 ++- src/borrow_pcg/state/obtain.rs | 21 +- src/combined_pcs/engine.rs | 24 ++- src/free_pcs/impl/bridge.rs | 30 ++- src/free_pcs/impl/join_semi_lattice.rs | 1 + src/free_pcs/impl/local.rs | 258 +++++++++++++++++++++---- src/free_pcs/impl/place.rs | 28 ++- src/free_pcs/impl/update.rs | 22 ++- src/utils/repacker.rs | 110 ++++++++--- src/utils/validity.rs | 8 +- test-files/81_shared_ref.rs | 17 ++ 11 files changed, 436 insertions(+), 108 deletions(-) create mode 100644 test-files/81_shared_ref.rs diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index 85cee3e8..d0d5a324 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -233,7 +233,12 @@ impl<'tcx> BorrowsState<'tcx> { for_exclusive: bool, repacker: PlaceRepacker<'_, 'tcx>, ) -> Result { - let changed = self.insert(edge.clone()); + let mut changed = self.insert(edge.clone()); + eprintln!( + "Add edge {:?} (for exclusive: {})", + edge.to_short_string(repacker), + for_exclusive + ); Ok(match edge.kind { BorrowPCGEdgeKind::Borrow(_) => todo!(), BorrowPCGEdgeKind::BorrowPCGExpansion(expansion) => { @@ -263,22 +268,16 @@ impl<'tcx> BorrowsState<'tcx> { // permission to `x: &'a mut T` and `*x` simultaneously. Intuitively, `*x` // gets its permission from `x↓'a`. if !expansion.is_deref_of_borrow(repacker) { - match expanded_capability { - CapabilityKind::Read => { - _ = self.set_capability( - base.into(), - CapabilityKind::Read, - repacker, - ); - } - _ => { - _ = self.remove_capability(base.into()); - } + if for_exclusive { + changed |= self.remove_capability(base.into()); + } else { + changed |= + self.set_capability(base.into(), CapabilityKind::Read, repacker); } } for p in expansion.expansion(repacker)?.iter() { - _ = self.set_capability((*p).into(), expanded_capability, repacker); + changed |= self.set_capability((*p).into(), expanded_capability, repacker); } } changed diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index 904a419c..f1bad1a8 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -13,6 +13,7 @@ use crate::combined_pcs::{PCGError, PCGNodeLike}; use crate::free_pcs::CapabilityKind; use crate::rustc_interface::middle::mir::{BorrowKind, Location, MutBorrowKind}; use crate::rustc_interface::middle::ty::{self, Mutability}; +use crate::utils::display::DisplayWithRepacker; use crate::utils::maybe_old::MaybeOldPlace; use crate::utils::{Place, PlaceRepacker}; use crate::visualization::dot_graph::DotGraph; @@ -79,7 +80,7 @@ impl<'tcx> BorrowsState<'tcx> { } if !self.contains(place, repacker) { - let extra_acts = self.expand_to(place, repacker, location)?; + let extra_acts = self.expand_to(place, repacker, obtain_reason, location)?; actions.extend(extra_acts); } @@ -186,15 +187,23 @@ impl<'tcx> BorrowsState<'tcx> { &mut self, to_place: Place<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, + obtain_reason: ObtainReason, location: Location, ) -> Result, PCGError> { + eprintln!( + "Expanding to {} because of {:?}", + to_place.to_short_string(repacker), + obtain_reason + ); + let for_exclusive = obtain_reason.min_post_obtain_capability() != CapabilityKind::Read; let mut actions = ExecutedActions::new(); for (base, _) in to_place.iter_projections() { let base: Place<'tcx> = base.into(); let base = base.with_inherent_region(repacker); - let (target, mut expansion, kind) = base.expand_one_level(to_place, repacker)?; - kind.insert_target_into_expansion(target, &mut expansion); + let expand_result = base.expand_one_level(to_place, repacker)?; + let expansion = expand_result.expansion(); + let target = expand_result.target_place; if let ty::TyKind::Ref(region, _, _) = base.ty(repacker).ty.kind() { // This is a dereference, taking the the form t -> *t where t @@ -213,7 +222,7 @@ impl<'tcx> BorrowsState<'tcx> { self.record_and_apply_action( BorrowPCGAction::add_edge( BorrowPCGEdge::new(block_edge.into(), PathConditions::new(location.block)), - true, + for_exclusive, ), &mut actions, repacker, @@ -234,7 +243,7 @@ impl<'tcx> BorrowsState<'tcx> { BorrowPCGEdgeKind::BorrowPCGExpansion(expansion), PathConditions::new(location.block), ), - true, + for_exclusive, ); self.record_and_apply_action(action, &mut actions, repacker)?; } @@ -261,7 +270,7 @@ impl<'tcx> BorrowsState<'tcx> { BorrowPCGEdgeKind::BorrowPCGExpansion(expansion), PathConditions::new(location.block), ), - true, + for_exclusive, ), &mut actions, repacker, diff --git a/src/combined_pcs/engine.rs b/src/combined_pcs/engine.rs index e775da80..28bbccb3 100644 --- a/src/combined_pcs/engine.rs +++ b/src/combined_pcs/engine.rs @@ -26,18 +26,19 @@ use crate::{ ty::{self, GenericArgsRef, TyCtxt}, }, }, + utils::Place, BodyAndBorrows, }; +use super::{ + domain::PlaceCapabilitySummary, DataflowStmtPhase, DotGraphs, EvalStmtPhase, PCGDebugData, +}; +use crate::borrow_pcg::borrow_checker::r#impl::BorrowCheckerImpl; use crate::{ borrow_pcg::engine::BorrowsEngine, free_pcs::{engine::FpcsEngine, CapabilityKind}, utils::PlaceRepacker, }; -use crate::borrow_pcg::borrow_checker::r#impl::BorrowCheckerImpl; -use super::{ - domain::PlaceCapabilitySummary, DataflowStmtPhase, DotGraphs, EvalStmtPhase, PCGDebugData, -}; #[rustversion::since(2024-10-03)] type OutputFacts = Box; @@ -199,9 +200,7 @@ impl<'a, 'tcx> PCGEngine<'a, 'tcx> { } result } - TerminatorEdges::SwitchInt { targets, .. } => { - targets.all_targets().to_vec() - } + TerminatorEdges::SwitchInt { targets, .. } => targets.all_targets().to_vec(), } } @@ -337,9 +336,14 @@ impl<'a, 'tcx> Analysis<'tcx> for PCGEngine<'a, 'tcx> { match cap { crate::free_pcs::CapabilityLocal::Unallocated => {} crate::free_pcs::CapabilityLocal::Allocated(capability_projections) => { - for (root, kind) in capability_projections.iter_mut() { - if !borrows.contains((*root).into(), self.cgx.rp) && kind.is_read() { - *kind = CapabilityKind::Exclusive; + let places: Vec> = + capability_projections.iter().map(|(p, _)| *p).collect(); + for p in places { + if !borrows.contains(p.into(), self.cgx.rp) + && capability_projections[&p].is_read() + && capability_projections.is_leaf(p) + { + capability_projections.insert(p, CapabilityKind::Exclusive); } } } diff --git a/src/free_pcs/impl/bridge.rs b/src/free_pcs/impl/bridge.rs index f186a7bc..9215fba5 100644 --- a/src/free_pcs/impl/bridge.rs +++ b/src/free_pcs/impl/bridge.rs @@ -4,12 +4,16 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use itertools::Itertools; + use crate::{ combined_pcs::PCGError, free_pcs::{ CapabilityKind, CapabilityLocal, CapabilityProjections, CapabilitySummary, RepackOp, }, - utils::{corrected::CorrectedPlace, PlaceOrdering, PlaceRepacker}, + utils::{ + corrected::CorrectedPlace, display::DisplayWithRepacker, PlaceOrdering, PlaceRepacker, + }, }; pub trait RepackingBridgeSemiLattice<'tcx> { @@ -57,7 +61,7 @@ impl<'tcx> RepackingBridgeSemiLattice<'tcx> for CapabilityLocal<'tcx> { let mut cps = cps.clone(); let local = cps.get_local(); let mut repacks = Vec::new(); - for (&p, k) in cps.iter_mut() { + for (p, mut k) in cps.iter_mut() { if *k > CapabilityKind::Write { repacks.push(RepackOp::Weaken(p, *k, CapabilityKind::Write)); *k = CapabilityKind::Write; @@ -91,15 +95,23 @@ impl<'tcx> RepackingBridgeSemiLattice<'tcx> for CapabilityProjections<'tcx> { ) -> std::result::Result>, PCGError> { // TODO: remove need for clone let mut from = self.clone(); + eprintln!("From initial: {:?}", from); let mut repacks = Vec::new(); - for (&place, &kind) in &**other { + for (&place, &kind) in other.iter().sorted_by_key(|(p, _)| p.projection.len()) { + eprintln!("Place: {:?}, Kind: {:?}", place, kind); let place = CorrectedPlace::new(place, repacker); let related = from.find_all_related(*place, None); + if let Some(c) = from.get(&place) + && *c == kind + { + continue; + } for (from_place, _) in (*related).iter().copied() { match from_place.partial_cmp(*place).unwrap() { PlaceOrdering::Prefix => { - let unpacks = from.expand(from_place, place, repacker)?; + let unpacks = + from.expand(from_place, place, CapabilityKind::Exclusive, repacker)?; repacks.extend(unpacks); break; } @@ -114,7 +126,8 @@ impl<'tcx> RepackingBridgeSemiLattice<'tcx> for CapabilityProjections<'tcx> { let common_prefix = related.common_prefix(*place); let collapse_repacks = from.collapse(related.get_places(), common_prefix, repacker)?; - let expand_repacks = from.expand(common_prefix, place, repacker)?; + let expand_repacks = + from.expand(common_prefix, place, CapabilityKind::Exclusive, repacker)?; repacks.extend(collapse_repacks); repacks.extend(expand_repacks); break; @@ -122,6 +135,13 @@ impl<'tcx> RepackingBridgeSemiLattice<'tcx> for CapabilityProjections<'tcx> { PlaceOrdering::Equal => {} } } + if !from.contains_key(&place) { + panic!( + "from does not contain place: {}. From: {:?}", + place.to_short_string(repacker), + from + ); + } // Downgrade the permission if needed let curr = from[&place]; if curr > kind { diff --git a/src/free_pcs/impl/join_semi_lattice.rs b/src/free_pcs/impl/join_semi_lattice.rs index b4167dfe..73dd0772 100644 --- a/src/free_pcs/impl/join_semi_lattice.rs +++ b/src/free_pcs/impl/join_semi_lattice.rs @@ -108,6 +108,7 @@ impl<'tcx> RepackingJoinSemiLattice<'tcx> for CapabilityProjections<'tcx> { self.expand( from, CorrectedPlace::new(joinable_place, repacker), + CapabilityKind::Exclusive, repacker, ) .unwrap(); diff --git a/src/free_pcs/impl/local.rs b/src/free_pcs/impl/local.rs index 07240047..e09ccf2c 100644 --- a/src/free_pcs/impl/local.rs +++ b/src/free_pcs/impl/local.rs @@ -6,7 +6,7 @@ use std::fmt::{Debug, Formatter, Result}; -use derive_more::{Deref, DerefMut}; +use derive_more::Deref; use rustc_interface::{ data_structures::fx::{FxHashMap, FxHashSet}, middle::mir::Local, @@ -17,9 +17,10 @@ use crate::{ free_pcs::{CapabilityKind, RelatedSet, RepackOp}, pcg_validity_assert, rustc_interface, utils::{ - corrected::CorrectedPlace, display::DisplayWithRepacker, Place, PlaceOrdering, + corrected::CorrectedPlace, display::DisplayWithRepacker, ExpandStep, Place, PlaceOrdering, PlaceRepacker, }, + validity_checks_enabled, }; #[derive(Clone, PartialEq, Eq)] @@ -73,7 +74,46 @@ impl<'tcx> CapabilityLocal<'tcx> { } } -#[derive(Clone, PartialEq, Eq, Deref, DerefMut)] +pub trait CheckValidityOnExpiry { + fn check_validity_on_expiry(&self); +} + +pub struct DropGuard<'a, S: CheckValidityOnExpiry, T> { + source: *const S, + value: &'a mut T, +} + +impl<'a, 'tcx, S: CheckValidityOnExpiry, T> std::ops::Deref for DropGuard<'a, S, T> { + type Target = T; + + fn deref(&self) -> &T { + self.value + } +} + +impl<'a, 'tcx, S: CheckValidityOnExpiry, T> std::ops::DerefMut for DropGuard<'a, S, T> { + fn deref_mut(&mut self) -> &mut T { + self.value + } +} + +impl<'a, 'tcx, S: CheckValidityOnExpiry, T> DropGuard<'a, S, T> { + /// Caller must ensure that value borrows from `source` + pub(crate) unsafe fn new(source: *const S, value: &'a mut T) -> Self { + Self { source, value } + } +} + +impl<'a, 'tcx, S: CheckValidityOnExpiry, T> Drop for DropGuard<'a, S, T> { + fn drop(&mut self) { + // SAFETY: DropGuard::new ensures that `value` mutably borrows from `source` + // once the DropGuard is dropped, `source` will no longer have any mutable references + // and we can safely obtain a shared reference to it + unsafe { (*self.source).check_validity_on_expiry() }; + } +} + +#[derive(Clone, PartialEq, Eq, Deref)] /// The permissions for all the projections of a place // We only need the projection part of the place pub struct CapabilityProjections<'tcx>(FxHashMap, CapabilityKind>); @@ -84,7 +124,100 @@ impl Debug for CapabilityProjections<'_> { } } +impl<'tcx> CheckValidityOnExpiry for CapabilityProjections<'tcx> { + fn check_validity_on_expiry(&self) { + self.check_validity(); + } +} + impl<'tcx> CapabilityProjections<'tcx> { + pub(crate) fn is_leaf(&self, place: Place<'tcx>) -> bool { + self.0.iter().all(|(p, _)| p.is_prefix(place)) + } + + pub(crate) fn get_mut(&mut self, place: Place<'tcx>) -> Option<&mut CapabilityKind> { + self.0.get_mut(&place) + } + + pub(crate) fn insert(&mut self, place: Place<'tcx>, cap: CapabilityKind) { + self.0.insert(place, cap); + } + + pub(crate) fn extend( + &mut self, + other: impl IntoIterator, CapabilityKind)>, + ) { + self.0.extend(other); + if validity_checks_enabled() { + self.check_validity(); + } + } + + pub(crate) fn remove(&mut self, place: &Place<'tcx>) -> Option { + self.0.remove(place) + } + + pub(crate) fn iter_mut<'a>( + &'a mut self, + ) -> impl Iterator< + Item = ( + Place<'tcx>, + DropGuard<'a, CapabilityProjections<'tcx>, CapabilityKind>, + ), + > { + struct Iter<'a, 'tcx> { + inner: *mut CapabilityProjections<'tcx>, + places: Vec>, + idx: usize, + _marker: std::marker::PhantomData<&'a ()>, + } + + impl<'a, 'tcx: 'a> Iterator for Iter<'a, 'tcx> { + type Item = ( + Place<'tcx>, + DropGuard<'a, CapabilityProjections<'tcx>, CapabilityKind>, + ); + + fn next(&mut self) -> Option { + if self.idx >= self.places.len() { + None + } else { + let place: Place<'tcx> = self.places[self.idx]; + // SAFETY: As long as `Iter<'a, tcx> is live, self.inner is blocked` + let capability: &'a mut CapabilityKind = + unsafe { self.inner.as_mut().unwrap().get_mut(place).unwrap() }; + self.idx += 1; + // SAFETY: `capability` borrows from self.inner + let guard = unsafe { DropGuard::new(self.inner, capability) }; + Some((place, guard)) + } + } + } + + let places = self.0.keys().copied().collect(); + Iter { + inner: self, + places, + idx: 0, + _marker: std::marker::PhantomData, + } + } + + fn check_validity(&self) { + for (place, cap) in &**self { + if cap.is_exclusive() { + for (other_place, other_cap) in &**self { + if other_place.is_strict_prefix(*place) && other_cap.is_exclusive() { + panic!( + "Found place {:?} with Exclusive capability that has a prefix {:?} also with Exclusive capability", + place, + other_place + ); + } + } + } + } + } pub(crate) fn debug_lines(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec { self.iter() .map(|(p, k)| format!("{}: {:?}", p.to_short_string(repacker), k)) @@ -108,6 +241,9 @@ impl<'tcx> CapabilityProjections<'tcx> { pub(crate) fn update_cap(&mut self, place: Place<'tcx>, cap: CapabilityKind) { let _old = self.insert(place, cap); + if validity_checks_enabled() { + self.check_validity(); + } // assert!(old.is_some()); } @@ -154,6 +290,7 @@ impl<'tcx> CapabilityProjections<'tcx> { &mut self, from: Place<'tcx>, to: CorrectedPlace<'tcx>, + for_cap: CapabilityKind, repacker: PlaceRepacker<'_, 'tcx>, ) -> std::result::Result>, PCGError> { assert!( @@ -161,23 +298,55 @@ impl<'tcx> CapabilityProjections<'tcx> { "Mutable reference {:?} should be expanded in borrow PCG, not owned PCG", from ); - pcg_validity_assert!(!self.contains_key(&to)); - let (expanded, mut others) = from.expand(*to, repacker)?; - let mut perm = self.remove(&from).unwrap(); - others.push(*to); + pcg_validity_assert!( + !self.contains_key(&to), + "We don't need to expand to {} because it already has a capability ({:?})", + to.to_short_string(repacker), + self.get(&to) + ); + + let (expand_steps, other_expanded_places) = from.expand(*to, repacker)?; + + // Update permission of `from` place + let from_cap = *self.get(&from).unwrap(); + let other_place_perm = from_cap; + let projection_path_perm = if for_cap.is_read() { + Some(CapabilityKind::Read) + } else { + None + }; + + for place in other_expanded_places.iter() { + self.insert(*place, other_place_perm); + } + let mut ops = Vec::new(); - for (from, to, kind) in expanded { - let others = others.extract_if(|other| !to.is_prefix(*other)); - self.extend(others.map(|p| (p, perm))); - if kind.is_box() && perm.is_shallow_exclusive() { - ops.push(RepackOp::DerefShallowInit(from, to)); - perm = CapabilityKind::Write; + + for ExpandStep { + base_place, + projected_place, + kind, + } in expand_steps + { + if let Some(perm) = projection_path_perm { + self.insert(base_place, perm); } else { - ops.push(RepackOp::Expand(from, to, perm)); + self.remove(&base_place); + } + + if kind.is_box() && from_cap.is_shallow_exclusive() { + ops.push(RepackOp::DerefShallowInit(base_place, projected_place)); + } else { + ops.push(RepackOp::Expand(base_place, projected_place, for_cap)); } } - self.extend(others.into_iter().map(|p| (p, perm))); - // assert!(self.contains_key(&to), "{self:?}\n{to:?}"); + + self.insert(*to, from_cap); + + if validity_checks_enabled() { + self.check_validity(); + } + Ok(ops) } @@ -185,27 +354,33 @@ impl<'tcx> CapabilityProjections<'tcx> { // state can always be packed up to the root pub(crate) fn collapse( &mut self, - mut from: FxHashSet>, + from: FxHashSet>, to: Place<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, ) -> std::result::Result>, PCGInternalError> { - // We could instead return this error, but failing early might be better - // for development - if self.contains_key(&to) { - let err = PCGInternalError::new(format!( - "Cannot collapse (from:{from:?}, to:{to:?}) because {to:?} already exists in {self:?}" - )); - panic!("{:?}", err); - } + let f = from.clone(); let mut old_caps: FxHashMap<_, _> = from .iter() .flat_map(|&p| self.remove(&p).map(|cap| (p, cap))) .collect(); - let collapsed = to.collapse(&mut from, repacker); - assert!(from.is_empty(), "{from:?} ({collapsed:?}) {to:?}"); + let collapsed = to.collapse(from, repacker); + eprintln!( + "Collapsing to {} from {} requires {:?}", + to.to_short_string(repacker), + f.iter() + .map(|p| p.to_short_string(repacker)) + .collect::>() + .join(", "), + collapsed + ); let mut exclusive_at = Vec::new(); if !to.projects_shared_ref(repacker) { - for (to, _, kind) in &collapsed { + for ExpandStep { + projected_place: to, + kind, + .. + } in &collapsed + { if kind.is_shared_ref() { let mut is_prefixed = false; exclusive_at @@ -224,8 +399,15 @@ impl<'tcx> CapabilityProjections<'tcx> { } } let mut ops = Vec::new(); - for (to, from, _) in collapsed { - let removed_perms: Vec<_> = old_caps.extract_if(|old, _| to.is_prefix(*old)).collect(); + for ExpandStep { + base_place, + projected_place, + .. + } in &collapsed + { + let removed_perms: Vec<_> = old_caps + .extract_if(|old, _| projected_place.is_prefix(*old)) + .collect(); let perm = removed_perms .iter() .fold(CapabilityKind::Exclusive, |acc, (_, p)| { @@ -235,17 +417,25 @@ impl<'tcx> CapabilityProjections<'tcx> { }); for (from, from_perm) in removed_perms { match from_perm.partial_cmp(&perm) { - Some(std::cmp::Ordering::Equal) => {}, // Equal, do nothing + Some(std::cmp::Ordering::Equal) => {} // Equal, do nothing Some(std::cmp::Ordering::Greater) => { ops.push(RepackOp::Weaken(from, from_perm, perm)); - }, + } _ => panic!("Weaken {:?} {:?} {:?}", from, from_perm, perm), } } - old_caps.insert(to, perm); - ops.push(RepackOp::Collapse(to, from, perm)); + old_caps.insert(*base_place, perm); + ops.push(RepackOp::Collapse(*base_place, *projected_place, perm)); } + pcg_validity_assert!( + old_caps.contains_key(&to), + "Old capabilities does not have a capability for collapse target {}", + to.to_short_string(repacker) + ); self.insert(to, old_caps[&to]); + if validity_checks_enabled() { + self.check_validity(); + } Ok(ops) } } diff --git a/src/free_pcs/impl/place.rs b/src/free_pcs/impl/place.rs index be219ab4..806c9aec 100644 --- a/src/free_pcs/impl/place.rs +++ b/src/free_pcs/impl/place.rs @@ -10,29 +10,53 @@ use std::{ fmt::{Debug, Formatter, Result}, }; -use rustc_interface::data_structures::fx::FxHashSet; +use crate::rustc_interface::data_structures::fx::FxHashSet; -use crate::{rustc_interface, utils::Place}; +use crate::{utils::Place, validity_checks_enabled}; #[derive(Debug, Deref)] pub(crate) struct RelatedSet<'tcx>(FxHashSet<(Place<'tcx>, CapabilityKind)>); impl<'tcx> RelatedSet<'tcx> { pub fn new(related: FxHashSet<(Place<'tcx>, CapabilityKind)>) -> Self { + if validity_checks_enabled() { + // Assert that for each place with Capability Exclusive, there is no prefix also with exclusive + for &(place, cap) in &related { + if cap == CapabilityKind::Exclusive { + for &(other_place, other_cap) in &related { + if other_place.is_strict_prefix(place) + && other_cap == CapabilityKind::Exclusive + { + panic!( + "Found place {:?} with Exclusive capability that has a prefix {:?} also with Exclusive capability", + place, + other_place + ); + } + } + } + } + } Self(related) } + pub fn get_places(&self) -> FxHashSet> { self.0.iter().map(|(p, _)| *p).collect() } + pub fn get_only_place(&self) -> Place<'tcx> { assert_eq!(self.0.len(), 1); self.0.iter().next().unwrap().0 } + pub fn common_prefix(&self, to: Place<'tcx>) -> Place<'tcx> { self.get_places() .iter() .fold(to, |acc, p| acc.common_prefix(*p)) } + pub(crate) fn len(&self) -> usize { + self.0.len() + } } #[derive(Copy, Clone, PartialEq, Eq, Hash)] diff --git a/src/free_pcs/impl/update.rs b/src/free_pcs/impl/update.rs index f66dc770..45053eee 100644 --- a/src/free_pcs/impl/update.rs +++ b/src/free_pcs/impl/update.rs @@ -10,7 +10,8 @@ use crate::{ pcg_validity_assert, rustc_interface::middle::mir::{Local, RETURN_PLACE}, utils::{ - corrected::CorrectedPlace, LocalMutationIsAllowed, Place, PlaceOrdering, PlaceRepacker, + corrected::CorrectedPlace, display::DisplayWithRepacker, LocalMutationIsAllowed, Place, + PlaceOrdering, PlaceRepacker, }, }; @@ -46,7 +47,7 @@ impl<'tcx> CapabilitySummary<'tcx> { } Condition::Capability(place, cap) => { let cp = self[place.local].get_allocated_mut(); - cp.repack(place, repacker)?; + cp.repack(place, repacker, cap)?; cp.insert(place, cap); } Condition::Return => { @@ -146,14 +147,27 @@ impl<'tcx> CapabilityProjections<'tcx> { &mut self, to: Place<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, + for_cap: CapabilityKind, ) -> Result<(), PCGError> { let related = self.find_all_related(to, None); - for (from_place, _) in (*related).iter().copied() { + if for_cap.is_read() && self.contains_key(&to) { + return Ok(()); + } + for (from_place, cap) in (*related).iter().copied() { match from_place.partial_cmp(to).unwrap() { PlaceOrdering::Prefix => { + if related.len() > 1 { + panic!( + "Cannot repack to {} for {:?}: more than 1 related place; {:?}", + to.to_short_string(repacker), + for_cap, + related + ); + } self.expand( related.get_only_place(), CorrectedPlace::new(to, repacker), + for_cap, repacker, )?; return Ok(()); @@ -168,7 +182,7 @@ impl<'tcx> CapabilityProjections<'tcx> { // Collapse self.collapse(related.get_places(), cp, repacker)?; // Expand - self.expand(cp, CorrectedPlace::new(to, repacker), repacker)?; + self.expand(cp, CorrectedPlace::new(to, repacker), cap, repacker)?; return Ok(()); } } diff --git a/src/utils/repacker.rs b/src/utils/repacker.rs index c5c75e46..634570ad 100644 --- a/src/utils/repacker.rs +++ b/src/utils/repacker.rs @@ -22,6 +22,7 @@ use crate::{ borrow_pcg::region_projection::PCGRegion, combined_pcs::{PCGError, PCGUnsupportedError}, rustc_interface, + utils::display::DisplayWithRepacker, }; use super::Place; @@ -36,6 +37,37 @@ pub enum ProjectionKind { Other, } +pub struct ShallowExpansion<'tcx> { + pub(crate) target_place: Place<'tcx>, + + /// Other places that could have resulted from this expansion. Note: this + /// vector is always incomplete when projecting with `Index` or `Subslice` + /// and also when projecting a slice type with `ConstantIndex`! + pub(crate) other_places: Vec>, + pub(crate) kind: ProjectionKind, +} + +impl<'tcx> ShallowExpansion<'tcx> { + pub(crate) fn new( + target_place: Place<'tcx>, + other_places: Vec>, + kind: ProjectionKind, + ) -> Self { + Self { + target_place, + other_places, + kind, + } + } + + pub(crate) fn expansion(&self) -> Vec> { + let mut expansion = self.other_places.clone(); + self.kind + .insert_target_into_expansion(self.target_place, &mut expansion); + expansion + } +} + impl ProjectionKind { pub(crate) fn is_box(self) -> bool { matches!(self, ProjectionKind::Box) @@ -127,6 +159,23 @@ pub struct ConstantIndex { pub(crate) from_end: bool, } +#[derive(Debug)] +pub struct ExpandStep<'tcx> { + pub(crate) base_place: Place<'tcx>, + pub(crate) projected_place: Place<'tcx>, + pub(crate) kind: ProjectionKind, +} + +impl<'tcx> ExpandStep<'tcx> { + pub(crate) fn new(from: Place<'tcx>, to: Place<'tcx>, kind: ProjectionKind) -> Self { + Self { + base_place: from, + projected_place: to, + kind, + } + } +} + impl<'tcx> Place<'tcx> { pub(crate) fn to_rust_place(self, repacker: PlaceRepacker<'_, 'tcx>) -> MirPlace<'tcx> { MirPlace { @@ -149,7 +198,7 @@ impl<'tcx> Place<'tcx> { mut self, to: Self, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Result<(Vec<(Self, Self, ProjectionKind)>, Vec), PCGError> { + ) -> Result<(Vec>, Vec), PCGError> { assert!( self.is_prefix(to), "The minuend ({self:?}) must be the prefix of the subtrahend ({to:?})." @@ -157,10 +206,14 @@ impl<'tcx> Place<'tcx> { let mut place_set = Vec::new(); let mut expanded = Vec::new(); while self.projection.len() < to.projection.len() { - let (new_minuend, places, kind) = self.expand_one_level(to, repacker)?; - expanded.push((self, new_minuend, kind)); - place_set.extend(places); - self = new_minuend; + let ShallowExpansion { + target_place, + other_places, + kind, + } = self.expand_one_level(to, repacker)?; + expanded.push(ExpandStep::new(self, target_place, kind)); + place_set.extend(other_places); + self = target_place; } Ok((expanded, place_set)) } @@ -170,30 +223,31 @@ impl<'tcx> Place<'tcx> { /// `expand`. pub fn collapse( self, - from: &mut FxHashSet, + from: FxHashSet, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Vec<(Self, Self, ProjectionKind)> { + ) -> Vec> { let mut collapsed = Vec::new(); let mut guide_places = vec![self]; - while let Some(guide_place) = guide_places.pop() { - if !from.remove(&guide_place) { - let expand_guide = *from - .iter() - .find(|p| guide_place.is_prefix(**p)) - .unwrap_or_else(|| { - panic!( - "The `from` set didn't contain all \ - the places required to construct the \ - `guide_place`. Currently tried to find \ - `{guide_place:?}` in `{from:?}`." - ) - }); - let (expanded, new_places) = guide_place.expand(expand_guide, repacker).unwrap(); - // Doing `collapsed.extend(expanded)` would result in a reversed order. - // Could also change this to `collapsed.push(expanded)` and return Vec>. + while let Some(place) = guide_places.pop() { + let mut next_projection_candidates: Vec> = from + .iter() + .copied() + .filter(|p| place.is_prefix_exact(*p)) + .collect(); + while let Some(next_candidate) = next_projection_candidates.pop() { + eprintln!( + "Projection candidate {} for {}", + next_candidate.to_short_string(repacker), + place.to_short_string(repacker) + ); + let (expanded, new_places) = place.expand(next_candidate, repacker).unwrap(); + + // Don't consider unpacking with targets in `new_places` since they are going + // to be packed via the packing in `expanded` + next_projection_candidates.retain(|p| !new_places.contains(p)); + collapsed.extend(expanded); guide_places.extend(new_places); - from.remove(&expand_guide); } } collapsed.reverse(); @@ -202,14 +256,12 @@ impl<'tcx> Place<'tcx> { /// Expand `self` one level down by following the `guide_place`. /// Returns the new `self` and a vector containing other places that - /// could have resulted from the expansion. Note: this vector is always - /// incomplete when projecting with `Index` or `Subslice` and also when - /// projecting a slice type with `ConstantIndex`! + /// could have resulted from the expansion. pub fn expand_one_level( self, guide_place: Self, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Result<(Self, Vec, ProjectionKind), PCGError> { + ) -> Result, PCGError> { let index = self.projection.len(); assert!( index < guide_place.projection.len(), @@ -289,7 +341,7 @@ impl<'tcx> Place<'tcx> { self, ); } - Ok((new_current_place, other_places, kind)) + Ok(ShallowExpansion::new(new_current_place, other_places, kind)) } /// Expands a place `x.f.g` of type struct into a vector of places for diff --git a/src/utils/validity.rs b/src/utils/validity.rs index fa1addcc..c51b4462 100644 --- a/src/utils/validity.rs +++ b/src/utils/validity.rs @@ -1,4 +1,6 @@ -use super::{env_feature_enabled, PlaceRepacker}; +use crate::validity_checks_enabled; + +use super::PlaceRepacker; pub trait HasValidityCheck<'tcx> { fn check_validity(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Result<(), String>; @@ -15,7 +17,3 @@ pub trait HasValidityCheck<'tcx> { self.check_validity(repacker).is_ok() } } - -pub(crate) fn validity_checks_enabled() -> bool { - env_feature_enabled("PCG_VALIDITY_CHECKS").unwrap_or(cfg!(debug_assertions)) -} diff --git a/test-files/81_shared_ref.rs b/test-files/81_shared_ref.rs new file mode 100644 index 00000000..7880c58c --- /dev/null +++ b/test-files/81_shared_ref.rs @@ -0,0 +1,17 @@ +struct S { + a: i32, + b: i32, +} + +fn main() { + let x = S { a: 1, b: 2 }; + let y = &x.a; + // PCG: bb0[4] post_main: x.b: E + // PCG: bb0[4] post_main: x.a: R + // PCG: bb0[4] post_main: x: R + // ~PCG: bb0[4] post_operands: Repacks Start: Collapse(_1, _1.0, R) + let z = &x; + + let a = z.b; + let b = *y; +} From 84c8d363cc3f90d542a967daff9cf668fe393587 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Mon, 3 Mar 2025 17:32:04 -0800 Subject: [PATCH 19/21] WIP --- src/borrow_pcg/action/mod.rs | 5 -- src/borrow_pcg/state/obtain.rs | 6 --- src/free_pcs/impl/bridge.rs | 72 ++++++++++++++------------ src/free_pcs/impl/join_semi_lattice.rs | 11 ++-- src/free_pcs/impl/local.rs | 23 +++----- src/free_pcs/impl/update.rs | 19 ++++++- src/utils/repacker.rs | 12 ++--- test-files/81_shared_ref.rs | 7 +++ test-files/82_shared_ref_2.rs | 10 ++++ 9 files changed, 91 insertions(+), 74 deletions(-) create mode 100644 test-files/82_shared_ref_2.rs diff --git a/src/borrow_pcg/action/mod.rs b/src/borrow_pcg/action/mod.rs index d0d5a324..76f7af02 100644 --- a/src/borrow_pcg/action/mod.rs +++ b/src/borrow_pcg/action/mod.rs @@ -234,11 +234,6 @@ impl<'tcx> BorrowsState<'tcx> { repacker: PlaceRepacker<'_, 'tcx>, ) -> Result { let mut changed = self.insert(edge.clone()); - eprintln!( - "Add edge {:?} (for exclusive: {})", - edge.to_short_string(repacker), - for_exclusive - ); Ok(match edge.kind { BorrowPCGEdgeKind::Borrow(_) => todo!(), BorrowPCGEdgeKind::BorrowPCGExpansion(expansion) => { diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index f1bad1a8..868d2ca0 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -13,7 +13,6 @@ use crate::combined_pcs::{PCGError, PCGNodeLike}; use crate::free_pcs::CapabilityKind; use crate::rustc_interface::middle::mir::{BorrowKind, Location, MutBorrowKind}; use crate::rustc_interface::middle::ty::{self, Mutability}; -use crate::utils::display::DisplayWithRepacker; use crate::utils::maybe_old::MaybeOldPlace; use crate::utils::{Place, PlaceRepacker}; use crate::visualization::dot_graph::DotGraph; @@ -190,11 +189,6 @@ impl<'tcx> BorrowsState<'tcx> { obtain_reason: ObtainReason, location: Location, ) -> Result, PCGError> { - eprintln!( - "Expanding to {} because of {:?}", - to_place.to_short_string(repacker), - obtain_reason - ); let for_exclusive = obtain_reason.min_post_obtain_capability() != CapabilityKind::Read; let mut actions = ExecutedActions::new(); diff --git a/src/free_pcs/impl/bridge.rs b/src/free_pcs/impl/bridge.rs index 9215fba5..881e0e02 100644 --- a/src/free_pcs/impl/bridge.rs +++ b/src/free_pcs/impl/bridge.rs @@ -95,44 +95,52 @@ impl<'tcx> RepackingBridgeSemiLattice<'tcx> for CapabilityProjections<'tcx> { ) -> std::result::Result>, PCGError> { // TODO: remove need for clone let mut from = self.clone(); - eprintln!("From initial: {:?}", from); let mut repacks = Vec::new(); for (&place, &kind) in other.iter().sorted_by_key(|(p, _)| p.projection.len()) { - eprintln!("Place: {:?}, Kind: {:?}", place, kind); let place = CorrectedPlace::new(place, repacker); let related = from.find_all_related(*place, None); - if let Some(c) = from.get(&place) - && *c == kind - { - continue; - } - for (from_place, _) in (*related).iter().copied() { - match from_place.partial_cmp(*place).unwrap() { - PlaceOrdering::Prefix => { - let unpacks = - from.expand(from_place, place, CapabilityKind::Exclusive, repacker)?; - repacks.extend(unpacks); - break; - } - PlaceOrdering::Suffix => { - let packs = from - .collapse(related.get_places(), *place, repacker) - .unwrap(); - repacks.extend(packs); - break; - } - PlaceOrdering::Both => { - let common_prefix = related.common_prefix(*place); - let collapse_repacks = - from.collapse(related.get_places(), common_prefix, repacker)?; - let expand_repacks = - from.expand(common_prefix, place, CapabilityKind::Exclusive, repacker)?; - repacks.extend(collapse_repacks); - repacks.extend(expand_repacks); - break; + if !from.contains_key(&place) { + for (from_place, _) in (*related) + .iter() + .copied() + .sorted_by_key(|(p, _)| p.projection.len()) + .rev() + { + match from_place.partial_cmp(*place).unwrap() { + PlaceOrdering::Prefix => { + let unpacks = from.expand( + from_place, + place, + CapabilityKind::Exclusive, + repacker, + )?; + repacks.extend(unpacks); + break; + } + PlaceOrdering::Suffix => { + let packs = from + .collapse(related.get_places(), *place, repacker) + .unwrap(); + repacks.extend(packs); + break; + } + PlaceOrdering::Both => { + let common_prefix = related.common_prefix(*place); + let collapse_repacks = + from.collapse(related.get_places(), common_prefix, repacker)?; + let expand_repacks = from.expand( + common_prefix, + place, + CapabilityKind::Exclusive, + repacker, + )?; + repacks.extend(collapse_repacks); + repacks.extend(expand_repacks); + break; + } + PlaceOrdering::Equal => {} } - PlaceOrdering::Equal => {} } } if !from.contains_key(&place) { diff --git a/src/free_pcs/impl/join_semi_lattice.rs b/src/free_pcs/impl/join_semi_lattice.rs index 73dd0772..352d1d20 100644 --- a/src/free_pcs/impl/join_semi_lattice.rs +++ b/src/free_pcs/impl/join_semi_lattice.rs @@ -6,7 +6,8 @@ use std::rc::Rc; -use rustc_interface::mir_dataflow::JoinSemiLattice; +use itertools::Itertools; +use crate::rustc_interface::mir_dataflow::JoinSemiLattice; use crate::{ combined_pcs::{EvalStmtPhase, PCGInternalError}, @@ -14,8 +15,9 @@ use crate::{ CapabilityKind, CapabilityLocal, CapabilityProjections, CapabilitySummary, FreePlaceCapabilitySummary, }, - rustc_interface, - utils::{corrected::CorrectedPlace, PlaceOrdering, PlaceRepacker}, + utils::{ + corrected::CorrectedPlace, PlaceOrdering, PlaceRepacker, + }, }; impl JoinSemiLattice for FreePlaceCapabilitySummary<'_, '_> { @@ -89,7 +91,7 @@ impl<'tcx> RepackingJoinSemiLattice<'tcx> for CapabilityProjections<'tcx> { return Ok(true); } let mut changed = false; - for (&place, &kind) in &**other { + for (&place, &kind) in other.iter().sorted_by_key(|(p, _)| p.projection.len()) { let related = self.find_all_related(place, None); for (from_place, _) in (*related).iter().copied() { let mut done_with_place = false; @@ -144,7 +146,6 @@ impl<'tcx> RepackingJoinSemiLattice<'tcx> for CapabilityProjections<'tcx> { } PlaceOrdering::Both => { changed = true; - let cp = related.common_prefix(place); self.collapse(related.get_places(), cp, repacker)?; done_with_place = true; diff --git a/src/free_pcs/impl/local.rs b/src/free_pcs/impl/local.rs index e09ccf2c..01bca295 100644 --- a/src/free_pcs/impl/local.rs +++ b/src/free_pcs/impl/local.rs @@ -83,7 +83,7 @@ pub struct DropGuard<'a, S: CheckValidityOnExpiry, T> { value: &'a mut T, } -impl<'a, 'tcx, S: CheckValidityOnExpiry, T> std::ops::Deref for DropGuard<'a, S, T> { +impl std::ops::Deref for DropGuard<'_, S, T> { type Target = T; fn deref(&self) -> &T { @@ -91,20 +91,20 @@ impl<'a, 'tcx, S: CheckValidityOnExpiry, T> std::ops::Deref for DropGuard<'a, S, } } -impl<'a, 'tcx, S: CheckValidityOnExpiry, T> std::ops::DerefMut for DropGuard<'a, S, T> { +impl std::ops::DerefMut for DropGuard<'_, S, T> { fn deref_mut(&mut self) -> &mut T { self.value } } -impl<'a, 'tcx, S: CheckValidityOnExpiry, T> DropGuard<'a, S, T> { +impl<'a, S: CheckValidityOnExpiry, T> DropGuard<'a, S, T> { /// Caller must ensure that value borrows from `source` pub(crate) unsafe fn new(source: *const S, value: &'a mut T) -> Self { Self { source, value } } } -impl<'a, 'tcx, S: CheckValidityOnExpiry, T> Drop for DropGuard<'a, S, T> { +impl Drop for DropGuard<'_, S, T> { fn drop(&mut self) { // SAFETY: DropGuard::new ensures that `value` mutably borrows from `source` // once the DropGuard is dropped, `source` will no longer have any mutable references @@ -124,7 +124,7 @@ impl Debug for CapabilityProjections<'_> { } } -impl<'tcx> CheckValidityOnExpiry for CapabilityProjections<'tcx> { +impl CheckValidityOnExpiry for CapabilityProjections<'_> { fn check_validity_on_expiry(&self) { self.check_validity(); } @@ -143,6 +143,7 @@ impl<'tcx> CapabilityProjections<'tcx> { self.0.insert(place, cap); } + #[allow(dead_code)] pub(crate) fn extend( &mut self, other: impl IntoIterator, CapabilityKind)>, @@ -240,7 +241,7 @@ impl<'tcx> CapabilityProjections<'tcx> { } pub(crate) fn update_cap(&mut self, place: Place<'tcx>, cap: CapabilityKind) { - let _old = self.insert(place, cap); + self.insert(place, cap); if validity_checks_enabled() { self.check_validity(); } @@ -358,21 +359,11 @@ impl<'tcx> CapabilityProjections<'tcx> { to: Place<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, ) -> std::result::Result>, PCGInternalError> { - let f = from.clone(); let mut old_caps: FxHashMap<_, _> = from .iter() .flat_map(|&p| self.remove(&p).map(|cap| (p, cap))) .collect(); let collapsed = to.collapse(from, repacker); - eprintln!( - "Collapsing to {} from {} requires {:?}", - to.to_short_string(repacker), - f.iter() - .map(|p| p.to_short_string(repacker)) - .collect::>() - .join(", "), - collapsed - ); let mut exclusive_at = Vec::new(); if !to.projects_shared_ref(repacker) { for ExpandStep { diff --git a/src/free_pcs/impl/update.rs b/src/free_pcs/impl/update.rs index 45053eee..52ad4189 100644 --- a/src/free_pcs/impl/update.rs +++ b/src/free_pcs/impl/update.rs @@ -150,8 +150,23 @@ impl<'tcx> CapabilityProjections<'tcx> { for_cap: CapabilityKind, ) -> Result<(), PCGError> { let related = self.find_all_related(to, None); - if for_cap.is_read() && self.contains_key(&to) { - return Ok(()); + if for_cap.is_read() { + if self.contains_key(&to) { + return Ok(()); + } + if let Some((projection_candidate, _)) = related + .iter() + .filter(|(p, _)| p.is_strict_prefix(to)) + .max_by_key(|(p, _)| p.projection.len()) + { + self.expand( + *projection_candidate, + CorrectedPlace::new(to, repacker), + for_cap, + repacker, + )?; + return Ok(()); + } } for (from_place, cap) in (*related).iter().copied() { match from_place.partial_cmp(to).unwrap() { diff --git a/src/utils/repacker.rs b/src/utils/repacker.rs index 634570ad..354944ed 100644 --- a/src/utils/repacker.rs +++ b/src/utils/repacker.rs @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use itertools::Itertools; use rustc_interface::{ data_structures::fx::FxHashSet, index::{bit_set::BitSet, Idx}, @@ -22,7 +23,6 @@ use crate::{ borrow_pcg::region_projection::PCGRegion, combined_pcs::{PCGError, PCGUnsupportedError}, rustc_interface, - utils::display::DisplayWithRepacker, }; use super::Place; @@ -60,7 +60,7 @@ impl<'tcx> ShallowExpansion<'tcx> { } } - pub(crate) fn expansion(&self) -> Vec> { + pub fn expansion(&self) -> Vec> { let mut expansion = self.other_places.clone(); self.kind .insert_target_into_expansion(self.target_place, &mut expansion); @@ -232,14 +232,10 @@ impl<'tcx> Place<'tcx> { let mut next_projection_candidates: Vec> = from .iter() .copied() - .filter(|p| place.is_prefix_exact(*p)) + .filter(|p| place.is_strict_prefix(*p)) + .sorted_by_key(|p| p.projection.len()) .collect(); while let Some(next_candidate) = next_projection_candidates.pop() { - eprintln!( - "Projection candidate {} for {}", - next_candidate.to_short_string(repacker), - place.to_short_string(repacker) - ); let (expanded, new_places) = place.expand(next_candidate, repacker).unwrap(); // Don't consider unpacking with targets in `new_places` since they are going diff --git a/test-files/81_shared_ref.rs b/test-files/81_shared_ref.rs index 7880c58c..e5f78f24 100644 --- a/test-files/81_shared_ref.rs +++ b/test-files/81_shared_ref.rs @@ -3,6 +3,13 @@ struct S { b: i32, } +fn f(x: Result, ()>) { + if let Ok(s@Some(y)) = x { + let r = &s; + let d = *r; + } +} + fn main() { let x = S { a: 1, b: 2 }; let y = &x.a; diff --git a/test-files/82_shared_ref_2.rs b/test-files/82_shared_ref_2.rs new file mode 100644 index 00000000..920ad722 --- /dev/null +++ b/test-files/82_shared_ref_2.rs @@ -0,0 +1,10 @@ +fn f(x: Result, ()>) { + if let Ok(s@Some(y)) = x { +// ~PCG: bb3[1] pre_operands: Repacks Start: Expand(_1, (_1@Ok), E) + let r = &s; + let d = *r; + } +} + +fn main() { +} From ddd85e8e94ff5dc95f11706c45eec88d8aa42145 Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Tue, 4 Mar 2025 11:45:08 -0800 Subject: [PATCH 20/21] Very large refactoring --- src/borrow_pcg/borrow_pcg_expansion.rs | 46 +-- src/borrow_pcg/graph/aliases.rs | 2 +- src/borrow_pcg/state/obtain.rs | 6 +- src/combined_pcs/domain.rs | 1 + src/combined_pcs/engine.rs | 104 +++++-- src/free_pcs/impl/bridge.rs | 115 +++----- src/free_pcs/impl/engine.rs | 27 +- src/free_pcs/impl/fpcs.rs | 114 +++---- src/free_pcs/impl/join_semi_lattice.rs | 143 +++------ src/free_pcs/impl/local.rs | 392 +++++++++---------------- src/free_pcs/impl/mod.rs | 2 +- src/free_pcs/impl/place.rs | 71 ++--- src/free_pcs/impl/triple.rs | 3 +- src/free_pcs/impl/update.rs | 155 +++++----- src/free_pcs/results/cursor.rs | 3 +- src/utils/domain_data.rs | 18 +- src/utils/place/mod.rs | 36 +-- src/utils/repacker.rs | 79 ++--- src/visualization/dot_graph.rs | 2 +- src/visualization/graph_constructor.rs | 9 +- test-files/79_add_overflow.rs | 2 +- test-files/81_shared_ref.rs | 7 - test-files/82_shared_ref_2.rs | 7 +- test-files/83_prusti_wand.rs | 12 + test-files/84_prusti_ownership.rs | 12 + test-files/85_prusti_rpe_example.rs | 8 + test-files/86_prusti_mut_borrows.rs | 25 ++ test-files/87_prusti_create_box.rs | 10 + 28 files changed, 664 insertions(+), 747 deletions(-) create mode 100644 test-files/83_prusti_wand.rs create mode 100644 test-files/84_prusti_ownership.rs create mode 100644 test-files/85_prusti_rpe_example.rs create mode 100644 test-files/86_prusti_mut_borrows.rs create mode 100644 test-files/87_prusti_create_box.rs diff --git a/src/borrow_pcg/borrow_pcg_expansion.rs b/src/borrow_pcg/borrow_pcg_expansion.rs index 656c864c..43a241b3 100644 --- a/src/borrow_pcg/borrow_pcg_expansion.rs +++ b/src/borrow_pcg/borrow_pcg_expansion.rs @@ -35,7 +35,7 @@ use crate::{ #[derive(PartialEq, Eq, Clone, Debug, Hash)] pub struct ExpansionOfBorrowed<'tcx, P = LocalNode<'tcx>> { pub(crate) base: P, - pub(crate) expansion: BorrowExpansion<'tcx>, + pub(crate) expansion: PlaceExpansion<'tcx>, } impl<'tcx, P: HasValidityCheck<'tcx>> HasValidityCheck<'tcx> for ExpansionOfBorrowed<'tcx, P> { @@ -44,14 +44,14 @@ impl<'tcx, P: HasValidityCheck<'tcx>> HasValidityCheck<'tcx> for ExpansionOfBorr self.expansion.check_validity(repacker) } } -/// The projections resulting from a node in the Borrow PCG. +/// The projections resulting from an expansion of a place. /// /// This representation is preferred to a `Vec` because it ensures /// it enables a more reasonable notion of equality between expansions. Directly /// storing the place elements in a `Vec` could lead to different representations /// for the same expansion, e.g. `{*x.f.a, *x.f.b}` and `{*x.f.b, *x.f.a}`. #[derive(PartialEq, Eq, Clone, Debug, Hash)] -pub(crate) enum BorrowExpansion<'tcx> { +pub(crate) enum PlaceExpansion<'tcx> { /// Fields from e.g. a struct or tuple, e.g. `{*x.f} -> {*x.f.a, *x.f.b}` /// Note that for region projections, not every field of the base type may /// be included. For example consider the following: @@ -75,19 +75,19 @@ pub(crate) enum BorrowExpansion<'tcx> { Subslice { from: u64, to: u64, from_end: bool }, } -impl<'tcx> HasValidityCheck<'tcx> for BorrowExpansion<'tcx> { +impl<'tcx> HasValidityCheck<'tcx> for PlaceExpansion<'tcx> { fn check_validity(&self, _repacker: PlaceRepacker<'_, 'tcx>) -> Result<(), String> { Ok(()) } } -impl<'tcx> BorrowExpansion<'tcx> { +impl<'tcx> PlaceExpansion<'tcx> { #[allow(unused)] pub(crate) fn is_deref(&self) -> bool { - matches!(self, BorrowExpansion::Deref) + matches!(self, PlaceExpansion::Deref) } - pub(super) fn from_places(places: Vec>, repacker: PlaceRepacker<'_, 'tcx>) -> Self { + pub(crate) fn from_places(places: Vec>, repacker: PlaceRepacker<'_, 'tcx>) -> Self { let mut fields = BTreeMap::new(); let mut constant_indices = BTreeSet::new(); @@ -110,15 +110,15 @@ impl<'tcx> BorrowExpansion<'tcx> { from_end, }); } - PlaceElem::Deref => return BorrowExpansion::Deref, + PlaceElem::Deref => return PlaceExpansion::Deref, PlaceElem::Downcast(symbol, variant_idx) => { - return BorrowExpansion::Downcast(symbol, variant_idx); + return PlaceExpansion::Downcast(symbol, variant_idx); } PlaceElem::Index(idx) => { - return BorrowExpansion::Index(idx); + return PlaceExpansion::Index(idx); } PlaceElem::Subslice { from, to, from_end } => { - return BorrowExpansion::Subslice { from, to, from_end }; + return PlaceExpansion::Subslice { from, to, from_end }; } PlaceElem::OpaqueCast(_) => todo!(), PlaceElem::Subtype(_) => todo!(), @@ -128,17 +128,17 @@ impl<'tcx> BorrowExpansion<'tcx> { if !fields.is_empty() { assert!(constant_indices.is_empty()); - BorrowExpansion::Fields(fields) + PlaceExpansion::Fields(fields) } else if !constant_indices.is_empty() { - BorrowExpansion::ConstantIndices(constant_indices) + PlaceExpansion::ConstantIndices(constant_indices) } else { unreachable!() } } - pub(super) fn elems(&self) -> Vec> { + pub(crate) fn elems(&self) -> Vec> { match self { - BorrowExpansion::Fields(fields) => { + PlaceExpansion::Fields(fields) => { assert!(fields.len() <= 1024, "Too many fields: {:?}", fields); fields .iter() @@ -146,12 +146,12 @@ impl<'tcx> BorrowExpansion<'tcx> { .map(|(idx, ty)| PlaceElem::Field(*idx, *ty)) .collect() } - BorrowExpansion::Deref => vec![PlaceElem::Deref], - BorrowExpansion::Downcast(symbol, variant_idx) => { + PlaceExpansion::Deref => vec![PlaceElem::Deref], + PlaceExpansion::Downcast(symbol, variant_idx) => { vec![PlaceElem::Downcast(*symbol, *variant_idx)] } - BorrowExpansion::Index(idx) => vec![PlaceElem::Index(*idx)], - BorrowExpansion::ConstantIndices(constant_indices) => constant_indices + PlaceExpansion::Index(idx) => vec![PlaceElem::Index(*idx)], + PlaceExpansion::ConstantIndices(constant_indices) => constant_indices .iter() .sorted_by_key(|a| a.offset) .map(|c| PlaceElem::ConstantIndex { @@ -160,7 +160,7 @@ impl<'tcx> BorrowExpansion<'tcx> { from_end: c.from_end, }) .collect(), - BorrowExpansion::Subslice { from, to, from_end } => { + PlaceExpansion::Subslice { from, to, from_end } => { vec![PlaceElem::Subslice { from: *from, to: *to, @@ -446,14 +446,14 @@ impl<'tcx, P: PCGNodeLike<'tcx> + HasPlace<'tcx> + Into>> pub(super) fn new( base: P, - expansion: BorrowExpansion<'tcx>, + expansion: PlaceExpansion<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, ) -> Result { if let LocalNode::Place(p) = base.into() && p.is_owned(repacker) { assert!( - matches!(expansion, BorrowExpansion::Deref), + matches!(expansion, PlaceExpansion::Deref), "Unexpected expansion for {:?}: {:?}", base, expansion @@ -466,7 +466,7 @@ impl<'tcx, P: PCGNodeLike<'tcx> + HasPlace<'tcx> + Into>> pub(super) fn from_borrowed_base( base: P, - expansion: BorrowExpansion<'tcx>, + expansion: PlaceExpansion<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, ) -> Result { if let LocalNode::Place(p) = base.into() { diff --git a/src/borrow_pcg/graph/aliases.rs b/src/borrow_pcg/graph/aliases.rs index 66048f43..cae98dda 100644 --- a/src/borrow_pcg/graph/aliases.rs +++ b/src/borrow_pcg/graph/aliases.rs @@ -54,7 +54,7 @@ impl<'tcx> BorrowsGraph<'tcx> { let mut results: FxHashSet> = FxHashSet::default(); for (place, proj) in node.iter_projections(repacker) { for c in results.iter() { - tracing::info!("{} {}", c.node.to_short_string(repacker), c.exact_alias); + tracing::debug!("{} {}", c.node.to_short_string(repacker), c.exact_alias); } results.insert(Alias { node: place.into(), diff --git a/src/borrow_pcg/state/obtain.rs b/src/borrow_pcg/state/obtain.rs index 868d2ca0..11aaccfd 100644 --- a/src/borrow_pcg/state/obtain.rs +++ b/src/borrow_pcg/state/obtain.rs @@ -1,7 +1,7 @@ use crate::borrow_pcg::action::executed_actions::ExecutedActions; use crate::borrow_pcg::action::BorrowPCGAction; use crate::borrow_pcg::borrow_pcg_edge::BorrowPCGEdge; -use crate::borrow_pcg::borrow_pcg_expansion::{BorrowExpansion, BorrowPCGExpansion}; +use crate::borrow_pcg::borrow_pcg_expansion::{PlaceExpansion, BorrowPCGExpansion}; use crate::borrow_pcg::edge::block::{BlockEdge, BlockEdgeKind}; use crate::borrow_pcg::edge::kind::BorrowPCGEdgeKind; use crate::borrow_pcg::graph::borrows_imgcat_debug; @@ -228,7 +228,7 @@ impl<'tcx> BorrowsState<'tcx> { if !target.is_owned(repacker) { let expansion = BorrowPCGExpansion::new( base.into(), - BorrowExpansion::from_places(expansion.clone(), repacker), + PlaceExpansion::from_places(expansion.clone(), repacker), repacker, )?; @@ -255,7 +255,7 @@ impl<'tcx> BorrowsState<'tcx> { if !dest_places.is_empty() { let expansion = BorrowPCGExpansion::from_borrowed_base( rp.into(), - BorrowExpansion::from_places(dest_places, repacker), + PlaceExpansion::from_places(dest_places, repacker), repacker, )?; self.record_and_apply_action( diff --git a/src/combined_pcs/domain.rs b/src/combined_pcs/domain.rs index 7a2aea78..3fb7978a 100644 --- a/src/combined_pcs/domain.rs +++ b/src/combined_pcs/domain.rs @@ -412,6 +412,7 @@ impl Debug for PlaceCapabilitySummary<'_, '_> { } impl JoinSemiLattice for PlaceCapabilitySummary<'_, '_> { + #[tracing::instrument(skip(self, other), fields(self_block = self.block().index(), other_block = other.block().index()))] fn join(&mut self, other: &Self) -> bool { if other.has_error() && !self.has_error() { self.pcg = other.pcg.clone(); diff --git a/src/combined_pcs/engine.rs b/src/combined_pcs/engine.rs index 28bbccb3..00fc9193 100644 --- a/src/combined_pcs/engine.rs +++ b/src/combined_pcs/engine.rs @@ -10,11 +10,14 @@ use std::{ rc::Rc, }; +use itertools::Itertools; + use crate::{ - free_pcs::CapabilitySummary, + borrow_pcg::{action::BorrowPCGActionKind, borrow_pcg_edge::BorrowPCGEdgeLike}, + free_pcs::{CapabilitySummary, RepackOp}, rustc_interface::{ borrowck::{ - self, BorrowSet, LocationTable, PoloniusInput, PoloniusOutput, RegionInferenceContext, + self, BorrowSet, LocationTable, PoloniusOutput, RegionInferenceContext, PoloniusInput, }, dataflow::Analysis, index::{bit_set::BitSet, Idx, IndexVec}, @@ -26,7 +29,6 @@ use crate::{ ty::{self, GenericArgsRef, TyCtxt}, }, }, - utils::Place, BodyAndBorrows, }; @@ -280,6 +282,67 @@ impl<'a, 'tcx> PCGEngine<'a, 'tcx> { ) { state.generate_dot_graph(phase.into(), statement_index); } + + fn restore_loaned_capabilities( + &self, + state: &mut PlaceCapabilitySummary<'a, 'tcx>, + ) -> Vec> { + let pcg = state.pcg_mut(); + let borrows = pcg.borrow.data.states[EvalStmtPhase::PostMain].frozen_graph(); + + // Restore capabilities for owned places that were previously lent out + // but are now no longer borrowed. + for action in pcg.borrow.actions[EvalStmtPhase::PreOperands].iter() { + if let BorrowPCGActionKind::RemoveEdge(edge) = &action.kind { + for place in edge + .blocked_places(self.cgx.rp) + .iter() + .flat_map(|p| p.as_current_place()) + { + if place.is_owned(self.cgx.rp) + && !borrows.contains(place.into(), self.cgx.rp) + { + tracing::debug!("Setting capability for place {:?} to Exclusive", place); + pcg.owned + .data + .get_mut(EvalStmtPhase::PostMain) + .set_capability(place, CapabilityKind::Exclusive); + } + } + } + } + let mut extra_ops = vec![]; + let fpcg_post_state = pcg.owned.data.get_mut(EvalStmtPhase::PostMain); + for caps in fpcg_post_state.capability_projections() { + let leaves = caps.leaves(self.cgx.rp); + + for place in leaves { + tracing::debug!("Setting capability for place {:?} to Exclusive", place); + if !borrows.contains(place.into(), self.cgx.rp) { + caps.set_capability(place, CapabilityKind::Exclusive); + } + } + + let mut expansions = caps + .expansions() + .clone() + .into_iter() + .sorted_by_key(|(p, _)| p.projection.len()) + .collect::>(); + while let Some((base, expansion)) = expansions.pop() { + let expansion_places = base.expansion_places(&expansion, self.cgx.rp); + if expansion_places + .iter() + .all(|p| !borrows.contains((*p).into(), self.cgx.rp)) && let Some(candidate_cap) = caps.get_capability(expansion_places[0]) && expansion_places + .iter() + .all(|p| caps.get_capability(*p) == Some(candidate_cap)) { + tracing::debug!("Collapsing {:?} to {:?}", expansion_places[0], base); + extra_ops.extend(caps.collapse(base, self.cgx.rp).unwrap()); + } + } + } + extra_ops + } } impl<'a, 'tcx> Analysis<'tcx> for PCGEngine<'a, 'tcx> { @@ -311,6 +374,7 @@ impl<'a, 'tcx> Analysis<'tcx> for PCGEngine<'a, 'tcx> { self.curr_block.set(START_BLOCK); state.pcg_mut().initialize_as_start_block(); } + #[tracing::instrument(skip(self, state, statement))] fn apply_before_statement_effect( &mut self, state: &mut Self::Domain, @@ -321,34 +385,15 @@ impl<'a, 'tcx> Analysis<'tcx> for PCGEngine<'a, 'tcx> { return; } self.initialize(state, location.block); - self.fpcs - .apply_before_statement_effect(state.owned_pcg_mut(), statement, location); self.borrows .apply_before_statement_effect(state.borrow_pcg_mut(), statement, location); - let pcg = state.pcg_mut(); - - let borrows = pcg.borrow.data.states[EvalStmtPhase::PostMain].frozen_graph(); + let mut extra_ops = self.restore_loaned_capabilities(state); + self.fpcs + .apply_before_statement_effect(state.owned_pcg_mut(), statement, location); + extra_ops.append(&mut state.pcg_mut().owned.actions[EvalStmtPhase::PreOperands]); + state.pcg_mut().owned.actions[EvalStmtPhase::PreOperands].extend(extra_ops); - // Restore capabilities for owned places that were previously lent out - // but are now no longer borrowed. - for cap in pcg.owned.post_operands_mut().iter_mut() { - match cap { - crate::free_pcs::CapabilityLocal::Unallocated => {} - crate::free_pcs::CapabilityLocal::Allocated(capability_projections) => { - let places: Vec> = - capability_projections.iter().map(|(p, _)| *p).collect(); - for p in places { - if !borrows.contains(p.into(), self.cgx.rp) - && capability_projections[&p].is_read() - && capability_projections.is_leaf(p) - { - capability_projections.insert(p, CapabilityKind::Exclusive); - } - } - } - } - } self.generate_dot_graph(state, DataflowStmtPhase::Initial, location.statement_index); self.generate_dot_graph(state, EvalStmtPhase::PreOperands, location.statement_index); self.generate_dot_graph(state, EvalStmtPhase::PostOperands, location.statement_index); @@ -369,6 +414,8 @@ impl<'a, 'tcx> Analysis<'tcx> for PCGEngine<'a, 'tcx> { self.generate_dot_graph(state, EvalStmtPhase::PreMain, location.statement_index); self.generate_dot_graph(state, EvalStmtPhase::PostMain, location.statement_index); } + + #[tracing::instrument(skip(self, state, terminator))] fn apply_before_terminator_effect( &mut self, state: &mut Self::Domain, @@ -381,8 +428,11 @@ impl<'a, 'tcx> Analysis<'tcx> for PCGEngine<'a, 'tcx> { self.initialize(state, location.block); self.borrows .apply_before_terminator_effect(state.borrow_pcg_mut(), terminator, location); + let mut extra_ops = self.restore_loaned_capabilities(state); self.fpcs .apply_before_terminator_effect(state.owned_pcg_mut(), terminator, location); + extra_ops.append(&mut state.pcg_mut().owned.actions[EvalStmtPhase::PreOperands]); + state.pcg_mut().owned.actions[EvalStmtPhase::PreOperands].extend(extra_ops); self.generate_dot_graph(state, DataflowStmtPhase::Initial, location.statement_index); self.generate_dot_graph(state, EvalStmtPhase::PreOperands, location.statement_index); self.generate_dot_graph(state, EvalStmtPhase::PostOperands, location.statement_index); diff --git a/src/free_pcs/impl/bridge.rs b/src/free_pcs/impl/bridge.rs index 881e0e02..46538eca 100644 --- a/src/free_pcs/impl/bridge.rs +++ b/src/free_pcs/impl/bridge.rs @@ -11,9 +11,7 @@ use crate::{ free_pcs::{ CapabilityKind, CapabilityLocal, CapabilityProjections, CapabilitySummary, RepackOp, }, - utils::{ - corrected::CorrectedPlace, display::DisplayWithRepacker, PlaceOrdering, PlaceRepacker, - }, + utils::{corrected::CorrectedPlace, PlaceRepacker}, }; pub trait RepackingBridgeSemiLattice<'tcx> { @@ -61,16 +59,14 @@ impl<'tcx> RepackingBridgeSemiLattice<'tcx> for CapabilityLocal<'tcx> { let mut cps = cps.clone(); let local = cps.get_local(); let mut repacks = Vec::new(); - for (p, mut k) in cps.iter_mut() { + for (p, k) in cps.place_capabilities_mut() { if *k > CapabilityKind::Write { - repacks.push(RepackOp::Weaken(p, *k, CapabilityKind::Write)); + repacks.push(RepackOp::Weaken(*p, *k, CapabilityKind::Write)); *k = CapabilityKind::Write; } } - if !cps.contains_key(&local.into()) { - let packs = cps - .collapse(cps.keys().copied().collect(), local.into(), repacker) - .unwrap(); + if cps.contains_expansion_from(local.into()) { + let packs = cps.collapse(local.into(), repacker).unwrap(); repacks.extend(packs); }; repacks.push(RepackOp::StorageDead(local)); @@ -93,70 +89,51 @@ impl<'tcx> RepackingBridgeSemiLattice<'tcx> for CapabilityProjections<'tcx> { other: &Self, repacker: PlaceRepacker<'_, 'tcx>, ) -> std::result::Result>, PCGError> { - // TODO: remove need for clone + let mut repacks = vec![]; let mut from = self.clone(); - - let mut repacks = Vec::new(); - for (&place, &kind) in other.iter().sorted_by_key(|(p, _)| p.projection.len()) { - let place = CorrectedPlace::new(place, repacker); - let related = from.find_all_related(*place, None); - if !from.contains_key(&place) { - for (from_place, _) in (*related) - .iter() - .copied() - .sorted_by_key(|(p, _)| p.projection.len()) - .rev() - { - match from_place.partial_cmp(*place).unwrap() { - PlaceOrdering::Prefix => { - let unpacks = from.expand( - from_place, - place, - CapabilityKind::Exclusive, - repacker, - )?; - repacks.extend(unpacks); - break; - } - PlaceOrdering::Suffix => { - let packs = from - .collapse(related.get_places(), *place, repacker) - .unwrap(); - repacks.extend(packs); - break; - } - PlaceOrdering::Both => { - let common_prefix = related.common_prefix(*place); - let collapse_repacks = - from.collapse(related.get_places(), common_prefix, repacker)?; - let expand_repacks = from.expand( - common_prefix, - place, - CapabilityKind::Exclusive, - repacker, - )?; - repacks.extend(collapse_repacks); - repacks.extend(expand_repacks); - break; - } - PlaceOrdering::Equal => {} + let other_expansions = other.expansions(); + 'outer: loop { + let from_expansions = from.expansions().clone(); + for (place, expansion) in from_expansions + .into_iter() + .sorted_by_key(|(p, _)| p.projection.len()) + .rev() + { + if let Some(other_expansion) = other_expansions.get(&place) { + if other_expansion != &expansion { + let collapse_repacks = from.collapse(place, repacker)?; + repacks.extend(collapse_repacks); + let expand_to = place.expansion_places(other_expansion, repacker)[0]; + repacks.extend(from.expand( + place, + CorrectedPlace::new(expand_to, repacker), + from.get_capability(place).unwrap(), + repacker, + )?); + continue; } + } else { + repacks.extend(from.collapse(place, repacker)?); + continue 'outer; } } - if !from.contains_key(&place) { - panic!( - "from does not contain place: {}. From: {:?}", - place.to_short_string(repacker), - from - ); - } - // Downgrade the permission if needed - let curr = from[&place]; - if curr > kind { - from.insert(*place, kind); - repacks.push(RepackOp::Weaken(*place, curr, kind)); - } else if curr < kind { - repacks.push(RepackOp::RegainLoanedCapability(*place, kind)); + break; + } + for (place, expansion) in other + .expansions() + .iter() + .sorted_by_key(|(p, _)| p.projection.len()) + { + if !from.expansions().contains_key(place) { + tracing::debug!("other expansion {:?} -> {:?}", place, expansion); + tracing::debug!("from: {:?}", from); + tracing::debug!("other: {:?}", other); + repacks.extend(from.expand( + *place, + CorrectedPlace::new(place.expansion_places(expansion, repacker)[0], repacker), + from.get_capability(*place).unwrap(), + repacker, + )?); } } Ok(repacks) diff --git a/src/free_pcs/impl/engine.rs b/src/free_pcs/impl/engine.rs index 7cd94d2b..adc7ca2f 100644 --- a/src/free_pcs/impl/engine.rs +++ b/src/free_pcs/impl/engine.rs @@ -84,12 +84,14 @@ impl<'a, 'tcx> Analysis<'tcx> for FpcsEngine<'a, 'tcx> { } impl<'a, 'tcx> FpcsEngine<'a, 'tcx> { + #[tracing::instrument(skip(self, state, tw))] fn apply_before( &self, state: &mut FreePlaceCapabilitySummary<'a, 'tcx>, tw: TripleWalker<'tcx>, location: Location, ) { + state.actions[EvalStmtPhase::PreOperands].clear(); if let Some(error) = tw.error { state.error = Some(error); return; @@ -100,9 +102,15 @@ impl<'a, 'tcx> FpcsEngine<'a, 'tcx> { for &triple in &tw.operand_triples { let triple = triple.replace_place(self.repacker); let pre_operands = state.data.states.get_mut(EvalStmtPhase::PreOperands); - if let Err(e) = pre_operands.requires(triple.pre(), self.repacker) { - state.error = Some(e); - return; + match pre_operands.requires(triple.pre(), self.repacker) { + Ok(ops) => { + tracing::debug!("Extend PreOperands with {:?}", ops); + state.actions[EvalStmtPhase::PreOperands].extend(ops); + } + Err(e) => { + state.error = Some(e); + return; + } } } @@ -122,6 +130,7 @@ impl<'a, 'tcx> FpcsEngine<'a, 'tcx> { tw: TripleWalker<'tcx>, location: Location, ) { + state.actions[EvalStmtPhase::PreMain].clear(); if let Some(error) = tw.error { state.error = Some(error); return; @@ -131,9 +140,15 @@ impl<'a, 'tcx> FpcsEngine<'a, 'tcx> { for &triple in &tw.main_triples { let triple = triple.replace_place(self.repacker); let pre_main = state.data.states.get_mut(EvalStmtPhase::PreMain); - if let Err(e) = pre_main.requires(triple.pre(), self.repacker) { - state.error = Some(e); - return; + match pre_main.requires(triple.pre(), self.repacker) { + Ok(ops) => { + tracing::debug!("Extend PreMain with {:?}", ops); + state.actions[EvalStmtPhase::PreMain].extend(ops); + } + Err(e) => { + state.error = Some(e); + return; + } } } diff --git a/src/free_pcs/impl/fpcs.rs b/src/free_pcs/impl/fpcs.rs index 988b7591..9aa64b86 100644 --- a/src/free_pcs/impl/fpcs.rs +++ b/src/free_pcs/impl/fpcs.rs @@ -10,19 +10,18 @@ use std::{ }; use derive_more::{Deref, DerefMut}; -use rustc_interface::{ +use crate::rustc_interface::{ index::Idx, index::IndexVec, middle::mir::{Local, RETURN_PLACE}, mir_dataflow::fmt::DebugWithContext, }; -use super::{engine::FpcsEngine, CapabilityKind, RepackingBridgeSemiLattice}; +use super::{engine::FpcsEngine, CapabilityKind}; use crate::{ combined_pcs::{EvalStmtPhase, PCGError, PCGErrorKind}, free_pcs::{CapabilityLocal, CapabilityProjections, RepackOp}, - rustc_interface, - utils::{domain_data::DomainData, PlaceRepacker}, + utils::{domain_data::DomainData, eval_stmt_data::EvalStmtData, PlaceRepacker}, }; pub(crate) struct RepackOps<'tcx> { @@ -35,14 +34,13 @@ pub struct FreePlaceCapabilitySummary<'a, 'tcx> { pub(crate) repacker: PlaceRepacker<'a, 'tcx>, pub(crate) data: DomainData>, pub(crate) error: Option, + pub(crate) actions: EvalStmtData>>, } impl<'a, 'tcx> FreePlaceCapabilitySummary<'a, 'tcx> { pub(crate) fn has_internal_error(&self) -> bool { self.error - .as_ref().is_some_and(|e| matches!(e.kind, PCGErrorKind::Internal(_))) - } - pub(crate) fn post_operands_mut(&mut self) -> &mut CapabilitySummary<'tcx> { - self.data.states.get_mut(EvalStmtPhase::PostOperands) + .as_ref() + .is_some_and(|e| matches!(e.kind, PCGErrorKind::Internal(_))) } pub(crate) fn new( @@ -53,6 +51,7 @@ impl<'a, 'tcx> FreePlaceCapabilitySummary<'a, 'tcx> { repacker, data: DomainData::new(capability_summary), error: None, + actions: EvalStmtData::default(), } } @@ -79,17 +78,12 @@ impl<'a, 'tcx> FreePlaceCapabilitySummary<'a, 'tcx> { } } - pub(crate) fn repack_ops( - &self, - previous: &CapabilitySummary<'tcx>, - ) -> std::result::Result, PCGError> { + pub(crate) fn repack_ops(&self) -> std::result::Result, PCGError> { if let Some(error) = &self.error { return Err(error.clone()); } - let start = - previous.bridge(&self.data.states[EvalStmtPhase::PreOperands], self.repacker)?; - let middle = self.data.states[EvalStmtPhase::PostOperands] - .bridge(&self.data.states[EvalStmtPhase::PreMain], self.repacker)?; + let start = self.actions[EvalStmtPhase::PreOperands].clone(); + let middle = self.actions[EvalStmtPhase::PreMain].clone(); Ok(RepackOps { start, middle }) } } @@ -113,9 +107,7 @@ impl<'a, 'tcx> DebugWithContext> for FreePlaceCapabilitySum _ctxt: &FpcsEngine<'a, 'tcx>, f: &mut Formatter<'_>, ) -> Result { - let RepackOps { start, middle } = self - .repack_ops(&old.data.states[EvalStmtPhase::PostMain]) - .unwrap(); + let RepackOps { start, middle } = self.repack_ops().unwrap(); if !start.is_empty() { writeln!(f, "{start:?}")?; } @@ -154,11 +146,11 @@ impl<'a, 'tcx> DebugWithContext> for FreePlaceCapabilitySum /// The free pcs of all locals pub struct CapabilitySummary<'tcx>(IndexVec>); -impl Default for CapabilitySummary<'_> { - fn default() -> Self { - Self::empty() - } -} +// impl Default for CapabilitySummary<'_> { +// fn default() -> Self { +// Self::empty() +// } +// } impl Debug for CapabilitySummary<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> Result { @@ -168,6 +160,13 @@ impl Debug for CapabilitySummary<'_> { } impl<'tcx> CapabilitySummary<'tcx> { + pub(crate) fn capability_projections(&mut self) -> Vec<&mut CapabilityProjections<'tcx>> { + self.0 + .iter_mut() + .filter(|c| !c.is_unallocated()) + .map(|c| c.get_allocated_mut()) + .collect() + } pub(crate) fn debug_lines(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec { self.0 .iter() @@ -176,8 +175,10 @@ impl<'tcx> CapabilitySummary<'tcx> { .concat() } pub fn default(local_count: usize) -> Self { - Self(IndexVec::from_elem_n( - CapabilityLocal::default(), + Self(IndexVec::from_fn_n( + |i| { + CapabilityLocal::Allocated(CapabilityProjections::new(i, CapabilityKind::Exclusive)) + }, local_count, )) } @@ -208,36 +209,37 @@ impl Debug for CapabilitySummaryCompare<'_, '_> { write!(f, "\u{001f}+{:?}", a.get_local())?; true } - (CapabilityLocal::Allocated(new), CapabilityLocal::Allocated(old)) => { - if new != old { - let mut new_set = CapabilityProjections::empty(); - let mut old_set = CapabilityProjections::empty(); - for (&p, &nk) in new.iter() { - match old.get(&p) { - Some(&ok) if nk == ok => (), - _ => { - new_set.insert(p, nk); - } - } - } - for (&p, &ok) in old.iter() { - match new.get(&p) { - Some(&nk) if nk == ok => (), - _ => { - old_set.insert(p, ok); - } - } - } - if !old_set.is_empty() { - write!(f, "\u{001f}-{old_set:?}")? - } - if !new_set.is_empty() { - write!(f, "\u{001f}+{new_set:?}")? - } - true - } else { - false - } + (CapabilityLocal::Allocated(_), CapabilityLocal::Allocated(_)) => { + true + // if new != old { + // let mut new_set = CapabilityProjections::empty(); + // let mut old_set = CapabilityProjections::empty(); + // for (&p, &nk) in new.iter() { + // match old.get(&p) { + // Some(&ok) if nk == ok => (), + // _ => { + // new_set.insert(p, nk); + // } + // } + // } + // for (&p, &ok) in old.iter() { + // match new.get(&p) { + // Some(&nk) if nk == ok => (), + // _ => { + // old_set.insert(p, ok); + // } + // } + // } + // if !old_set.is_empty() { + // write!(f, "\u{001f}-{old_set:?}")? + // } + // if !new_set.is_empty() { + // write!(f, "\u{001f}+{new_set:?}")? + // } + // true + // } else { + // false + // } } }; if changed { diff --git a/src/free_pcs/impl/join_semi_lattice.rs b/src/free_pcs/impl/join_semi_lattice.rs index 352d1d20..e5f23af2 100644 --- a/src/free_pcs/impl/join_semi_lattice.rs +++ b/src/free_pcs/impl/join_semi_lattice.rs @@ -6,18 +6,16 @@ use std::rc::Rc; -use itertools::Itertools; +use crate::combined_pcs::PCGError; use crate::rustc_interface::mir_dataflow::JoinSemiLattice; +use itertools::Itertools; use crate::{ - combined_pcs::{EvalStmtPhase, PCGInternalError}, + combined_pcs::EvalStmtPhase, free_pcs::{ - CapabilityKind, CapabilityLocal, CapabilityProjections, CapabilitySummary, - FreePlaceCapabilitySummary, - }, - utils::{ - corrected::CorrectedPlace, PlaceOrdering, PlaceRepacker, + CapabilityLocal, CapabilityProjections, CapabilitySummary, FreePlaceCapabilitySummary, }, + utils::PlaceRepacker, }; impl JoinSemiLattice for FreePlaceCapabilitySummary<'_, '_> { @@ -28,7 +26,7 @@ impl JoinSemiLattice for FreePlaceCapabilitySummary<'_, '_> { match entry_state.join(&other.data.states[EvalStmtPhase::PostMain], self.repacker) { Ok(changed) => changed, Err(e) => { - self.error = Some(e.into()); + self.error = Some(e); false } } @@ -36,19 +34,11 @@ impl JoinSemiLattice for FreePlaceCapabilitySummary<'_, '_> { } pub(crate) trait RepackingJoinSemiLattice<'tcx> { - fn join( - &mut self, - other: &Self, - repacker: PlaceRepacker<'_, 'tcx>, - ) -> Result; + fn join(&mut self, other: &Self, repacker: PlaceRepacker<'_, 'tcx>) -> Result; } impl<'tcx> RepackingJoinSemiLattice<'tcx> for CapabilitySummary<'tcx> { - fn join( - &mut self, - other: &Self, - repacker: PlaceRepacker<'_, 'tcx>, - ) -> Result { + fn join(&mut self, other: &Self, repacker: PlaceRepacker<'_, 'tcx>) -> Result { let mut changed = false; for (l, to) in self.iter_enumerated_mut() { let local_changed = to.join(&other[l], repacker)?; @@ -59,11 +49,7 @@ impl<'tcx> RepackingJoinSemiLattice<'tcx> for CapabilitySummary<'tcx> { } impl<'tcx> RepackingJoinSemiLattice<'tcx> for CapabilityLocal<'tcx> { - fn join( - &mut self, - other: &Self, - repacker: PlaceRepacker<'_, 'tcx>, - ) -> Result { + fn join(&mut self, other: &Self, repacker: PlaceRepacker<'_, 'tcx>) -> Result { match (&mut *self, other) { (CapabilityLocal::Unallocated, CapabilityLocal::Unallocated) => Ok(false), (CapabilityLocal::Allocated(to_places), CapabilityLocal::Allocated(from_places)) => { @@ -80,90 +66,47 @@ impl<'tcx> RepackingJoinSemiLattice<'tcx> for CapabilityLocal<'tcx> { } impl<'tcx> RepackingJoinSemiLattice<'tcx> for CapabilityProjections<'tcx> { - fn join( - &mut self, - other: &Self, - repacker: PlaceRepacker<'_, 'tcx>, - ) -> Result { - if self.is_empty() { - // Handle the bottom case - *self = other.clone(); - return Ok(true); - } + fn join(&mut self, other: &Self, repacker: PlaceRepacker<'_, 'tcx>) -> Result { let mut changed = false; - for (&place, &kind) in other.iter().sorted_by_key(|(p, _)| p.projection.len()) { - let related = self.find_all_related(place, None); - for (from_place, _) in (*related).iter().copied() { - let mut done_with_place = false; - let final_place = match from_place.partial_cmp(place).unwrap() { - PlaceOrdering::Prefix => { - let from = related.get_only_place(); - let joinable_place = if self[&from] != CapabilityKind::Exclusive { - // One cannot expand a `Write` or a `ShallowInit` capability - from - } else { - from.joinable_to(place) - }; - assert!(from.is_prefix(joinable_place)); - if joinable_place != from { - changed = true; - self.expand( - from, - CorrectedPlace::new(joinable_place, repacker), - CapabilityKind::Exclusive, - repacker, - ) - .unwrap(); - } - Some(joinable_place) - } - PlaceOrdering::Equal => Some(place), - PlaceOrdering::Suffix => { - // Downgrade the permission if needed - for &(p, k) in &*related { - // Might not contain key if `p.projects_ptr(repacker)` - // returned `Some` in a previous iteration. - if !self.contains_key(&p) { - continue; - } - let collapse_to = if kind != CapabilityKind::Exclusive { - place - } else { - place.joinable_to(p) - }; - if collapse_to != p { - changed = true; - let mut from = related.get_places(); - from.retain(|&from| collapse_to.is_prefix(from)); - self.collapse(from, collapse_to, repacker).unwrap(); - } - if k > kind { - changed = true; - self.update_cap(collapse_to, kind); - } - } - None - } - PlaceOrdering::Both => { + 'outer: loop { + let expansions = self.expansions().clone(); + for (place, other_expansion) in other + .expansions() + .iter() + .sorted_by_key(|(p, _)| p.projection.len()) + { + if let Some(self_expansion) = expansions.get(place) { + if other_expansion != self_expansion { + tracing::debug!("collapse to {:?}", place); + self.collapse(*place, repacker)?; + tracing::debug!("self: {:?}", self); changed = true; - let cp = related.common_prefix(place); - self.collapse(related.get_places(), cp, repacker)?; - done_with_place = true; - Some(cp) + continue 'outer; } - }; - if let Some(place) = final_place { - // Downgrade the permission if needed - if self[&place] > kind { - changed = true; - self.update_cap(place, kind); + } else if self.contains_expansion_to(*place, repacker) { + // Otherwise, this is an expansion from a place that won't survive the join + tracing::debug!("insert expansion {:?} -> {:?}", place, other_expansion); + tracing::debug!("other: {:?}", other); + self.insert_expansion(*place, other_expansion.clone(), repacker); + if let Some(cap) = other.get_capability(*place) { + self.set_capability(*place, cap); + } else { + self.remove_capability(*place); } - } - if done_with_place { - break; + for place in place.expansion_places(other_expansion, repacker) { + if let Some(cap) = other.get_capability(place) { + self.set_capability(place, cap); + } else { + self.remove_capability(place); + } + } + changed = true; + continue 'outer; } } + break; } + tracing::debug!("self: {:?}", self); Ok(changed) } } diff --git a/src/free_pcs/impl/local.rs b/src/free_pcs/impl/local.rs index 01bca295..d7316c90 100644 --- a/src/free_pcs/impl/local.rs +++ b/src/free_pcs/impl/local.rs @@ -6,19 +6,21 @@ use std::fmt::{Debug, Formatter, Result}; -use derive_more::Deref; -use rustc_interface::{ - data_structures::fx::{FxHashMap, FxHashSet}, - middle::mir::Local, +use crate::{ + borrow_pcg::borrow_pcg_expansion::PlaceExpansion, + rustc_interface::{ + data_structures::fx::FxHashMap, + middle::mir::Local, + }, }; +use itertools::Itertools; use crate::{ combined_pcs::{PCGError, PCGInternalError}, - free_pcs::{CapabilityKind, RelatedSet, RepackOp}, - pcg_validity_assert, rustc_interface, + free_pcs::{CapabilityKind, RepackOp}, + pcg_validity_assert, utils::{ - corrected::CorrectedPlace, display::DisplayWithRepacker, ExpandStep, Place, PlaceOrdering, - PlaceRepacker, + corrected::CorrectedPlace, display::DisplayWithRepacker, Place, PlaceRepacker, }, validity_checks_enabled, }; @@ -40,12 +42,6 @@ impl Debug for CapabilityLocal<'_> { } } -impl Default for CapabilityLocal<'_> { - fn default() -> Self { - Self::Allocated(CapabilityProjections::empty()) - } -} - impl<'tcx> CapabilityLocal<'tcx> { pub(crate) fn debug_lines(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec { match self { @@ -99,6 +95,7 @@ impl std::ops::DerefMut for DropGuard<'_, S, T> { impl<'a, S: CheckValidityOnExpiry, T> DropGuard<'a, S, T> { /// Caller must ensure that value borrows from `source` + #[allow(dead_code)] pub(crate) unsafe fn new(source: *const S, value: &'a mut T) -> Self { Self { source, value } } @@ -113,180 +110,110 @@ impl Drop for DropGuard<'_, S, T> { } } -#[derive(Clone, PartialEq, Eq, Deref)] -/// The permissions for all the projections of a place -// We only need the projection part of the place -pub struct CapabilityProjections<'tcx>(FxHashMap, CapabilityKind>); - -impl Debug for CapabilityProjections<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - self.0.fmt(f) - } +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct CapabilityProjections<'tcx> { + local: Local, + expansions: FxHashMap, PlaceExpansion<'tcx>>, + capabilities: FxHashMap, CapabilityKind>, } impl CheckValidityOnExpiry for CapabilityProjections<'_> { - fn check_validity_on_expiry(&self) { - self.check_validity(); - } + fn check_validity_on_expiry(&self) {} } impl<'tcx> CapabilityProjections<'tcx> { - pub(crate) fn is_leaf(&self, place: Place<'tcx>) -> bool { - self.0.iter().all(|(p, _)| p.is_prefix(place)) - } - - pub(crate) fn get_mut(&mut self, place: Place<'tcx>) -> Option<&mut CapabilityKind> { - self.0.get_mut(&place) - } - - pub(crate) fn insert(&mut self, place: Place<'tcx>, cap: CapabilityKind) { - self.0.insert(place, cap); - } - - #[allow(dead_code)] - pub(crate) fn extend( + pub(crate) fn insert_expansion( &mut self, - other: impl IntoIterator, CapabilityKind)>, + place: Place<'tcx>, + expansion: PlaceExpansion<'tcx>, + repacker: PlaceRepacker<'_, 'tcx>, ) { - self.0.extend(other); - if validity_checks_enabled() { - self.check_validity(); - } + pcg_validity_assert!( + self.contains_expansion_to(place, repacker), + "{:?} does not contain expansion to {:?}", + self, + place + ); + self.expansions.insert(place, expansion); } - pub(crate) fn remove(&mut self, place: &Place<'tcx>) -> Option { - self.0.remove(place) + pub(crate) fn expansions(&self) -> &FxHashMap, PlaceExpansion<'tcx>> { + &self.expansions } - pub(crate) fn iter_mut<'a>( - &'a mut self, - ) -> impl Iterator< - Item = ( - Place<'tcx>, - DropGuard<'a, CapabilityProjections<'tcx>, CapabilityKind>, - ), - > { - struct Iter<'a, 'tcx> { - inner: *mut CapabilityProjections<'tcx>, - places: Vec>, - idx: usize, - _marker: std::marker::PhantomData<&'a ()>, - } - - impl<'a, 'tcx: 'a> Iterator for Iter<'a, 'tcx> { - type Item = ( - Place<'tcx>, - DropGuard<'a, CapabilityProjections<'tcx>, CapabilityKind>, - ); - - fn next(&mut self) -> Option { - if self.idx >= self.places.len() { - None - } else { - let place: Place<'tcx> = self.places[self.idx]; - // SAFETY: As long as `Iter<'a, tcx> is live, self.inner is blocked` - let capability: &'a mut CapabilityKind = - unsafe { self.inner.as_mut().unwrap().get_mut(place).unwrap() }; - self.idx += 1; - // SAFETY: `capability` borrows from self.inner - let guard = unsafe { DropGuard::new(self.inner, capability) }; - Some((place, guard)) - } - } - } - - let places = self.0.keys().copied().collect(); - Iter { - inner: self, - places, - idx: 0, - _marker: std::marker::PhantomData, - } + pub(crate) fn leaves(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec> { + self.expansions + .iter() + .flat_map(|(p, e)| p.expansion_places(e, repacker)) + .filter(|p| !self.contains_expansion_from(*p)) + .collect::>() } - fn check_validity(&self) { - for (place, cap) in &**self { - if cap.is_exclusive() { - for (other_place, other_cap) in &**self { - if other_place.is_strict_prefix(*place) && other_cap.is_exclusive() { - panic!( - "Found place {:?} with Exclusive capability that has a prefix {:?} also with Exclusive capability", - place, - other_place - ); - } - } - } - } - } pub(crate) fn debug_lines(&self, repacker: PlaceRepacker<'_, 'tcx>) -> Vec { - self.iter() + self.capabilities + .iter() .map(|(p, k)| format!("{}: {:?}", p.to_short_string(repacker), k)) .collect() } pub fn new(local: Local, perm: CapabilityKind) -> Self { - Self([(local.into(), perm)].into_iter().collect()) + Self { + local, + expansions: FxHashMap::default(), + capabilities: FxHashMap::from_iter([(local.into(), perm)]), + } } pub fn new_uninit(local: Local) -> Self { Self::new(local, CapabilityKind::Write) } - /// Should only be called when creating an update within `ModifiesFreeState` - pub(crate) fn empty() -> Self { - Self(FxHashMap::default()) - } - pub(crate) fn get_local(&self) -> Local { - self.iter().next().unwrap().0.local - } - - pub(crate) fn update_cap(&mut self, place: Place<'tcx>, cap: CapabilityKind) { - self.insert(place, cap); - if validity_checks_enabled() { - self.check_validity(); - } - // assert!(old.is_some()); + pub(crate) fn contains_expansion_from(&self, place: Place<'tcx>) -> bool { + self.expansions.contains_key(&place) } - /// Returns all related projections of the given place that are contained in this map. - /// A `Ordering::Less` means that the given `place` is a prefix of the iterator place. - /// For example: find_all_related(x.f.g) = [(Less, x.f.g.h), (Greater, x.f)] - /// It also checks that the ordering conforms to the expected ordering (the above would - /// fail in any situation since all orderings need to be the same) - pub(crate) fn find_all_related( + pub(crate) fn contains_expansion_to( &self, - to: Place<'tcx>, - mut expected: Option, - ) -> RelatedSet<'tcx> { - // let mut minimum = None::; - let mut related = Vec::new(); - for (&from, &cap) in &**self { - if let Some(ord) = from.partial_cmp(to) { - // let cap_no_read = cap.read_as_exclusive(); - // minimum = if let Some(min) = minimum { - // Some(min.minimum(cap_no_read).unwrap()) - // } else { - // Some(cap_no_read) - // }; - if let Some(_expected) = expected { - // assert_eq!(ord, expected, "({self:?}) {from:?} {to:?}"); + place: Place<'tcx>, + repacker: PlaceRepacker<'_, 'tcx>, + ) -> bool { + match place.last_projection() { + Some((p, _)) => { + if let Some(expansion) = self.expansions.get(&p) { + p.expansion_places(expansion, repacker).contains(&place) } else { - expected = Some(ord); + false } - related.push((from, cap)); } + None => place.local == self.local, } - assert!( - !related.is_empty(), - "Cannot find related of {to:?} in {self:?}" - ); - let relation = expected.unwrap(); - if matches!(relation, PlaceOrdering::Prefix | PlaceOrdering::Equal) { - // assert_eq!(related.len(), 1); - } - RelatedSet::new(related.into_iter().collect()) } + pub(crate) fn get_local(&self) -> Local { + self.local + } + + pub(crate) fn set_capability(&mut self, place: Place<'tcx>, cap: CapabilityKind) { + self.capabilities.insert(place, cap); + } + + pub(crate) fn remove_capability(&mut self, place: Place<'tcx>) -> Option { + self.capabilities.remove(&place) + } + + pub(crate) fn get_capability(&self, place: Place<'tcx>) -> Option { + self.capabilities.get(&place).copied() + } + + pub(crate) fn place_capabilities_mut(&mut self) -> &mut FxHashMap, CapabilityKind> { + &mut self.capabilities + } + + pub(crate) fn place_capabilities(&self) -> &FxHashMap, CapabilityKind> { + &self.capabilities + } + + fn check_validity(&self) {} + pub(crate) fn expand( &mut self, from: Place<'tcx>, @@ -294,22 +221,29 @@ impl<'tcx> CapabilityProjections<'tcx> { for_cap: CapabilityKind, repacker: PlaceRepacker<'_, 'tcx>, ) -> std::result::Result>, PCGError> { - assert!( - !from.is_mut_ref(repacker.body(), repacker.tcx()), - "Mutable reference {:?} should be expanded in borrow PCG, not owned PCG", - from - ); - pcg_validity_assert!( - !self.contains_key(&to), - "We don't need to expand to {} because it already has a capability ({:?})", - to.to_short_string(repacker), - self.get(&to) - ); - - let (expand_steps, other_expanded_places) = from.expand(*to, repacker)?; + // assert!( + // !from.is_mut_ref(repacker.body(), repacker.tcx()), + // "Mutable reference {:?} should be expanded in borrow PCG, not owned PCG", + // from + // ); + // pcg_validity_assert!( + // self.contains_key(&from), + // "No capability for {} in {:?}", + // from.to_short_string(repacker), + // self + // ); + // pcg_validity_assert!( + // !self.contains_key(&to), + // "We don't need to expand to {} because it already has a capability ({:?})", + // to.to_short_string(repacker), + // self.get(&to) + // ); + + let from_cap = self.get_capability(from).unwrap(); + + let expansion = from.expand(*to, repacker)?; // Update permission of `from` place - let from_cap = *self.get(&from).unwrap(); let other_place_perm = from_cap; let projection_path_perm = if for_cap.is_read() { Some(CapabilityKind::Read) @@ -317,32 +251,38 @@ impl<'tcx> CapabilityProjections<'tcx> { None }; - for place in other_expanded_places.iter() { - self.insert(*place, other_place_perm); + for place in expansion.other_expansions() { + self.set_capability(place, other_place_perm); } let mut ops = Vec::new(); - for ExpandStep { - base_place, - projected_place, - kind, - } in expand_steps - { + for expansion in expansion.expansions() { + self.insert_expansion( + expansion.base_place(), + PlaceExpansion::from_places(expansion.expansion(), repacker), + repacker, + ); if let Some(perm) = projection_path_perm { - self.insert(base_place, perm); + self.set_capability(expansion.base_place(), perm); } else { - self.remove(&base_place); + self.remove_capability(expansion.base_place()); } - - if kind.is_box() && from_cap.is_shallow_exclusive() { - ops.push(RepackOp::DerefShallowInit(base_place, projected_place)); + if expansion.kind.is_box() && from_cap.is_shallow_exclusive() { + ops.push(RepackOp::DerefShallowInit( + expansion.base_place(), + expansion.target_place, + )); } else { - ops.push(RepackOp::Expand(base_place, projected_place, for_cap)); + ops.push(RepackOp::Expand( + expansion.base_place(), + expansion.target_place, + for_cap, + )); } } - self.insert(*to, from_cap); + self.set_capability(*to, from_cap); if validity_checks_enabled() { self.check_validity(); @@ -355,75 +295,33 @@ impl<'tcx> CapabilityProjections<'tcx> { // state can always be packed up to the root pub(crate) fn collapse( &mut self, - from: FxHashSet>, to: Place<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, ) -> std::result::Result>, PCGInternalError> { - let mut old_caps: FxHashMap<_, _> = from + let expansions = self + .expansions .iter() - .flat_map(|&p| self.remove(&p).map(|cap| (p, cap))) + .filter(|(p, _)| to.is_prefix(**p)) + .map(|(p, e)| (*p, e.clone())) + .sorted_by_key(|(p, _)| p.projection.len()) + .rev() + .collect::>(); + let ops = expansions + .into_iter() + .map(|(p, expansion)| { + let expansion_places = p.expansion_places(&expansion, repacker); + let retained_cap = expansion_places.iter().fold( + CapabilityKind::Exclusive, + |acc, place| match self.remove_capability(*place) { + Some(cap) => acc.minimum(cap).unwrap_or(CapabilityKind::Write), + None => acc, + }, + ); + self.set_capability(p, retained_cap); + self.expansions.remove(&p); + RepackOp::Collapse(p, expansion_places[0], retained_cap) + }) .collect(); - let collapsed = to.collapse(from, repacker); - let mut exclusive_at = Vec::new(); - if !to.projects_shared_ref(repacker) { - for ExpandStep { - projected_place: to, - kind, - .. - } in &collapsed - { - if kind.is_shared_ref() { - let mut is_prefixed = false; - exclusive_at - .extract_if(|old| { - let cmp = to.either_prefix(*old); - if matches!(cmp, Some(false)) { - is_prefixed = true; - } - cmp.unwrap_or_default() - }) - .for_each(drop); - if !is_prefixed { - exclusive_at.push(*to); - } - } - } - } - let mut ops = Vec::new(); - for ExpandStep { - base_place, - projected_place, - .. - } in &collapsed - { - let removed_perms: Vec<_> = old_caps - .extract_if(|old, _| projected_place.is_prefix(*old)) - .collect(); - let perm = removed_perms - .iter() - .fold(CapabilityKind::Exclusive, |acc, (_, p)| { - acc.minimum(*p).unwrap_or_else(|| { - panic!("Minimum of {:?}", removed_perms); - }) - }); - for (from, from_perm) in removed_perms { - match from_perm.partial_cmp(&perm) { - Some(std::cmp::Ordering::Equal) => {} // Equal, do nothing - Some(std::cmp::Ordering::Greater) => { - ops.push(RepackOp::Weaken(from, from_perm, perm)); - } - _ => panic!("Weaken {:?} {:?} {:?}", from, from_perm, perm), - } - } - old_caps.insert(*base_place, perm); - ops.push(RepackOp::Collapse(*base_place, *projected_place, perm)); - } - pcg_validity_assert!( - old_caps.contains_key(&to), - "Old capabilities does not have a capability for collapse target {}", - to.to_short_string(repacker) - ); - self.insert(to, old_caps[&to]); if validity_checks_enabled() { self.check_validity(); } diff --git a/src/free_pcs/impl/mod.rs b/src/free_pcs/impl/mod.rs index 7f90ba9a..2f48a85f 100644 --- a/src/free_pcs/impl/mod.rs +++ b/src/free_pcs/impl/mod.rs @@ -9,7 +9,7 @@ mod local; mod place; pub(crate) mod engine; pub(crate) mod join_semi_lattice; -mod triple; +pub(crate) mod triple; mod update; mod bridge; diff --git a/src/free_pcs/impl/place.rs b/src/free_pcs/impl/place.rs index 806c9aec..0cfd3445 100644 --- a/src/free_pcs/impl/place.rs +++ b/src/free_pcs/impl/place.rs @@ -12,51 +12,53 @@ use std::{ use crate::rustc_interface::data_structures::fx::FxHashSet; -use crate::{utils::Place, validity_checks_enabled}; +use crate::utils::Place; #[derive(Debug, Deref)] pub(crate) struct RelatedSet<'tcx>(FxHashSet<(Place<'tcx>, CapabilityKind)>); impl<'tcx> RelatedSet<'tcx> { pub fn new(related: FxHashSet<(Place<'tcx>, CapabilityKind)>) -> Self { - if validity_checks_enabled() { - // Assert that for each place with Capability Exclusive, there is no prefix also with exclusive - for &(place, cap) in &related { - if cap == CapabilityKind::Exclusive { - for &(other_place, other_cap) in &related { - if other_place.is_strict_prefix(place) - && other_cap == CapabilityKind::Exclusive - { - panic!( - "Found place {:?} with Exclusive capability that has a prefix {:?} also with Exclusive capability", - place, - other_place - ); - } - } - } - } - } + // if validity_checks_enabled() { + // // Assert that for each place with Capability Exclusive, there is no prefix also with exclusive + // for &(place, cap) in &related { + // if cap == CapabilityKind::Exclusive { + // for &(other_place, other_cap) in &related { + // if other_place.is_strict_prefix(place) + // && other_cap == CapabilityKind::Exclusive + // { + // panic!( + // "Found place {:?} with Exclusive capability that has a prefix {:?} also with Exclusive capability", + // place, + // other_place + // ); + // } + // } + // } + // } + // } Self(related) } - pub fn get_places(&self) -> FxHashSet> { - self.0.iter().map(|(p, _)| *p).collect() - } - - pub fn get_only_place(&self) -> Place<'tcx> { - assert_eq!(self.0.len(), 1); - self.0.iter().next().unwrap().0 + pub(crate) fn place_to_collapse_to( + &self, + to: Place<'tcx>, + for_cap: CapabilityKind, + ) -> Place<'tcx> { + self.0.iter().fold(to.local.into(), |best, (p, cap)| { + if p.projection.len() >= best.projection.len() + && p.projection.len() <= to.projection.len() + && *cap >= for_cap + { + let result = to.common_prefix(*p); + if result.projection.len() > best.projection.len() { + return result; + } + } + best + }) } - pub fn common_prefix(&self, to: Place<'tcx>) -> Place<'tcx> { - self.get_places() - .iter() - .fold(to, |acc, p| acc.common_prefix(*p)) - } - pub(crate) fn len(&self) -> usize { - self.0.len() - } } #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -109,7 +111,6 @@ impl PartialOrd for CapabilityKind { (CapabilityKind::Exclusive, CapabilityKind::Write) => Some(Ordering::Greater), (CapabilityKind::Write, CapabilityKind::Exclusive) => Some(Ordering::Less), - // Transitive relationships through LentShared (CapabilityKind::Exclusive, CapabilityKind::Read) => Some(Ordering::Greater), (CapabilityKind::Read, CapabilityKind::Exclusive) => Some(Ordering::Less), diff --git a/src/free_pcs/impl/triple.rs b/src/free_pcs/impl/triple.rs index a9cc3219..649ce148 100644 --- a/src/free_pcs/impl/triple.rs +++ b/src/free_pcs/impl/triple.rs @@ -43,6 +43,7 @@ impl<'tcx> Triple<'tcx> { #[derive(Debug, Clone, Copy)] pub(crate) enum Condition<'tcx> { Capability(Place<'tcx>, CapabilityKind), + RemoveCapability(Place<'tcx>), AllocateOrDeallocate(Local), Unalloc(Local), Return, @@ -211,7 +212,7 @@ impl<'tcx> Visitor<'tcx> for TripleWalker<'tcx> { BorrowKind::Fake(..) => return, BorrowKind::Mut { .. } => Triple { pre: Condition::exclusive(*place), - post: None, + post: Some(Condition::RemoveCapability((*place).into())), }, }; self.main_triples.push(triple); diff --git a/src/free_pcs/impl/update.rs b/src/free_pcs/impl/update.rs index 52ad4189..66898d0f 100644 --- a/src/free_pcs/impl/update.rs +++ b/src/free_pcs/impl/update.rs @@ -6,28 +6,36 @@ use crate::{ combined_pcs::PCGError, - free_pcs::{CapabilityKind, CapabilityLocal, CapabilityProjections}, + free_pcs::{CapabilityKind, CapabilityLocal, CapabilityProjections, RepackOp}, pcg_validity_assert, rustc_interface::middle::mir::{Local, RETURN_PLACE}, - utils::{ - corrected::CorrectedPlace, display::DisplayWithRepacker, LocalMutationIsAllowed, Place, - PlaceOrdering, PlaceRepacker, - }, + utils::{corrected::CorrectedPlace, LocalMutationIsAllowed, Place, PlaceRepacker}, }; use super::{ triple::{Condition, Triple}, - CapabilitySummary, + CapabilitySummary, RelatedSet, }; impl<'tcx> CapabilitySummary<'tcx> { + pub(crate) fn set_capability(&mut self, place: Place<'tcx>, cap: CapabilityKind) { + self[place.local] + .get_allocated_mut() + .set_capability(place, cap) + } + pub(crate) fn requires( &mut self, cond: Condition<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Result<(), PCGError> { + ) -> Result>, PCGError> { match cond { - Condition::Unalloc(_) => {} + Condition::RemoveCapability(place) => { + let cp = self[place.local].get_allocated_mut(); + cp.remove_capability(place); + Ok(vec![]) + } + Condition::Unalloc(_) => Ok(vec![]), Condition::AllocateOrDeallocate(local) => { match &mut self[local] { cap @ CapabilityLocal::Unallocated => { @@ -36,19 +44,19 @@ impl<'tcx> CapabilitySummary<'tcx> { // precondition of SD can be met, but we'll catch this in // `bridge` and emit a IgnoreSD op. *cap = CapabilityLocal::Allocated(CapabilityProjections::new_uninit(local)); + Ok(vec![]) } - CapabilityLocal::Allocated(_) => { - self.requires( - Condition::Capability(local.into(), CapabilityKind::Write), - repacker, - )?; - } + CapabilityLocal::Allocated(_) => self.requires( + Condition::Capability(local.into(), CapabilityKind::Write), + repacker, + ), } } Condition::Capability(place, cap) => { let cp = self[place.local].get_allocated_mut(); - cp.repack(place, repacker, cap)?; - cp.insert(place, cap); + let result = cp.repack(place, repacker, cap)?; + cp.set_capability(place, cap); + Ok(result) } Condition::Return => { let always_live = repacker.always_live_locals(); @@ -63,13 +71,14 @@ impl<'tcx> CapabilitySummary<'tcx> { }; self.check_pre_satisfied(pre, repacker); } + Ok(vec![]) } } - Ok(()) } fn check_pre_satisfied(&self, pre: Condition<'tcx>, repacker: PlaceRepacker<'_, 'tcx>) { match pre { + Condition::RemoveCapability(_place) => {} Condition::Unalloc(local) => { assert!( self[local].is_unallocated(), @@ -129,6 +138,10 @@ impl<'tcx> CapabilitySummary<'tcx> { }; match post { Condition::Return => unreachable!(), + Condition::RemoveCapability(place) => { + let cp = self[place.local].get_allocated_mut(); + cp.remove_capability(place); + } Condition::Unalloc(local) => { self[local] = CapabilityLocal::Unallocated; } @@ -136,72 +149,70 @@ impl<'tcx> CapabilitySummary<'tcx> { self[local] = CapabilityLocal::Allocated(CapabilityProjections::new_uninit(local)); } Condition::Capability(place, cap) => { - self[place.local].get_allocated_mut().update_cap(place, cap); + self[place.local] + .get_allocated_mut() + .set_capability(place, cap); } } } } impl<'tcx> CapabilityProjections<'tcx> { + fn get_longest_prefix(&self, to: Place<'tcx>) -> Place<'tcx> { + self.place_capabilities() + .iter() + .filter(|(p, _)| p.is_prefix(to)) + .max_by_key(|(p, _)| p.projection.len()) + .map(|(p, _)| *p) + .unwrap_or(self.get_local().into()) + } + + pub(crate) fn place_to_collapse_to( + &self, + to: Place<'tcx>, + for_cap: CapabilityKind, + ) -> Place<'tcx> { + let related = self.find_all_related(to); + related.place_to_collapse_to(to, for_cap) + } + + pub(crate) fn find_all_related(&self, to: Place<'tcx>) -> RelatedSet<'tcx> { + RelatedSet::new( + self.place_capabilities() + .iter() + .filter(|(p, _)| (*p).partial_cmp(&to).is_some()) + .map(|(p, c)| (*p, *c)) + .collect(), + ) + } + + #[tracing::instrument(skip(self, repacker))] pub(super) fn repack( &mut self, to: Place<'tcx>, repacker: PlaceRepacker<'_, 'tcx>, for_cap: CapabilityKind, - ) -> Result<(), PCGError> { - let related = self.find_all_related(to, None); - if for_cap.is_read() { - if self.contains_key(&to) { - return Ok(()); - } - if let Some((projection_candidate, _)) = related - .iter() - .filter(|(p, _)| p.is_strict_prefix(to)) - .max_by_key(|(p, _)| p.projection.len()) - { - self.expand( - *projection_candidate, - CorrectedPlace::new(to, repacker), - for_cap, - repacker, - )?; - return Ok(()); - } - } - for (from_place, cap) in (*related).iter().copied() { - match from_place.partial_cmp(to).unwrap() { - PlaceOrdering::Prefix => { - if related.len() > 1 { - panic!( - "Cannot repack to {} for {:?}: more than 1 related place; {:?}", - to.to_short_string(repacker), - for_cap, - related - ); - } - self.expand( - related.get_only_place(), - CorrectedPlace::new(to, repacker), - for_cap, - repacker, - )?; - return Ok(()); - } - PlaceOrdering::Equal => (), - PlaceOrdering::Suffix => { - self.collapse(related.get_places(), to, repacker)?; - return Ok(()); - } - PlaceOrdering::Both => { - let cp = related.common_prefix(to); - // Collapse - self.collapse(related.get_places(), cp, repacker)?; - // Expand - self.expand(cp, CorrectedPlace::new(to, repacker), cap, repacker)?; - return Ok(()); - } - } + ) -> Result>, PCGError> { + // TODO + // if !to.is_owned(repacker) { + // panic!("Cannot repack to borrowed place {:?}", to); + // } + let collapse_to = self.place_to_collapse_to(to, for_cap); + // eprintln!("Collapse to {collapse_to:?} for repack to {to:?} in {self:?}"); + let mut result = self.collapse(collapse_to, repacker)?; + tracing::debug!("Post collapse result: {result:?}"); + tracing::debug!("Post collapse self: {self:?}"); + let prefix = self.get_longest_prefix(to); + if prefix != to && self.get_capability(to).is_none() { + result.extend(self.expand( + prefix, + CorrectedPlace::new(to, repacker), + for_cap, + repacker, + )?); } - Ok(()) + tracing::debug!("Result: {result:?}"); + tracing::debug!("Self: {self:?}"); + Ok(result) } } diff --git a/src/free_pcs/results/cursor.rs b/src/free_pcs/results/cursor.rs index 1e71fa2c..1f572369 100644 --- a/src/free_pcs/results/cursor.rs +++ b/src/free_pcs/results/cursor.rs @@ -101,13 +101,12 @@ impl<'mir, 'tcx> FreePcsAnalysis<'mir, 'tcx> { let state = self.cursor.get(); - let prev_post_main = state.get_curr_fpcg().data.entry_state.clone(); let curr_fpcg = state.get_curr_fpcg(); let curr_borrows = state.get_curr_borrow_pcg(); if let Some(e) = curr_borrows.error() { return Err(e.clone()); } - let repack_ops = curr_fpcg.repack_ops(&prev_post_main).map_err(|mut e| { + let repack_ops = curr_fpcg.repack_ops().map_err(|mut e| { e.add_context(format!("At {:?}", location)); e })?; diff --git a/src/utils/domain_data.rs b/src/utils/domain_data.rs index f2b66e7e..a19cfafc 100644 --- a/src/utils/domain_data.rs +++ b/src/utils/domain_data.rs @@ -11,7 +11,6 @@ pub(crate) struct DomainData { pub(crate) entry_state: Rc, pub(crate) states: DomainDataStates, pub(crate) phase: DataflowPhase, - pub(crate) visited_stmts_in_block: bool, } #[derive(Debug, Clone, PartialEq, Eq, Default)] @@ -26,6 +25,14 @@ impl std::ops::Index for DomainDataStates { } impl DomainDataStates { + pub fn new(entry_state: Rc) -> Self { + Self(EvalStmtData { + pre_operands: entry_state.clone(), + post_operands: entry_state.clone(), + pre_main: entry_state.clone(), + post_main: entry_state, + }) + } pub(crate) fn get_mut(&mut self, phase: EvalStmtPhase) -> &mut T { Rc::::make_mut(&mut self.0[phase]) } @@ -37,17 +44,15 @@ impl Default for DomainData { entry_state: Rc::default(), states: Default::default(), phase: DataflowPhase::Init, - visited_stmts_in_block: false, } } } -impl DomainData { +impl DomainData { pub(crate) fn new(entry_state: Rc) -> Self { Self { - entry_state, - states: Default::default(), + entry_state: entry_state.clone(), + states: DomainDataStates::new(entry_state), phase: DataflowPhase::Init, - visited_stmts_in_block: false, } } } @@ -74,7 +79,6 @@ impl DomainData { // The entry state may have taken into account previous joins self.states.0.post_main = self.entry_state.clone(); self.phase = DataflowPhase::Transfer; - self.visited_stmts_in_block = true; } else { self.entry_state = self.states.0.post_main.clone(); } diff --git a/src/utils/place/mod.rs b/src/utils/place/mod.rs index 9d56edf7..879ea39b 100644 --- a/src/utils/place/mod.rs +++ b/src/utils/place/mod.rs @@ -13,7 +13,7 @@ use std::{ use derive_more::{Deref, DerefMut}; -use crate::{combined_pcs::{PCGError, PCGUnsupportedError}, rustc_interface::{ +use crate::{borrow_pcg::borrow_pcg_expansion::PlaceExpansion, combined_pcs::{PCGError, PCGUnsupportedError}, rustc_interface::{ ast::Mutability, data_structures::fx::FxHasher, index::IndexVec, @@ -167,10 +167,6 @@ impl<'tcx> HasPlace<'tcx> for Place<'tcx> { } } -#[allow(dead_code)] -#[derive(Debug)] -pub struct PlaceProjectionError(String); - impl<'tcx> Place<'tcx> { /// Projects the place deeper by one element. /// @@ -248,6 +244,18 @@ impl<'tcx> Place<'tcx> { Self(PlaceRef { local, projection }) } + pub(crate) fn expansion_places( + self, + expansion: &PlaceExpansion<'tcx>, + repacker: PlaceRepacker<'_, 'tcx>, + ) -> Vec> { + let mut places = Vec::new(); + for elem in expansion.elems() { + places.push(self.project_deeper(elem, repacker).unwrap()); + } + places + } + pub(crate) fn base_region_projection( self, repacker: PlaceRepacker<'_, 'tcx>, @@ -468,24 +476,6 @@ impl<'tcx> Place<'tcx> { .unwrap_or(false) } - /// Check if the place `self` is a prefix of `place` or vice versa. For example: - /// - /// + `is_prefix(x.f, x.f) == None` - /// + `is_prefix(x.f, x.f.g) == Some(true)` - /// + `is_prefix(x.f.g, x.f) == Some(false)` - /// + `is_prefix(x.g, x.f) == None` - pub(crate) fn either_prefix(self, place: Self) -> Option { - Self::partial_cmp(self, place).and_then(|o| { - if o == PlaceOrdering::Prefix { - Some(true) - } else if o == PlaceOrdering::Suffix { - Some(false) - } else { - None - } - }) - } - /// Returns `true` if either of the places can reach the other /// with a series of expand/collapse operations. Note that /// both operations are allowed and so e.g. diff --git a/src/utils/repacker.rs b/src/utils/repacker.rs index 354944ed..1242f401 100644 --- a/src/utils/repacker.rs +++ b/src/utils/repacker.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use itertools::Itertools; use rustc_interface::{ data_structures::fx::FxHashSet, index::{bit_set::BitSet, Idx}, @@ -60,6 +59,10 @@ impl<'tcx> ShallowExpansion<'tcx> { } } + pub(crate) fn base_place(&self) -> Place<'tcx> { + self.target_place.last_projection().unwrap().0 + } + pub fn expansion(&self) -> Vec> { let mut expansion = self.other_places.clone(); self.kind @@ -72,9 +75,6 @@ impl ProjectionKind { pub(crate) fn is_box(self) -> bool { matches!(self, ProjectionKind::Box) } - pub(crate) fn is_shared_ref(self) -> bool { - matches!(self, ProjectionKind::Ref(Mutability::Not)) - } pub(crate) fn insert_target_into_expansion<'tcx>( self, @@ -159,20 +159,23 @@ pub struct ConstantIndex { pub(crate) from_end: bool, } -#[derive(Debug)] -pub struct ExpandStep<'tcx> { - pub(crate) base_place: Place<'tcx>, - pub(crate) projected_place: Place<'tcx>, - pub(crate) kind: ProjectionKind, -} +pub(crate) struct DeepExpansion<'tcx>(Vec>); -impl<'tcx> ExpandStep<'tcx> { - pub(crate) fn new(from: Place<'tcx>, to: Place<'tcx>, kind: ProjectionKind) -> Self { - Self { - base_place: from, - projected_place: to, - kind, - } +impl<'tcx> DeepExpansion<'tcx> { + pub(crate) fn new(expansions: Vec>) -> Self { + Self(expansions) + } + + pub(crate) fn other_expansions(&self) -> FxHashSet> { + self.0 + .iter() + .flat_map(|e| e.other_places.iter()) + .cloned() + .collect() + } + + pub(crate) fn expansions(&self) -> &[ShallowExpansion<'tcx>] { + &self.0 } } @@ -191,19 +194,18 @@ impl<'tcx> Place<'tcx> { /// `expand(x.f, x.f.g.h)` is performed by unrolling `x.f` into /// `{x.g, x.h, x.f.f, x.f.h, x.f.g.f, x.f.g.g, x.f.g.h}` and /// subtracting `{x.f.g.h}` from it, which results into (`{x.f, x.f.g}`, `{x.g, x.h, - /// x.f.f, x.f.h, x.f.g.f, x.f.g.g}`). The first vector contains the chain of + /// x.f.f, x.f.h, x.f.g.f, x.f.g.g}`). The result contains the chain of /// places that were expanded along with the target to of each expansion. #[allow(clippy::type_complexity)] pub(crate) fn expand( mut self, to: Self, repacker: PlaceRepacker<'_, 'tcx>, - ) -> Result<(Vec>, Vec), PCGError> { + ) -> Result, PCGError> { assert!( self.is_prefix(to), "The minuend ({self:?}) must be the prefix of the subtrahend ({to:?})." ); - let mut place_set = Vec::new(); let mut expanded = Vec::new(); while self.projection.len() < to.projection.len() { let ShallowExpansion { @@ -211,43 +213,10 @@ impl<'tcx> Place<'tcx> { other_places, kind, } = self.expand_one_level(to, repacker)?; - expanded.push(ExpandStep::new(self, target_place, kind)); - place_set.extend(other_places); + expanded.push(ShallowExpansion::new(target_place, other_places, kind)); self = target_place; } - Ok((expanded, place_set)) - } - - /// Try to collapse all places in `from` by following the - /// `guide_place`. This function is basically the reverse of - /// `expand`. - pub fn collapse( - self, - from: FxHashSet, - repacker: PlaceRepacker<'_, 'tcx>, - ) -> Vec> { - let mut collapsed = Vec::new(); - let mut guide_places = vec![self]; - while let Some(place) = guide_places.pop() { - let mut next_projection_candidates: Vec> = from - .iter() - .copied() - .filter(|p| place.is_strict_prefix(*p)) - .sorted_by_key(|p| p.projection.len()) - .collect(); - while let Some(next_candidate) = next_projection_candidates.pop() { - let (expanded, new_places) = place.expand(next_candidate, repacker).unwrap(); - - // Don't consider unpacking with targets in `new_places` since they are going - // to be packed via the packing in `expanded` - next_projection_candidates.retain(|p| !new_places.contains(p)); - - collapsed.extend(expanded); - guide_places.extend(new_places); - } - } - collapsed.reverse(); - collapsed + Ok(DeepExpansion::new(expanded)) } /// Expand `self` one level down by following the `guide_place`. diff --git a/src/visualization/dot_graph.rs b/src/visualization/dot_graph.rs index d9d2c5a3..36242e10 100644 --- a/src/visualization/dot_graph.rs +++ b/src/visualization/dot_graph.rs @@ -18,7 +18,7 @@ impl DotGraph { } pub fn render_with_imgcat(dot_str: &str, comment: &str) -> Result<(), std::io::Error> { - tracing::info!("{}", comment); + tracing::debug!("{}", comment); let mut dot_process = Command::new("dot") .args(["-Tpng"]) .stdin(Stdio::piped()) diff --git a/src/visualization/graph_constructor.rs b/src/visualization/graph_constructor.rs index 021e8955..2e2d230b 100644 --- a/src/visualization/graph_constructor.rs +++ b/src/visualization/graph_constructor.rs @@ -22,10 +22,7 @@ use crate::rustc_interface::middle::ty::TyCtxt; use crate::utils::place::maybe_old::MaybeOldPlace; use crate::utils::place::maybe_remote::MaybeRemotePlace; use crate::utils::place::remote::RemotePlace; -use std::{ - collections::{BTreeSet, HashSet}, - ops::Deref, -}; +use std::collections::{BTreeSet, HashSet}; #[derive(Eq, PartialEq, Hash)] pub struct GraphCluster { @@ -432,7 +429,7 @@ impl<'tcx> CapabilityGetter<'tcx> for PCGCapabilityGetter<'_, 'tcx> { let place = node.as_current_place()?; let alloc = self.summary.get(place.local)?; if let CapabilityLocal::Allocated(projections) = alloc { - projections.deref().get(&place).cloned() + projections.get_capability(place) } else { None } @@ -568,7 +565,7 @@ impl<'a, 'tcx> PCSGraphConstructor<'a, 'tcx> { match capability { CapabilityLocal::Unallocated => {} CapabilityLocal::Allocated(projections) => { - for (place, _) in projections.iter() { + for place in projections.place_capabilities().keys() { let capability_getter = &PCGCapabilityGetter { summary: self.summary, borrows_domain: self.borrows_domain, diff --git a/test-files/79_add_overflow.rs b/test-files/79_add_overflow.rs index e9dcae99..c942259b 100644 --- a/test-files/79_add_overflow.rs +++ b/test-files/79_add_overflow.rs @@ -2,6 +2,6 @@ fn main() { let a = 1; let b = 2; let c = a + b; - // PCG: bb0[12] post_operands: Repacks Start: Expand(_6, _6.0, E) + // PCG: bb0[12] post_operands: Repacks Start: Expand(_6, _6.1, E) println!("{}", c); } diff --git a/test-files/81_shared_ref.rs b/test-files/81_shared_ref.rs index e5f78f24..7880c58c 100644 --- a/test-files/81_shared_ref.rs +++ b/test-files/81_shared_ref.rs @@ -3,13 +3,6 @@ struct S { b: i32, } -fn f(x: Result, ()>) { - if let Ok(s@Some(y)) = x { - let r = &s; - let d = *r; - } -} - fn main() { let x = S { a: 1, b: 2 }; let y = &x.a; diff --git a/test-files/82_shared_ref_2.rs b/test-files/82_shared_ref_2.rs index 920ad722..b95f3c37 100644 --- a/test-files/82_shared_ref_2.rs +++ b/test-files/82_shared_ref_2.rs @@ -1,10 +1,9 @@ fn f(x: Result, ()>) { - if let Ok(s@Some(y)) = x { -// ~PCG: bb3[1] pre_operands: Repacks Start: Expand(_1, (_1@Ok), E) + if let Ok(s @ Some(y)) = x { + // ~PCG: bb3[1] pre_operands: Repacks Start: Expand(_1, (_1@Ok), E) let r = &s; let d = *r; } } -fn main() { -} +fn main() {} diff --git a/test-files/83_prusti_wand.rs b/test-files/83_prusti_wand.rs new file mode 100644 index 00000000..249ed54b --- /dev/null +++ b/test-files/83_prusti_wand.rs @@ -0,0 +1,12 @@ +struct T { + val: i32 +} + +fn identity_use2() { + let mut t = T { val: 5 }; + assert!(t.val == 5); + // PCG: bb0[7] pre_operands: Repacks Start: Collapse(_1, _1.0, E) + let y = &mut t; +} + +fn main() {} diff --git a/test-files/84_prusti_ownership.rs b/test-files/84_prusti_ownership.rs new file mode 100644 index 00000000..13785240 --- /dev/null +++ b/test-files/84_prusti_ownership.rs @@ -0,0 +1,12 @@ +struct Point { + x: u32, + y: u32, +} + + +fn shift_end(tuple: (Point, Point)) { + let x = tuple.0.x; + // ~PCG: bb0[1] pre_operands: Repacks Start: Collapse(_1, _1.0, R) +} + +fn main(){} diff --git a/test-files/85_prusti_rpe_example.rs b/test-files/85_prusti_rpe_example.rs new file mode 100644 index 00000000..82778c3a --- /dev/null +++ b/test-files/85_prusti_rpe_example.rs @@ -0,0 +1,8 @@ +fn f(pair: (T, T)) { + // ~PCG: bb0[3] post_operands: Repacks Start: Expand(_1, _1.0, E) + let fst = pair.0; +} + +fn main(){ + +} diff --git a/test-files/86_prusti_mut_borrows.rs b/test-files/86_prusti_mut_borrows.rs new file mode 100644 index 00000000..4d107bc6 --- /dev/null +++ b/test-files/86_prusti_mut_borrows.rs @@ -0,0 +1,25 @@ +struct T { + f: u32, +} + +struct U { + f: u32, + g: u32, +} + + +pub fn test6() { + let mut a = U { f: 6, g: 1, }; + let y = &mut a.f; + let z = &mut a.g; + *y = 7; + let y2 = &mut *y; + let z2 = &mut *z; + *y2 = 3; + // PCG: bb0[17] pre_operands: a.f: E + *z2 = 4; + assert!(a.f == 3); + assert!(a.g == 4); +} + +fn main() {} diff --git a/test-files/87_prusti_create_box.rs b/test-files/87_prusti_create_box.rs new file mode 100644 index 00000000..a70e5b5e --- /dev/null +++ b/test-files/87_prusti_create_box.rs @@ -0,0 +1,10 @@ +fn use_box(v: i32) -> Box { + let x = Box::new(v); + let y = *x; + assert!(v == y); + let z = Box::new(y); + assert!(v == *z); + Box::new(*z) +} + +fn main() {} From 8d3fe0caf5b748f28152fd83155d683597b2bfdc Mon Sep 17 00:00:00 2001 From: Zack Grannan Date: Tue, 4 Mar 2025 13:02:34 -0800 Subject: [PATCH 21/21] More benchmark --- benchmark_results.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/benchmark_results.txt b/benchmark_results.txt index 4de4a83c..96ade379 100644 --- a/benchmark_results.txt +++ b/benchmark_results.txt @@ -78,3 +78,12 @@ 78_interior_mutability_observable.rs: 46875188 79_add_overflow.rs: 69546402 80_crc_bytewise.rs: 211820594 +79_add_overflow.rs: 69144639 +80_crc_bytewise.rs: 221361585 +81_shared_ref.rs: 43520001 +82_shared_ref_2.rs: 58097901 +83_prusti_wand.rs: 44743154 +84_prusti_ownership.rs: 41035866 +85_prusti_rpe_example.rs: 40983383 +86_prusti_mut_borrows.rs: 52540071 +87_prusti_create_box.rs: 50621751