pub(crate) mod builtin_models;
pub(crate) mod interprocedural;
#[cfg(rapx_has_attr_ir)]
use rustc_attr_ir::LangItem;
#[cfg(all(not(rapx_has_attr_ir), not(rapx_ge_100)))]
use rustc_hir::LangItem;
#[cfg(all(not(rapx_has_attr_ir), rapx_ge_100))]
use rustc_hir::attrs::lang_items::LangItem;
use rustc_hir::def_id::DefId;
use rustc_middle::{
mir::{Local, Operand},
ty::{Ty, TyCtxt, TyKind},
};
use crate::compat::FxHashMap;
use crate::helpers::mir_utils;
use crate::verify::api_classify::{is_branch, is_std_vec};
#[derive(Clone, Debug, Default)]
pub(crate) struct CallContext {
pub concrete: FxHashMap<usize, i128>,
}
#[derive(Clone, Debug)]
pub(crate) struct CallDependencySummary {
pub return_depends_on_args: Vec<usize>,
pub must_write_args: Vec<usize>,
pub unsupported: bool,
}
impl CallDependencySummary {
fn unknown(arg_count: usize) -> Self {
Self {
return_depends_on_args: (0..arg_count).collect(),
must_write_args: Vec::new(),
unsupported: true,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct CallEffectSummary {
pub effects: Vec<CallEffect>,
pub unsupported: bool,
}
impl CallEffectSummary {
fn unknown() -> Self {
Self {
effects: Vec::new(),
unsupported: true,
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum CallEffect {
ReturnAliasArg { arg: usize },
SelectUnpredictable,
ReturnPointerFromArg { arg: usize },
ReturnPointerAdd {
base_arg: usize,
offset_arg: usize,
stride: Option<u64>,
dereferenceable: bool,
},
ReturnPointerSub {
base_arg: usize,
offset_arg: usize,
stride: Option<u64>,
},
ReturnNonZero,
ReturnAligned,
ReturnConst { value: u64 },
WriteMemory { pointer_arg: usize },
ReturnFreshAllocation {
pointer_arg: usize,
size_arg: usize,
elem_size: u64,
},
ReturnExchangeMalloc { size_arg: usize },
ReturnLengthOfArg { arg: usize },
ReturnMin { lhs_arg: usize, rhs_arg: usize },
ReturnMax { lhs_arg: usize, rhs_arg: usize },
ReturnClamp {
value_arg: usize,
min_arg: usize,
max_arg: usize,
},
ReturnAbs { arg: usize },
ReturnNeg { arg: usize },
ReturnAdd { lhs_arg: usize, rhs_arg: usize },
ReturnMul { lhs_arg: usize, rhs_arg: usize },
ReturnOptionSomeAdd { lhs_arg: usize, rhs_arg: usize },
ReturnOptionSomeMul { lhs_arg: usize, rhs_arg: usize },
ReturnNonZeroIff { arg: usize },
ReturnOptionSomeNonZeroIff { arg: usize },
ReturnOptionSomeNonZero,
ReturnTupleFieldNonZero { field: usize },
ReturnTupleFieldLength { field: usize, from_arg: usize },
ReturnNewAllocation { size_arg: usize, elem_size: u64 },
ReturnBoxAllocation,
ReturnNewAllocationFromBox,
ReturnNewAllocationFromCap { cap_arg: usize, elem_size: u64 },
ReturnPowerOfTwo,
ReturnBoxFromVec { arg: usize },
OwnsInitMemory { arg: usize },
ReturnOptionSomeScanIndex { self_arg: usize },
ReturnBranchPayload { arg: usize },
ReturnScanLength,
ReturnAlignOffset { ptr_arg: usize, align_arg: usize },
ReturnAlignTo { receiver_arg: usize },
ReturnTransparentDeref { arg: usize, peel: usize },
ReturnRange { bounds_arg: usize },
ReturnDerefArg { arg: usize },
DropMemory { pointer_arg: usize },
ReturnSliceRangeIndex,
ReturnSliceRangeGet,
ReturnIterLen { is_len: bool },
ReturnNonNullNew,
ReturnIterNext,
ReturnRangeNext,
ReturnLayoutSymbolic { is_size: bool },
}
pub(crate) fn dependency_summary<'tcx>(
tcx: TyCtxt<'tcx>,
func: &Operand<'tcx>,
arg_count: usize,
context: &CallContext,
) -> CallDependencySummary {
let callee = mir_utils::dep_callee_def_id(func);
if let Some(callee) = callee {
if tcx.intrinsic(callee).is_some() || mir_utils::is_drop_in_place(callee) {
return CallDependencySummary::unknown(arg_count);
}
if let Some(must_write_args) = interprocedural::local_must_write_args(tcx, callee, context)
&& !must_write_args.is_empty() {
return CallDependencySummary {
return_depends_on_args: Vec::new(),
must_write_args: must_write_args
.into_iter()
.filter(|index| *index < arg_count)
.collect(),
unsupported: false,
};
}
if is_branch(Some(callee)) {
return CallDependencySummary {
return_depends_on_args: vec![0],
must_write_args: Vec::new(),
unsupported: false,
};
}
if let Some(return_deps) = interprocedural::local_return_dependencies(tcx, callee) {
return CallDependencySummary {
return_depends_on_args: return_deps
.into_iter()
.filter(|index| *index < arg_count)
.collect(),
must_write_args: Vec::new(),
unsupported: false,
};
}
}
CallDependencySummary::unknown(arg_count)
}
pub(crate) fn effect_summary<'tcx>(tcx: TyCtxt<'tcx>, func: &Operand<'tcx>) -> CallEffectSummary {
if let Some(peel) = transparent_deref_peel(tcx, func) {
return CallEffectSummary {
effects: vec![CallEffect::ReturnTransparentDeref { arg: 0, peel }],
unsupported: false,
};
}
CallEffectSummary::unknown()
}
fn transparent_deref_peel<'tcx>(tcx: TyCtxt<'tcx>, func: &Operand<'tcx>) -> Option<usize> {
let self_ty = mir_utils::fn_def_first_type_arg(func)?;
let TyKind::Adt(adt_def, _) = self_ty.kind() else {
return None;
};
let did = adt_def.did();
if tcx.is_lang_item(did, LangItem::ManuallyDrop) {
return Some(2);
}
if is_maybe_dangling(tcx, did) {
return Some(1);
}
None
}
fn is_maybe_dangling(tcx: TyCtxt<'_>, did: DefId) -> bool {
#[cfg(rapx_has_maybe_dangling_lang_item)]
{
tcx.is_lang_item(did, LangItem::MaybeDangling)
}
#[cfg(not(rapx_has_maybe_dangling_lang_item))]
{
tcx.def_path_str(did).contains("MaybeDangling")
}
}
pub(crate) fn vec_elem_ty<'tcx>(_tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if let TyKind::Adt(adt_def, substs) = ty.kind()
&& is_std_vec(adt_def.did()) {
return substs.first().and_then(|s| s.as_type());
}
None
}
pub(crate) fn from_raw_parts_elem_ty<'tcx>(
tcx: TyCtxt<'tcx>,
caller: DefId,
dest: Option<Local>,
) -> Option<Ty<'tcx>> {
let d = dest?;
let ty = tcx.optimized_mir(caller).local_decls[d].ty;
match ty.kind() {
TyKind::Ref(_, inner, _) => match inner.kind() {
TyKind::Slice(e) => Some(*e),
_ => None,
},
TyKind::RawPtr(inner, _) => match inner.kind() {
TyKind::Slice(e) => Some(*e),
_ => None,
},
TyKind::Adt(..) => vec_elem_ty(tcx, ty),
_ => None,
}
}
pub(crate) fn from_raw_parts_elem_size<'tcx>(
tcx: TyCtxt<'tcx>,
caller: DefId,
dest: Option<Local>,
) -> u64 {
from_raw_parts_elem_ty(tcx, caller, dest)
.and_then(|e| mir_utils::type_layout(tcx, caller, e).map(|(_, s)| s))
.unwrap_or(1)
}