use super::{
common::{SmtCheckResult, SmtChecker, TypeSizeClass},
};
use crate::verify::{
contract::{self, Property, PropertyKind},
helpers::Checkpoint,
report::CheckResult,
verifier::ForwardVisitResult,
};
pub(crate) fn check<'tcx>(
checker: &SmtChecker<'tcx>,
checkpoint: &Checkpoint<'tcx>,
property: &Property<'tcx>,
forward: &ForwardVisitResult<'tcx>,
) -> SmtCheckResult {
let Some(required_ty) = checker.property_required_ty(Some(checkpoint), property) else {
return SmtCheckResult::unknown("ValidPtr type could not be resolved");
};
match checker.type_size_class(checkpoint.caller, required_ty) {
TypeSizeClass::Zero => {
SmtCheckResult::proved(format!("ValidPtr proved by Size({required_ty:?}, 0)"))
}
TypeSizeClass::NonZero => {
let deref_property = contract::decomp::with_kind(property, PropertyKind::Deref);
let deref = checker.check(checkpoint, &deref_property, forward);
match &deref.result {
CheckResult::Proved => {
SmtCheckResult::proved("ValidPtr proved: non-ZST target satisfies Deref")
}
CheckResult::Failed => SmtCheckResult {
result: CheckResult::Failed,
query: None,
notes: vec![String::from(
"ValidPtr failed: non-ZST target does not satisfy Deref",
)],
},
CheckResult::Unknown => {
if let Some(reason) = super::provenance::pedigree_proof(
checker, checkpoint, property, forward, true,
) {
SmtCheckResult::proved(format!("ValidPtr proved: {reason}"))
} else if let Some(reason) = super::field_invariant::discharge_from_contract_fact_with_checkpoint(property, forward, checkpoint) {
SmtCheckResult::proved(format!("ValidPtr proved: {reason}"))
} else if let Some(reason) =
field_invariant_reason(checker, checkpoint, property, forward, required_ty)
{
SmtCheckResult::proved(format!("ValidPtr proved: {reason}"))
} else {
SmtCheckResult::unknown("ValidPtr unknown: non-ZST Deref is not proved")
}
}
}
.with_note(format!("primitive Deref via SMT: {:?}", deref.result))
}
TypeSizeClass::Unknown => {
let deref_property = contract::decomp::with_kind(property, PropertyKind::Deref);
let deref = checker.check(checkpoint, &deref_property, forward);
match &deref.result {
CheckResult::Proved => SmtCheckResult::proved(
"ValidPtr proved: Deref holds, so the formula holds for zero and non-zero sizes",
),
CheckResult::Failed | CheckResult::Unknown => SmtCheckResult::unknown(
"ValidPtr unknown: type size is unresolved and Deref is not proved",
),
}
.with_note("type size: Unknown")
.with_note(format!("primitive Deref via SMT: {:?}", deref.result))
}
}
}
fn field_invariant_reason<'tcx>(
checker: &SmtChecker<'tcx>,
checkpoint: &Checkpoint<'tcx>,
property: &Property<'tcx>,
forward: &ForwardVisitResult<'tcx>,
required_ty: rustc_middle::ty::Ty<'tcx>,
) -> Option<String> {
let target = checker.property_target(Some(checkpoint), property)?;
let required_elements = checker
.property_len_expr(Some(checkpoint), property)
.and_then(|expr| match expr {
crate::verify::contract::ContractExpr::Const(value) => u64::try_from(value).ok(),
_ => None,
});
super::field_invariant::discharge_from_field_invariant(
checker.tcx,
checkpoint.caller,
&target,
forward,
PropertyKind::ValidPtr,
Some(required_ty),
required_elements,
)
}