use rustc_middle::ty::{TyCtxt, TyKind};
use super::common::{SmtCheckResult, SmtChecker, SmtObligation};
use crate::verify::{
contract::{ContractPlace, PlaceBase, Property, PropertyArg},
helpers::Checkpoint,
verifier::ForwardVisitResult,
};
fn is_nonnull_param_ty(tcx: TyCtxt<'_>, ty: rustc_middle::ty::Ty<'_>) -> bool {
let peeled = ty.peel_refs();
if let TyKind::Adt(def, _) = peeled.kind() {
let path = tcx.def_path_str(def.did());
if path.contains("ptr::non_null::NonNull") {
return true;
}
}
false
}
pub(crate) fn check<'tcx>(
checker: &SmtChecker<'tcx>,
checkpoint: &Checkpoint<'tcx>,
property: &Property<'tcx>,
forward: &ForwardVisitResult<'tcx>,
) -> SmtCheckResult {
if checkpoint.is_ref {
return SmtCheckResult::proved("NonNull trivially holds for ref-derived pointer");
}
if let Some(callee) = checkpoint.callee {
if let Some(PropertyArg::Place(ContractPlace {
base: PlaceBase::Arg(arg_index),
..
})) = property.args.first()
{
let tcx = checker.tcx;
let fn_sig = tcx.fn_sig(callee).skip_binder();
let input_tys = fn_sig.inputs().skip_binder();
if let Some(param_ty) = input_tys.get(*arg_index) {
if is_nonnull_param_ty(tcx, *param_ty) {
return SmtCheckResult::proved(
"NonNull trivially holds: callee parameter type is NonNull<T>",
);
}
}
}
}
let Some(target) = checker.property_target(Some(checkpoint), property) else {
return SmtCheckResult::unknown("NonNull target could not be resolved");
};
let obligation = SmtObligation::NonZero { place: target };
let result = checker.prove_obligation(
checkpoint,
forward,
obligation,
property.null_guard.as_ref(),
);
if result.result == crate::verify::report::CheckResult::Unknown {
if let Some(reason) =
super::provenance::pedigree_proof(checker, checkpoint, property, forward, false)
{
return SmtCheckResult::proved(format!("NonNull proved: {reason}"));
}
}
result
}
pub(crate) fn check_for_checkpoint<'tcx>(
checker: &SmtChecker<'tcx>,
caller: rustc_hir::def_id::DefId,
property: &Property<'tcx>,
forward: &ForwardVisitResult<'tcx>,
) -> SmtCheckResult {
let Some(target) = checker.property_target(None, property) else {
return SmtCheckResult::unknown("SMT NonNull target could not be resolved");
};
let obligation = SmtObligation::NonZero { place: target };
checker.prove_obligation_for_checkpoint(caller, forward, obligation)
}