use rustc_hir::def_id::DefId;
use rustc_middle::{
mir::{Body, Operand},
ty::{Mutability, Ty, TyCtxt, TyKind},
};
use crate::compat::Spanned;
use crate::def_ids::lang_api;
use crate::verify::api_classify;
use super::CallDependencySummary;
pub(crate) fn supports_result(ty: Ty<'_>) -> bool {
matches!(ty.kind(), TyKind::Bool)
|| matches!(ty.kind(), TyKind::Adt(def, _) if api_classify::is_std_ordering(def.did()))
}
pub(super) fn estimate<'tcx>(
tcx: TyCtxt<'tcx>,
callee: DefId,
args: &[Spanned<Operand<'tcx>>],
body: &Body<'tcx>,
) -> Option<CallDependencySummary> {
if tcx.is_mir_available(callee) {
return None;
}
let trait_id = tcx.trait_of_assoc(callee)?;
if !lang_api::is_fn_trait(tcx, trait_id) {
return None;
}
let write_args: Vec<_> = args
.iter()
.enumerate()
.filter_map(|(index, arg)| {
(index == 0 || operand_ty(tcx, body, &arg.node).is_none_or(may_write_reachable))
.then_some(index)
})
.collect();
let all_args: Vec<_> = (0..args.len()).collect();
Some(CallDependencySummary {
return_depends_on_args: all_args.clone(),
write_args,
write_inputs: all_args,
})
}
fn operand_ty<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
operand: &Operand<'tcx>,
) -> Option<Ty<'tcx>> {
match operand {
Operand::Copy(place) | Operand::Move(place) => Some(place.ty(&body.local_decls, tcx).ty),
Operand::Constant(constant) => Some(constant.const_.ty()),
#[cfg(rapx_ge_95)]
Operand::RuntimeChecks(_) => None,
}
}
fn may_write_reachable(ty: Ty<'_>) -> bool {
match ty.kind() {
TyKind::Ref(_, _, Mutability::Mut) | TyKind::RawPtr(..) => true,
TyKind::Ref(_, inner, Mutability::Not) => may_contain_interior_mutability(*inner),
TyKind::Tuple(fields) => fields.iter().any(may_write_reachable),
TyKind::Array(inner, _) | TyKind::Slice(inner) => may_write_reachable(*inner),
TyKind::Bool | TyKind::Char | TyKind::Int(_) | TyKind::Uint(_) | TyKind::Float(_)
| TyKind::Never | TyKind::Str => false,
_ => true,
}
}
fn may_contain_interior_mutability(ty: Ty<'_>) -> bool {
match ty.kind() {
TyKind::Bool | TyKind::Char | TyKind::Int(_) | TyKind::Uint(_) | TyKind::Float(_)
| TyKind::Never | TyKind::Str => false,
TyKind::Tuple(fields) => fields.iter().any(may_contain_interior_mutability),
TyKind::Array(inner, _) | TyKind::Slice(inner) => may_contain_interior_mutability(*inner),
TyKind::Ref(_, inner, Mutability::Not) => may_contain_interior_mutability(*inner),
_ => true,
}
}