pub use crate::helpers::def_use::*;
use rustc_middle::mir::Operand;
use rustc_middle::ty::TyCtxt;
use super::contract::{
ContractExpr, ContractPlace, ContractProjection, NumericPredicate, PlaceBase, Property,
PropertyArg, PropertyKind,
};
use crate::helpers::mir_utils::callee_param_index_for_local;
use crate::helpers::mir_scan::Checkpoint;
impl PlaceKey {
pub fn from_contract_place(place: &ContractPlace<'_>) -> Self {
Self {
base: match place.base {
PlaceBase::Return => PlaceBaseKey::Return,
PlaceBase::Arg(index) => PlaceBaseKey::Arg(index),
PlaceBase::Local(local) => PlaceBaseKey::Local(local),
},
fields: place
.projections
.iter()
.filter_map(|projection| match projection {
ContractProjection::Field { index, .. } => Some(*index),
ContractProjection::Downcast { .. } => Some(0),
ContractProjection::IterElements => None,
})
.collect(),
}
}
}
impl RelevantPlaces {
pub fn from_property(property: &Property<'_>) -> Self {
let mut set = Self::new();
set.collect_property(property);
set
}
pub fn insert_contract_place(&mut self, place: &ContractPlace<'_>) {
self.insert_place_key(PlaceKey::from_contract_place(place));
}
fn collect_property(&mut self, property: &Property<'_>) {
for (arg_index, arg) in property.args.iter().enumerate() {
if self.collect_target_argument_root(&property.kind, arg_index, arg) {
continue;
}
self.collect_property_arg(arg);
}
}
fn collect_target_argument_root(
&mut self,
kind: &PropertyKind,
arg_index: usize,
arg: &PropertyArg<'_>,
) -> bool {
if !is_target_argument_index(kind, arg_index) {
return false;
}
let PropertyArg::Expr(ContractExpr::Const(value)) = arg else {
return false;
};
let Ok(index) = usize::try_from(*value) else {
return false;
};
self.insert_place_key(PlaceKey {
base: PlaceBaseKey::Arg(index),
fields: Vec::new(),
});
true
}
fn collect_property_arg(&mut self, arg: &PropertyArg<'_>) {
match arg {
PropertyArg::Expr(expr) => self.collect_contract_expr(expr),
PropertyArg::Predicates(predicates) => {
for predicate in predicates {
self.collect_numeric_predicate(predicate);
}
}
PropertyArg::Ty(_) | PropertyArg::Ident(_) => {}
}
}
fn collect_numeric_predicate(&mut self, predicate: &NumericPredicate<'_>) {
self.collect_contract_expr(&predicate.lhs);
self.collect_contract_expr(&predicate.rhs);
}
fn collect_contract_expr(&mut self, expr: &ContractExpr<'_>) {
match expr {
ContractExpr::Place(place) => self.insert_contract_place(place),
ContractExpr::Binary { lhs, rhs, .. } => {
self.collect_contract_expr(lhs);
self.collect_contract_expr(rhs);
}
ContractExpr::Unary { expr, .. } => self.collect_contract_expr(expr),
ContractExpr::Len(expr) => {
self.collect_contract_expr(expr);
if let ContractExpr::Place(place) = expr.as_ref() {
self.need_len.insert(PlaceKey::from_contract_place(place));
}
}
ContractExpr::IndexAccess { slice, index } => {
self.collect_contract_expr(slice);
self.collect_contract_expr(index);
}
ContractExpr::Min { a, b } | ContractExpr::Max { a, b } => {
self.collect_contract_expr(a);
self.collect_contract_expr(b);
}
ContractExpr::Const(_)
| ContractExpr::ConstParam { .. }
| ContractExpr::SizeOf(_)
| ContractExpr::AlignOf(_)
| ContractExpr::Unknown => {}
}
}
}
fn is_target_argument_index(kind: &PropertyKind, arg_index: usize) -> bool {
match kind {
PropertyKind::NonOverlap | PropertyKind::Alias => arg_index <= 1,
PropertyKind::ValidNum | PropertyKind::Unknown => false,
_ => arg_index == 0,
}
}
pub fn bind_callsite_roots(
tcx: TyCtxt<'_>,
relevance: &mut RelevantPlaces,
checkpoint: &Checkpoint<'_>,
) {
let argument_roots: Vec<(PlaceKey, usize)> = relevance
.places
.iter()
.filter_map(|place| match place.base {
PlaceBaseKey::Arg(index) => Some((place.clone(), index)),
PlaceBaseKey::Local(local) => checkpoint
.callee
.and_then(|callee| callee_param_index_for_local(tcx, callee, local))
.map(|index| (place.clone(), index)),
_ => None,
})
.collect();
let mut bound_roots = RelevantPlaces::new();
let mut rebound_roots = Vec::new();
for (root, index) in argument_roots {
if let Some(operand) = checkpoint.args.get(index) {
if let Some(place) = bind_operand_place(operand, &root.fields) {
bound_roots.insert_place_key(place);
} else {
bound_roots.extend(operand_uses(operand));
}
rebound_roots.push(root);
}
}
relevance.remove_place_keys(&rebound_roots);
relevance.extend(bound_roots);
{
let need_len_roots: Vec<(PlaceKey, usize)> = relevance
.need_len
.iter()
.filter_map(|place| match place.base {
PlaceBaseKey::Arg(index) => Some((place.clone(), index)),
PlaceBaseKey::Local(local) => checkpoint
.callee
.and_then(|callee| callee_param_index_for_local(tcx, callee, local))
.map(|index| (place.clone(), index)),
_ => None,
})
.collect();
for (root, index) in need_len_roots {
if let Some(operand) = checkpoint.args.get(index) {
if let Some(place) = bind_operand_place(operand, &root.fields) {
relevance.need_len.insert(place);
}
}
}
}
}
fn bind_operand_place(operand: &Operand<'_>, fields: &[usize]) -> Option<PlaceKey> {
let mut place = match operand {
Operand::Copy(place) | Operand::Move(place) => PlaceKey::from_mir_place(place),
Operand::Constant(_) => return None,
#[cfg(rapx_rustc_ge_196)]
Operand::RuntimeChecks(_) => return None,
};
place.fields.extend(fields.iter().copied());
Some(place)
}