pub struct PathGraph<'tcx> {
pub cfg: ControlFlowGraph<'tcx>,
pub block_info: Vec<BlockConstantInfo>,
pub disc_info: DiscriminantInfo,
pub aggregate_field_sources: FxHashMap<usize, usize>,
pub cast_chains: FxHashMap<usize, usize>,
pub field_projection_source: FxHashMap<usize, usize>,
pub inline_bindings: FxHashMap<usize, InlineBinding>,
pub inline_parents: FxHashMap<usize, DefId>,
pub inlined_call_blocks: FxHashSet<usize>,
local_bases: FxHashMap<DefId, usize>,
next_local_base: usize,
}Expand description
CFG augmented with per-block constant info and discriminant metadata for path reachability analysis.
PathGraph wraps a ControlFlowGraph and adds block-indexed data
that track assignments, constants, and copy chains. These are used by
check_transition to update a set of
discriminant constraints while traversing the CFG, enabling early
pruning of infeasible SwitchInt branches during path enumeration.
Fields§
§cfg: ControlFlowGraph<'tcx>§block_info: Vec<BlockConstantInfo>§disc_info: DiscriminantInfo§aggregate_field_sources: FxHashMap<usize, usize>Global store: maps encoded (local, field_idx) to source local from Aggregates.
cast_chains: FxHashMap<usize, usize>Global store: maps dest to src for pointer-cast chains.
field_projection_source: FxHashMap<usize, usize>Global store: maps _dest to encoded (base, field_idx) from field-projection copies.
inline_bindings: FxHashMap<usize, InlineBinding>Per-inlined-callee argument binding: maps the callee’s entry block (global index) to the caller’s argument locals and destination local.
inline_parents: FxHashMap<usize, DefId>Per-inlined-callee parent: entry block (global index) → the DefId of
the caller whose Call was inlined at that entry.
inlined_call_blocks: FxHashSet<usize>Caller blocks whose Call terminator was inlined (their successor edge
now leads into a callee). The slicer skips these calls.
local_bases: FxHashMap<DefId, usize>Per-function local-index namespace: def_id → base offset. The root
caller uses base 0; every inlined callee gets a fresh base so its MIR
locals do not clash with the caller’s in the flat constraint maps
(block_info, disc_info, cast_chains, …). A constraint key k in
the namespace of def_id denotes MIR local k - base.
next_local_base: usizeNext unused local-namespace base (monotonic counter).
Implementations§
Source§impl<'tcx> PathGraph<'tcx>
impl<'tcx> PathGraph<'tcx>
pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId) -> PathGraph<'tcx>
Sourcepub fn inline_callees(&mut self)
pub fn inline_callees(&mut self)
Inline the CFG of inlinable callees into this graph so that path enumeration covers the callee’s branches.
Inlining is recursive within the local crate: a local callee’s own
calls are inlined too. Cross-crate callees are inlined only at the level
where a local function calls them — their bodies are not recursed into,
so the standard library’s internal branchy precondition machinery
(is_aligned_to, ub_checks, …) is not pulled in, which would blow up
path enumeration with branches that have no constraint tracking.
Each callee is otherwise subject to the same shape constraints as
before: cross-crate callees with MIR are always inlined, and a local
callee is inlined only when it is small (at most
LOCAL_INLINE_BLOCK_LIMIT basic
blocks), so branch-free accessors
(get, count_ones, …) also land in the CFG and their field
provenance is reconstructed element-by-element. Intrinsics
have no MIR; callees with a builtin model are kept opaque because their
summary is more precise than their body.
Recursion is bounded by an expanded set so that (mutually) recursive
local functions do not grow the CFG without bound — a back-edge to an
already-inlined callee is left as an ordinary call edge for the VM’s
fallback handling.
Opt-in — callers that assume a single-function block space (e.g. alias analysis) must not call this.
Sourcefn inline_one(&mut self, caller_idx: usize, callee: DefId)
fn inline_one(&mut self, caller_idx: usize, callee: DefId)
Inline a single callee into caller_idx, reconnecting the caller’s
normal successor edge through the callee body.
pub fn find_scc(&mut self)
pub fn def_id(&self) -> DefId
pub fn tcx(&self) -> TyCtxt<'tcx>
pub fn cfg_block(&self, index: usize) -> &CfgBlock
pub fn cfg_block_mut(&mut self, index: usize) -> &mut CfgBlock
Sourcepub fn terminator(&self, index: usize) -> Option<&Terminator<'tcx>>
pub fn terminator(&self, index: usize) -> Option<&Terminator<'tcx>>
Retrieve the MIR terminator for the block at index on demand.
pub fn is_cleanup_block(&self, index: usize) -> bool
Sourcefn local_base_of(&self, def_id: DefId) -> usize
fn local_base_of(&self, def_id: DefId) -> usize
The local-index base of def_id’s MIR locals in the flat constraint
maps. The root caller is base 0; inlined callees get a fresh base.
Sourcefn remap_local(&self, def_id: DefId, local: usize) -> usize
fn remap_local(&self, def_id: DefId, local: usize) -> usize
Remap def_id’s MIR local local into the global constraint namespace.
Sourcefn assign_local_base(&mut self, def_id: DefId) -> usize
fn assign_local_base(&mut self, def_id: DefId) -> usize
Assign a fresh local-namespace base for def_id (or return its existing
base if the function has already been inlined).
Sourcefn get_variant_count(&self, local: usize, def_id: DefId) -> Option<usize>
fn get_variant_count(&self, local: usize, def_id: DefId) -> Option<usize>
Get the number of variants for a constraint local (in def_id’s local
namespace). First checks the pre-populated variant_count_of hashmap,
then falls back to the local’s declared type (for ADT locals that gained
their type through field projections in nested destructuring patterns
rather than explicit construction).
Sourcepub fn check_transition(
&self,
cur: usize,
next: usize,
constraints: &mut FxHashMap<usize, usize>,
) -> bool
pub fn check_transition( &self, cur: usize, next: usize, constraints: &mut FxHashMap<usize, usize>, ) -> bool
Check a single transition cur -> next for reachability and update
discriminant constraints. Returns false if the transition is
provably unreachable.
fn check_assert_transition( &self, cur: usize, next: usize, constraints: &FxHashMap<usize, usize>, ) -> bool
fn resolve_simple_bool( &self, local: usize, constraints: &FxHashMap<usize, usize>, ) -> Option<usize>
fn resolve_bool_local( &self, local: usize, constraints: &FxHashMap<usize, usize>, ) -> Option<usize>
Sourcefn check_switch_transition(
&self,
cur: usize,
next: usize,
constraints: &mut FxHashMap<usize, usize>,
) -> bool
fn check_switch_transition( &self, cur: usize, next: usize, constraints: &mut FxHashMap<usize, usize>, ) -> bool
Check whether cur → next is a valid SwitchInt transition given
current discriminant constraints. Returns false when the transition
contradicts a known discriminant value. Also records newly learned
constraints from the taken branch into constraints.
Sourcefn learn_constraint_with_backprop(
&self,
cur: usize,
constraint_local: Option<usize>,
targets: &SwitchTargets,
next: usize,
constraints: &mut FxHashMap<usize, usize>,
)
fn learn_constraint_with_backprop( &self, cur: usize, constraint_local: Option<usize>, targets: &SwitchTargets, next: usize, constraints: &mut FxHashMap<usize, usize>, )
After learning a constraint for a discriminant local, propagate the constraint backward through the copy chain so that source locals also receive the value. This prevents losing track of the constraint when the destination temporary is reassigned on loop back-edges.
fn backprop_constraint( &self, cur: usize, local: usize, val: usize, constraints: &mut FxHashMap<usize, usize>, )
Sourcefn resolve_local_value_direct(
&self,
local: usize,
constraints: &FxHashMap<usize, usize>,
) -> Option<usize>
fn resolve_local_value_direct( &self, local: usize, constraints: &FxHashMap<usize, usize>, ) -> Option<usize>
Recursively resolve a local’s value through constraint copies,
cast chains, field projections, and aggregate sources (global maps).
Like resolve_local_value but does NOT follow increment chains.
Used in conservative path filtering where we need high confidence.
fn resolve_local_value( &self, local: usize, constraints: &FxHashMap<usize, usize>, ) -> Option<usize>
Sourcefn infer_otherwise_value(
&self,
cur: usize,
targets: &SwitchTargets,
discr_local: usize,
) -> Option<usize>
fn infer_otherwise_value( &self, cur: usize, targets: &SwitchTargets, discr_local: usize, ) -> Option<usize>
For the “otherwise” branch of a SwitchInt, try to infer the single
concrete value that the discriminant must have (because all other
possible values are covered by explicit targets).
Sourcefn is_unwind_target(&self, cur: usize, next: usize) -> bool
fn is_unwind_target(&self, cur: usize, next: usize) -> bool
Check whether next is an unwind target reachable from cur via a
call or drop terminator (may not be recorded as a normal CFG successor).
Sourcefn local_is_known_nonnull(
&self,
constraints: &FxHashMap<usize, usize>,
local: usize,
) -> bool
fn local_is_known_nonnull( &self, constraints: &FxHashMap<usize, usize>, local: usize, ) -> bool
Return true if local is known to be a non-null pointer.
Sourcefn local_is_known_null(
&self,
constraints: &FxHashMap<usize, usize>,
local: usize,
) -> bool
fn local_is_known_null( &self, constraints: &FxHashMap<usize, usize>, local: usize, ) -> bool
Return true if local is known to be a null pointer.
Sourcefn populate_child_sccs(&mut self, enter: usize)
fn populate_child_sccs(&mut self, enter: usize)
Populate the child_sccs field for a given SCC entry block, then
recurse into those child SCCs. Called eagerly from find_scc() so
that enumeration can be purely read-only on the graph.
fn populate_all_child_sccs(&mut self)
Trait Implementations§
Auto Trait Implementations§
impl<'tcx> !RefUnwindSafe for PathGraph<'tcx>
impl<'tcx> !Send for PathGraph<'tcx>
impl<'tcx> !Sync for PathGraph<'tcx>
impl<'tcx> !UnwindSafe for PathGraph<'tcx>
impl<'tcx> DynSend for PathGraph<'tcx>
impl<'tcx> DynSync for PathGraph<'tcx>
impl<'tcx> Freeze for PathGraph<'tcx>
impl<'tcx> Unpin for PathGraph<'tcx>
impl<'tcx> UnsafeUnpin for PathGraph<'tcx>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more