use crate::helpers::mir_utils;
use crate::verify::checkpoint::Checkpoint;
use crate::verify::contract::Property;
use crate::verify::report::{CheckResult, UnknownReason};
use crate::verify::vm::state::VmState;
use super::PropertyChecker;
impl PropertyChecker {
pub(super) fn check_alias<'z3, 'tcx>(
&self,
vm_state: &VmState<'z3, 'tcx>,
checkpoint: &Checkpoint<'tcx>,
_property: &Property<'tcx>,
) -> CheckResult {
match crate::verify::vm::alias::check_alias_vm(vm_state, checkpoint) {
crate::verify::vm::alias::VmAliasResult::Proved => CheckResult::ProvedByRule,
crate::verify::vm::alias::VmAliasResult::Failed(_msg) => CheckResult::Failed,
crate::verify::vm::alias::VmAliasResult::Unknown => {
CheckResult::Unknown(UnknownReason::Unimplemented)
}
}
}
pub(super) fn check_owning<'z3, 'tcx>(
&self,
vm_state: &VmState<'z3, 'tcx>,
checkpoint: &Checkpoint<'tcx>,
property: &Property<'tcx>,
) -> CheckResult {
let Some(value) = self.target_value(vm_state, checkpoint, property) else {
return CheckResult::Unknown(UnknownReason::Unimplemented);
};
let value = self.resolve_pointer_provenance(vm_state, value);
let alloc_id = value.provenance_alloc_id().or_else(|| {
vm_state
.find_local_by_address(&value.z3_term)
.and_then(|owner| vm_state.owner_ptr_field(owner))
.and_then(|v| v.provenance_alloc_id())
});
let Some(alloc_id) = alloc_id else {
return CheckResult::Unknown(UnknownReason::Unimplemented);
};
if vm_state.alloc(alloc_id).facts.for_each.owning {
return CheckResult::ProvedByRule;
}
let origin = vm_state.resolve_origin(&value);
let target_place = property.target_place();
let arg_place = target_place
.and_then(|place| match place.base {
crate::verify::contract::PlaceBase::Arg(n) => checkpoint.args.get(n),
_ => None,
})
.and_then(mir_utils::operand_mir_place);
let owning_invariant_field = arg_place.and_then(|arg_place| {
let tree =
crate::verify::vm::alias_tree::AliasTree::build(vm_state.tcx, checkpoint.caller);
let (root, fields) = tree.resolve_local_to_root(arg_place.local);
let field = fields.first().copied().or_else(|| {
arg_place
.projection
.iter()
.find_map(|projection| match projection {
rustc_middle::mir::ProjectionElem::Field(index, _) => {
Some(index.as_usize())
}
_ => None,
})
})?;
let root_ty =
vm_state.body().local_decls[rustc_middle::mir::Local::from_usize(root)].ty;
let root_ty = match root_ty.kind() {
rustc_middle::ty::TyKind::Ref(_, inner, _) => *inner,
_ => root_ty,
};
let rustc_middle::ty::TyKind::Adt(adt, _) = root_ty.kind() else {
return None;
};
Some((adt.did(), field))
});
let owns_field_by_invariant = owning_invariant_field.is_some_and(|(adt_id, field)| {
crate::verify::target::get_struct_invariants_from_annotation(
vm_state.tcx,
adt_id,
adt_id,
)
.iter()
.any(|p| {
p.kind() == Some(crate::verify::contract::PropertyKind::Owning)
&& p.target_place().is_some_and(|place| {
place
.projections
.iter()
.find_map(|projection| match projection {
crate::verify::contract::ContractProjection::Field {
index,
..
} => Some(*index),
_ => None,
})
== Some(field)
})
})
});
let internal_alloc = !vm_state.alloc(alloc_id).is_external();
let local_alloc = vm_state
.current_frame
.local_alloc
.values()
.any(|id| *id == alloc_id);
let untrusted_raw_origin = origin.as_ref().is_none_or(|origin| match origin.kind {
crate::verify::vm::alias::VmOriginKind::RawPtr
| crate::verify::vm::alias::VmOriginKind::SharedRef
| crate::verify::vm::alias::VmOriginKind::Unknown => true,
crate::verify::vm::alias::VmOriginKind::MutRef => !owns_field_by_invariant,
crate::verify::vm::alias::VmOriginKind::Owned(def_id) => {
!(crate::verify::api_classify::is_std_box(def_id)
|| crate::verify::api_classify::is_std_vec(def_id)
|| crate::verify::api_classify::is_std_cstring(def_id)
|| owns_field_by_invariant
|| (internal_alloc && !local_alloc))
}
});
let dest_local = checkpoint.destination.or_else(|| {
let body = vm_state.tcx.optimized_mir(checkpoint.caller);
match &body.basic_blocks[checkpoint.block].terminator().kind {
rustc_middle::mir::TerminatorKind::Call { destination, .. } => {
Some(destination.local)
}
_ => None,
}
});
let live = crate::verify::vm::alias_hazard::live_locals_at(
vm_state.tcx,
checkpoint.caller,
checkpoint.block,
usize::MAX,
true,
true,
);
let typing_env =
rustc_middle::ty::TypingEnv::non_body_analysis(vm_state.tcx, checkpoint.caller);
for local in vm_state.current_frame.local_alloc.keys() {
if Some(*local) == dest_local {
continue;
}
if !live.contains(local) {
continue;
}
let ty = vm_state.body().local_decls[*local].ty;
if !ty.needs_drop(vm_state.tcx, typing_env) {
continue;
}
for path in vm_state.field_paths(*local) {
let Some(val) = vm_state.field_value(*local, &path) else {
continue;
};
if val.provenance_alloc_id() != Some(alloc_id) {
continue;
}
return CheckResult::Failed;
}
}
let matches_owning_precondition = vm_state
.alloc(alloc_id)
.facts
.owning
.as_ref()
.is_some_and(|owner| owner == &value.z3_term);
if untrusted_raw_origin && !matches_owning_precondition {
let fresh_internal = origin.is_none() && internal_alloc && !local_alloc;
let local_manually_drop =
crate::verify::api_classify::is_manually_drop_drop(checkpoint.callee)
&& internal_alloc
&& local_alloc
&& origin.as_ref().is_some_and(|origin| {
matches!(
origin.kind,
crate::verify::vm::alias::VmOriginKind::MutRef
| crate::verify::vm::alias::VmOriginKind::Owned(_)
)
});
if fresh_internal || local_manually_drop {
CheckResult::ProvedByRule
} else {
CheckResult::Unknown(UnknownReason::Unimplemented)
}
} else {
CheckResult::ProvedByRule
}
}
}