use super::common::{SmtCheckResult, SmtChecker, SmtObligation};
use crate::verify::{contract::Property, helpers::Checkpoint, verifier::ForwardVisitResult};
pub(crate) fn check<'tcx>(
checker: &SmtChecker<'tcx>,
checkpoint: &Checkpoint<'tcx>,
property: &Property<'tcx>,
forward: &ForwardVisitResult<'tcx>,
) -> SmtCheckResult {
if let Some(reason) = super::field_invariant::discharge_from_contract_fact_with_checkpoint(property, forward, checkpoint) {
return SmtCheckResult::proved(format!("Allocated proved: {reason}"));
}
let Some(target) = checker.property_target(Some(checkpoint), property) else {
return SmtCheckResult::unknown("Allocated target could not be resolved");
};
if checker.property_required_ty(Some(checkpoint), property).is_none()
&& checker.property_len_expr(Some(checkpoint), property).is_none()
{
if checker.is_len_carrying_place_for_caller(checkpoint.caller, &target) {
return SmtCheckResult::proved(
"allocation proved: safe slice/reference target carries one live allocation object",
);
}
return SmtCheckResult::unknown(
"Allocated target has no type/count arguments and is not a supported slice/reference",
);
}
let Some(required_ty) = checker.property_required_ty(Some(checkpoint), property) else {
return SmtCheckResult::unknown("Allocated type could not be resolved");
};
let Some(elements_expr) = checker.property_len_expr(Some(checkpoint), property) else {
return SmtCheckResult::unknown("Allocated element-count argument could not be resolved");
};
let Some(elements) = checker.contract_expr_to_smt_term(checkpoint.caller, &elements_expr, None)
else {
return SmtCheckResult::unknown(
"Allocated element-count argument could not be lowered to SMT",
);
};
if let Some(reason) = super::provenance::vec_from_raw_parts_roundtrip(checker, checkpoint) {
return SmtCheckResult::proved(format!("Allocated proved: {reason}"));
}
let result = checker.prove_obligation(
checkpoint,
forward,
SmtObligation::Allocated {
place: target.clone(),
ty_name: format!("{required_ty:?}"),
elements: elements.clone(),
},
property.null_guard.as_ref(),
);
if matches!(result.result, crate::verify::report::CheckResult::Proved) {
return result;
}
let required_elements = match elements {
super::common::SmtTerm::Const(value) => Some(value),
_ => None,
};
if let Some(reason) = super::field_invariant::discharge_from_field_invariant(
checker.tcx,
checkpoint.caller,
&target,
forward,
crate::verify::contract::PropertyKind::Allocated,
Some(required_ty),
required_elements,
) {
return SmtCheckResult::proved(format!("Allocated 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("Allocated target could not be resolved");
};
let Some(required_ty) = checker.property_required_ty(None, property) else {
return SmtCheckResult::unknown("Allocated type could not be resolved");
};
let Some(elements_expr) = checker.property_len_expr(None, property) else {
return SmtCheckResult::unknown("Allocated element-count argument could not be resolved");
};
let Some(elements) = checker.contract_expr_to_smt_term(caller, &elements_expr, None) else {
return SmtCheckResult::unknown(
"Allocated element-count argument could not be lowered to SMT",
);
};
let result = checker.prove_obligation_for_checkpoint(
caller,
forward,
SmtObligation::Allocated {
place: target,
ty_name: format!("{required_ty:?}"),
elements,
},
);
if matches!(result.result, crate::verify::report::CheckResult::Proved) {
return result;
}
if let Some(reason) = super::field_invariant::discharge_from_contract_fact(property, forward) {
return SmtCheckResult::proved(format!("Allocated proved: {reason}"));
}
result
}