use rustc_hir::def_id::DefId;
use rustc_middle::{
mir::{Body, Local, Operand, Place, ProjectionElem},
ty::{Region, Ty, TyCtxt},
};
use z3::{
Context,
ast::{Array, Ast, Bool, Int},
};
use crate::compat::{FxHashMap, FxHashSet};
use crate::verify::{def_use::PlaceKey, path_extractor::Path};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct AllocId(pub usize);
#[derive(Clone, Debug)]
pub(crate) enum OffsetKind<'z3> {
Field,
Element(Int<'z3>),
Byte,
}
#[derive(Clone, Debug)]
pub(crate) struct Provenance<'z3> {
pub alloc_id: AllocId,
pub offset: Int<'z3>,
pub offset_kind: Option<OffsetKind<'z3>>,
}
#[derive(Clone, Debug, Default)]
pub(crate) struct ValueInvariants<'z3> {
pub non_null: bool,
pub init: bool,
pub in_bounds: bool,
pub align_n: Option<Int<'z3>>,
}
#[derive(Clone, Debug)]
pub(crate) struct VmValue<'z3, 'tcx> {
pub z3_term: Int<'z3>,
pub ty: Ty<'tcx>,
pub provenance: Option<Provenance<'z3>>,
pub invariants: ValueInvariants<'z3>,
pub source: ValueSource<'z3>,
}
impl<'z3, 'tcx> VmValue<'z3, 'tcx> {
pub(crate) fn new(term: Int<'z3>, ty: Ty<'tcx>) -> Self {
VmValue {
z3_term: term,
ty,
provenance: None,
invariants: ValueInvariants::default(),
source: ValueSource::None,
}
}
pub(crate) fn provenance_alloc_id(&self) -> Option<AllocId> {
self.provenance.as_ref().map(|p| p.alloc_id)
}
pub(crate) fn discriminant(&self) -> Option<&Int<'z3>> {
match &self.source {
ValueSource::Discriminant(d) => Some(d),
_ => None,
}
}
pub(crate) fn bool_cond(&self) -> Option<&Bool<'z3>> {
match &self.source {
ValueSource::Comparison { cond, .. } => Some(cond),
_ => None,
}
}
pub(crate) fn is_field_offset(&self) -> bool {
matches!(self.source, ValueSource::FieldOffset)
}
pub(crate) fn is_pointer(&self) -> bool {
self.provenance.is_some()
}
}
#[derive(Clone, Debug)]
pub(crate) enum AllocKind<'z3> {
Object,
Slice { len: Int<'z3> },
External,
}
#[derive(Clone, Debug)]
pub(crate) enum ContentTy<'tcx> {
Typed(Ty<'tcx>),
Generic,
}
impl<'tcx> ContentTy<'tcx> {
pub(crate) fn as_ty(&self) -> Option<Ty<'tcx>> {
match self {
ContentTy::Typed(t) => Some(*t),
ContentTy::Generic => None,
}
}
pub(crate) fn is_generic(&self) -> bool {
matches!(self, ContentTy::Generic)
}
}
impl<'tcx> From<Option<Ty<'tcx>>> for ContentTy<'tcx> {
fn from(o: Option<Ty<'tcx>>) -> Self {
match o {
Some(t) => ContentTy::Typed(t),
None => ContentTy::Generic,
}
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct ForEachFacts<'z3, 'tcx> {
pub target_ty: Option<Ty<'tcx>>,
pub aligned_ty: Option<Ty<'tcx>>,
pub allocated: Option<(Ty<'tcx>, Int<'z3>)>,
pub owning: bool,
}
#[derive(Clone, Debug, Default)]
pub(crate) struct AllocFacts<'z3, 'tcx> {
pub dead: bool,
pub initialized: bool,
pub liveness: Option<Region<'tcx>>,
pub for_each: ForEachFacts<'z3, 'tcx>,
pub cstr_trusted: bool,
}
#[derive(Clone, Debug)]
pub(crate) struct Allocation<'z3, 'tcx> {
pub base: Int<'z3>,
pub size: Int<'z3>,
pub align: Int<'z3>,
pub element_ty: ContentTy<'tcx>,
pub kind: AllocKind<'z3>,
pub facts: AllocFacts<'z3, 'tcx>,
pub parent: Option<AllocId>,
}
impl<'z3, 'tcx> Allocation<'z3, 'tcx> {
pub(crate) fn new(
base: Int<'z3>,
size: Int<'z3>,
align: Int<'z3>,
element_ty: Option<Ty<'tcx>>,
kind: AllocKind<'z3>,
) -> Self {
Allocation {
base,
size,
align,
element_ty: element_ty.into(),
kind,
facts: AllocFacts::default(),
parent: None,
}
}
pub(crate) fn is_external(&self) -> bool {
matches!(self.kind, AllocKind::External)
}
pub(crate) fn slice_len(&self) -> Option<&Int<'z3>> {
match &self.kind {
AllocKind::Slice { len } => Some(len),
_ => None,
}
}
pub(crate) fn set_slice_len(&mut self, len: Int<'z3>) {
self.kind = AllocKind::Slice { len };
}
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct PathFacts {
pub reenter: bool,
pub split_transmute_asserted: bool,
pub alias_hazard_accepted: bool,
pub has_checked_bounds: bool,
pub saw_next_discriminant: bool,
}
#[derive(Default)]
pub(crate) struct InlineCtx<'z3, 'tcx> {
pub inline_depth: usize,
pub arg_referents: Vec<Option<Local>>,
pub deferred_field_writes: Vec<(Local, Vec<usize>, VmValue<'z3, 'tcx>)>,
}
#[derive(Clone, Debug)]
pub(crate) enum ValueSource<'z3> {
None,
FieldOffset,
Discriminant(Int<'z3>),
Comparison {
lhs: Option<PlaceKey>,
rhs: Option<PlaceKey>,
op: rustc_middle::mir::BinOp,
cond: Bool<'z3>,
},
BinaryOp {
lhs: Option<PlaceKey>,
rhs: Option<PlaceKey>,
op: rustc_middle::mir::BinOp,
},
}
impl<'z3> ValueSource<'z3> {
pub(crate) fn operands(&self) -> Option<(&Option<PlaceKey>, &Option<PlaceKey>, rustc_middle::mir::BinOp)> {
match self {
ValueSource::Comparison { lhs, rhs, op, .. } => Some((lhs, rhs, *op)),
ValueSource::BinaryOp { lhs, rhs, op } => Some((lhs, rhs, *op)),
_ => None,
}
}
pub(crate) fn field_offset_only(&self) -> ValueSource<'z3> {
match self {
ValueSource::FieldOffset => ValueSource::FieldOffset,
_ => ValueSource::None,
}
}
}
#[derive(Default)]
pub(crate) struct Memory<'z3, 'tcx> {
pub(crate) allocations: Vec<Allocation<'z3, 'tcx>>,
pub(crate) byte_arrays: FxHashMap<AllocId, Array<'z3>>,
pub(crate) byte_max: FxHashMap<AllocId, usize>,
pub(crate) values: FxHashMap<(AllocId, Ty<'tcx>, Vec<usize>), VmValue<'z3, 'tcx>>,
}
#[derive(Default)]
pub(crate) struct Constraints<'z3, 'tcx> {
pub(crate) assertions: Vec<Bool<'z3>>,
pub(crate) term_caches: TermCaches<'z3, 'tcx>,
}
#[derive(Default)]
pub(crate) struct TermCaches<'z3, 'tcx> {
pub(crate) sizes: FxHashMap<Ty<'tcx>, Int<'z3>>,
pub(crate) aligns: FxHashMap<Ty<'tcx>, Int<'z3>>,
pub(crate) not_mask_terms: FxHashSet<Int<'z3>>,
pub(crate) exact_div_roots: FxHashMap<Int<'z3>, Int<'z3>>,
pub(crate) div_roots: FxHashMap<Int<'z3>, (Int<'z3>, Int<'z3>)>,
pub(crate) iter_ptr_offset: FxHashMap<AllocId, (Int<'z3>, Option<Int<'z3>>)>,
pub(crate) uninit_byte: Option<Int<'z3>>,
}
pub(crate) struct FrameState {
pub(crate) current_def_id: DefId,
pub(crate) local_alloc: FxHashMap<Local, AllocId>,
}
pub(crate) struct VmState<'z3, 'tcx> {
pub(crate) z3_ctx: &'z3 Context,
pub(crate) tcx: TyCtxt<'tcx>,
pub(crate) current_frame: FrameState,
pub(crate) caller_frames: Vec<FrameState>,
pub(crate) memory: Memory<'z3, 'tcx>,
pub(crate) constraints: Constraints<'z3, 'tcx>,
pub(crate) inline: InlineCtx<'z3, 'tcx>,
pub(crate) path_facts: PathFacts,
}
impl<'z3, 'tcx> VmState<'z3, 'tcx> {
pub(crate) fn new(
z3_ctx: &'z3 Context,
tcx: TyCtxt<'tcx>,
path: &Path,
caller_def_id: DefId,
) -> Self {
let reenter = path.reenters();
let mut constraints = Constraints::default();
let uninit = Int::fresh_const(z3_ctx, "uninit_byte");
constraints.term_caches.uninit_byte = Some(uninit.clone());
constraints
.assertions
.push(uninit.ge(&Int::from_u64(z3_ctx, 256)));
Self {
z3_ctx,
tcx,
current_frame: FrameState {
current_def_id: caller_def_id,
local_alloc: FxHashMap::default(),
},
caller_frames: Vec::default(),
memory: Memory::default(),
inline: InlineCtx::default(),
constraints,
path_facts: PathFacts {
reenter,
..PathFacts::default()
},
}
}
pub(crate) fn body(&self) -> &'tcx Body<'tcx> {
self.tcx.optimized_mir(self.current_frame.current_def_id)
}
pub(crate) fn save_frame(&mut self) -> FrameState {
FrameState {
current_def_id: self.current_frame.current_def_id,
local_alloc: std::mem::take(&mut self.current_frame.local_alloc),
}
}
pub(crate) fn restore_frame(&mut self, frame: FrameState) {
self.current_frame = frame;
}
pub(crate) fn local_value(&self, local: Local) -> Option<&VmValue<'z3, 'tcx>> {
let alloc_id = *self.current_frame.local_alloc.get(&local)?;
let view_ty = self.body().local_decls[local].ty;
self.load_value(alloc_id, view_ty, &[])
}
pub(crate) fn set_local(&mut self, local: Local, value: VmValue<'z3, 'tcx>) {
self.ensure_local_allocation(local);
let alloc_id = self.current_frame.local_alloc[&local];
let view_ty = self.body().local_decls[local].ty;
self.store_value(alloc_id, view_ty, vec![], value);
}
pub(crate) fn local_address(&mut self, local: Local) -> Int<'z3> {
self.ensure_local_allocation(local);
let id = self.current_frame.local_alloc[&local];
self.memory.allocations[id.0].base.clone()
}
pub(crate) fn allocate(
&mut self,
size: Int<'z3>,
align: Int<'z3>,
element_ty: Option<Ty<'tcx>>,
) -> (AllocId, Int<'z3>) {
self.allocate_internal(size, align, element_ty, AllocKind::Object)
}
pub(crate) fn allocate_external(
&mut self,
size: Int<'z3>,
align: Int<'z3>,
element_ty: Option<Ty<'tcx>>,
) -> (AllocId, Int<'z3>) {
self.allocate_internal(size, align, element_ty, AllocKind::External)
}
pub(crate) fn allocate_slice(
&mut self,
len: Int<'z3>,
elem_size: Int<'z3>,
align: Int<'z3>,
element_ty: Option<Ty<'tcx>>,
) -> (AllocId, Int<'z3>) {
let size = Int::mul(self.z3_ctx, &[&len, &elem_size]);
let (id, base) = self.allocate(size, align, element_ty);
self.alloc_mut(id).set_slice_len(len);
(id, base)
}
fn allocate_internal(
&mut self,
size: Int<'z3>,
align: Int<'z3>,
element_ty: Option<Ty<'tcx>>,
kind: AllocKind<'z3>,
) -> (AllocId, Int<'z3>) {
let id = AllocId(self.memory.allocations.len());
let base = {
let name = format!(
"{}_{}",
if matches!(kind, AllocKind::External) {
"ext"
} else {
"heap"
},
id.0
);
Int::new_const(self.z3_ctx, name.as_str())
};
let alloc = Allocation::new(base.clone(), size, align, element_ty, kind);
self.memory.allocations.push(alloc);
(id, base)
}
pub(crate) fn alloc(&self, id: AllocId) -> &Allocation<'z3, 'tcx> {
&self.memory.allocations[id.0]
}
pub(crate) fn alloc_mut(&mut self, id: AllocId) -> &mut Allocation<'z3, 'tcx> {
&mut self.memory.allocations[id.0]
}
pub(crate) fn is_cstr_trusted(&self, id: AllocId) -> bool {
self.alloc(id).facts.cstr_trusted
}
pub(crate) fn root_alloc(&self, id: AllocId) -> AllocId {
let mut cur = id;
let mut guard = 0;
while let Some(parent) = self.alloc(cur).parent {
cur = parent;
guard += 1;
if guard > self.memory.allocations.len() {
break;
}
}
cur
}
pub(crate) fn fresh_int(&self, prefix: &str) -> Int<'z3> {
Int::fresh_const(self.z3_ctx, prefix)
}
pub(crate) fn field_value(&self, local: Local, path: &[usize]) -> Option<&VmValue<'z3, 'tcx>> {
let alloc_id = *self.current_frame.local_alloc.get(&local)?;
let view_ty = self.body().local_decls[local].ty;
self.load_value(alloc_id, view_ty, path)
}
pub(crate) fn field_paths(&self, local: Local) -> Vec<Vec<usize>> {
let Some(&alloc_id) = self.current_frame.local_alloc.get(&local) else {
return Vec::new();
};
let view_ty = self.body().local_decls[local].ty;
self.memory
.values
.keys()
.filter(|(a, t, p)| *a == alloc_id && *t == view_ty && !p.is_empty())
.map(|(_, _, p)| p.clone())
.collect()
}
fn frame_local_ty(&self, frame: &FrameState, local: Local) -> Ty<'tcx> {
self.tcx.optimized_mir(frame.current_def_id).local_decls[local].ty
}
pub(crate) fn frame_field_paths(
&self,
frame: &FrameState,
local: Local,
) -> Vec<Vec<usize>> {
let Some(&alloc_id) = frame.local_alloc.get(&local) else {
return Vec::new();
};
let view_ty = self.frame_local_ty(frame, local);
self.memory
.values
.keys()
.filter(|(a, t, p)| *a == alloc_id && *t == view_ty && !p.is_empty())
.map(|(_, _, p)| p.clone())
.collect()
}
pub(crate) fn frame_field_value(
&self,
frame: &FrameState,
local: Local,
path: &[usize],
) -> Option<&VmValue<'z3, 'tcx>> {
let alloc_id = *frame.local_alloc.get(&local)?;
let view_ty = self.frame_local_ty(frame, local);
self.load_value(alloc_id, view_ty, path)
}
pub(crate) fn frame_local_value(
&self,
frame: &FrameState,
local: Local,
) -> Option<&VmValue<'z3, 'tcx>> {
let alloc_id = *frame.local_alloc.get(&local)?;
let view_ty = self.frame_local_ty(frame, local);
self.load_value(alloc_id, view_ty, &[])
}
pub(crate) fn all_local_values(&self) -> Vec<(Local, &VmValue<'z3, 'tcx>)> {
self.current_frame
.local_alloc
.keys()
.copied()
.filter_map(|local| self.local_value(local).map(|value| (local, value)))
.collect()
}
pub(crate) fn iter_buffer(&self, local: Local) -> Option<AllocId> {
self.field_value(local, &[1])
.and_then(|end| end.provenance.as_ref())
.map(|ep| ep.alloc_id)
}
pub(crate) fn iter_utf8_buffer(&self, local: Local) -> Option<(AllocId, Int<'z3>)> {
let mut best: Option<(usize, AllocId, Int<'z3>)> = None;
for path in self.field_paths(local) {
if path.last() != Some(&1) {
continue;
}
let Some(v) = self.field_value(local, &path) else {
continue;
};
let Some(prov) = v.provenance.as_ref() else {
continue;
};
if best.as_ref().map_or(true, |(depth, _, _)| path.len() < *depth) {
best = Some((path.len(), prov.alloc_id, prov.offset.clone()));
}
}
best.map(|(_, id, off)| (id, off))
}
pub(crate) fn owner_ptr_field(&self, local: Local) -> Option<&VmValue<'z3, 'tcx>> {
let ty = self.body().local_decls[local].ty;
if let Some((path, _)) = self.container_ptr_field(ty) {
if let Some(v) = self.field_value(local, &path) {
if v.provenance_alloc_id().is_some() {
return Some(v);
}
}
}
self.field_paths(local)
.iter()
.find_map(|path| {
self.field_value(local, path)
.filter(|v| v.provenance_alloc_id().is_some())
})
}
pub(crate) fn invalidate_owner_field(&mut self, local: Local) {
let ty = self.body().local_decls[local].ty;
let Some((path, _)) = self.container_ptr_field(ty) else {
return;
};
let Some(mut fv) = self.field_value(local, &path).cloned() else {
return;
};
if fv.provenance_alloc_id().is_some() {
fv.provenance = None;
self.set_field_value(local, path, fv);
}
}
pub(crate) fn set_field_value(
&mut self,
local: Local,
path: Vec<usize>,
value: VmValue<'z3, 'tcx>,
) {
let view_ty = self.body().local_decls[local].ty;
self.ensure_local_allocation(local);
let alloc_id = self.current_frame.local_alloc[&local];
self.store_value(alloc_id, view_ty, path, value);
}
pub(crate) fn assert_all(&self, solver: &z3::Solver<'z3>) {
for cond in &self.constraints.assertions {
solver.assert(cond);
}
let zero = Int::from_u64(self.z3_ctx, 0);
for alloc in &self.memory.allocations {
if !alloc.is_external() {
solver.assert(&alloc.base._eq(&zero).not());
}
solver.assert(&alloc.size.ge(&zero));
if alloc.align.simplify().as_u64() != Some(1) {
solver.assert(&alloc.base.rem(&alloc.align)._eq(&zero));
}
}
let local_ids: Vec<Local> = self.current_frame.local_alloc.keys().copied().collect();
for local in local_ids {
if let Some(value) = self.local_value(local) {
self.assert_value_constraints(solver, value);
}
for path in self.field_paths(local) {
if let Some(value) = self.field_value(local, &path) {
self.assert_value_constraints(solver, value);
}
}
}
}
fn assert_value_constraints(&self, solver: &z3::Solver<'z3>, value: &VmValue<'z3, 'tcx>) {
let zero = Int::from_u64(self.z3_ctx, 0);
if value.invariants.non_null {
solver.assert(&value.z3_term._eq(&zero).not());
}
if let Some(ref prov) = value.provenance {
let alloc = self.alloc(prov.alloc_id);
let expected = Int::add(self.z3_ctx, &[&alloc.base, &prov.offset]);
solver.assert(&value.z3_term._eq(&expected));
}
if matches!(
value.ty.kind(),
rustc_middle::ty::TyKind::Uint(_)
| rustc_middle::ty::TyKind::Bool
| rustc_middle::ty::TyKind::Char
) {
solver.assert(&value.z3_term.ge(&zero));
}
if matches!(value.ty.kind(), rustc_middle::ty::TyKind::Bool) {
let one = Int::from_u64(self.z3_ctx, 1);
solver.assert(&value.z3_term.le(&one));
}
if matches!(value.ty.kind(), rustc_middle::ty::TyKind::Char) {
let max = Int::from_u64(self.z3_ctx, 0x10FFFF);
solver.assert(&value.z3_term.le(&max));
}
}
}
impl std::fmt::Debug for VmState<'_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VmState")
.field("locals_count", &self.current_frame.local_alloc.len())
.field("allocations_count", &self.memory.allocations.len())
.field("assertions", &self.constraints.assertions.len())
.finish()
}
}
impl<'z3, 'tcx> VmState<'z3, 'tcx> {
pub(crate) fn value_of_operand(&self, operand: &Operand<'tcx>) -> VmValue<'z3, 'tcx> {
match operand {
Operand::Copy(place) | Operand::Move(place) => self
.value_of_place(place)
.unwrap_or_else(|| self.unknown_value_for_place(place)),
Operand::Constant(constant) => {
let text = format!("{:?}", constant.const_);
if let rustc_middle::mir::Const::Unevaluated(uneval, _) = constant.const_ {
let def_name = self.tcx.def_path_str(uneval.def);
let is_size = def_name.ends_with("SizedTypeProperties::SIZE");
let is_align = def_name.ends_with("SizedTypeProperties::ALIGN");
if (is_size || is_align) && !uneval.args.is_empty() {
let ty = uneval.args.type_at(0);
let term = if is_size {
self.size_sym_read(ty)
} else {
self.align_sym_read(ty)
};
return VmValue::new(term, constant.const_.ty());
}
}
let int_val = crate::helpers::mir_utils::eval_const_scalar_int(
self.tcx,
&constant.const_,
&text,
);
let field_offset = int_val.is_none()
&& crate::helpers::mir_utils::offset_of_container(self.tcx, &constant.const_)
.is_some();
let term = if let Some(v) = int_val {
if v < 0 {
Int::from_i64(self.z3_ctx, v as i64)
} else {
Int::from_u64(self.z3_ctx, v as u64)
}
} else {
let name = format!("const_{}", text.replace([':', '#', ' '], "_"));
Int::new_const(self.z3_ctx, name.as_str())
};
let ty = constant.const_.ty();
VmValue {
z3_term: term,
ty,
provenance: None,
invariants: ValueInvariants::default(),
source: if field_offset {
ValueSource::FieldOffset
} else {
ValueSource::None
},
}
}
#[cfg(rapx_ge_95)]
Operand::RuntimeChecks(_) => VmValue::new(
self.fresh_int("runtime_checks"),
self.body().local_decls[Local::from_usize(0)].ty,
),
}
}
fn byte_from_field(&self, alloc_id: AllocId, ty: Ty<'tcx>, offset: usize) -> Option<Int<'z3>> {
let rustc_middle::ty::TyKind::Adt(adt_def, substs) = ty.kind() else {
return None;
};
let variant = adt_def.non_enum_variant();
for (idx, field_def) in variant.fields.iter().enumerate() {
let field_ty = crate::helpers::mir_utils::field_ty(self.tcx, field_def, substs);
let field_off = self.field_offset_in_bytes(ty, idx) as usize;
let field_size = self.size_of_ty(field_ty) as usize;
if offset >= field_off && offset < field_off + field_size {
let fv = self.load_value(alloc_id, ty, &[idx])?;
let byte_idx = offset - field_off;
let divisor = Int::from_u64(self.z3_ctx, 1u64 << (byte_idx * 8));
let modulus = Int::from_u64(self.z3_ctx, 256);
return Some(fv.z3_term.div(&divisor).rem(&modulus));
}
}
None
}
pub(crate) fn value_of_place(&self, place: &Place<'tcx>) -> Option<VmValue<'z3, 'tcx>> {
if place.projection.is_empty() {
return self.local_value(place.local).cloned();
}
let place_ty = place.ty(self.body(), self.tcx).ty;
let field_path: Vec<usize> = place
.projection
.iter()
.filter_map(|proj| match proj.kind() {
ProjectionElem::Field(field_idx, _) => Some(field_idx.as_usize()),
_ => None,
})
.collect();
let is_pure_field = place.projection.iter().all(|p| {
matches!(
p.kind(),
ProjectionElem::Field(..) | ProjectionElem::Downcast(..)
)
});
let has_downcast = place
.projection
.iter()
.any(|p| matches!(p.kind(), ProjectionElem::Downcast(..)));
if !field_path.is_empty() && is_pure_field {
if let Some(val) = self.field_value(place.local, &field_path).cloned() {
return Some(val);
}
if !has_downcast {
if let Some(base_val) = self.local_value(place.local) {
if let Some(ref prov) = base_val.provenance {
return Some(VmValue {
z3_term: base_val.z3_term.clone(),
ty: place_ty,
provenance: Some(prov.clone()),
invariants: base_val.invariants.clone(),
source: ValueSource::None,
});
}
}
return None;
}
}
if !field_path.is_empty()
&& field_path.len() < place.projection.len()
&& place
.projection
.iter()
.any(|p| matches!(p.kind(), ProjectionElem::Deref))
{
let non_field_deref = place.projection.iter().all(|p| {
matches!(
p.kind(),
ProjectionElem::Field(..)
| ProjectionElem::Deref
| ProjectionElem::Subslice { .. }
)
});
if non_field_deref {
if let Some(val) = self.field_value(place.local, &field_path).cloned() {
return Some(val);
}
if let Some(base_val) = self.local_value(place.local) {
if let Some(alloc_id) = base_val.provenance_alloc_id() {
let view_ty = crate::helpers::mir_utils::pointee_ty(base_val.ty)
.unwrap_or(base_val.ty);
if let Some(val) = self.load_value(alloc_id, view_ty, &field_path).cloned() {
return Some(val);
}
}
}
}
}
let mut base = self.local_value(place.local)?.clone();
for proj in place.projection.iter() {
match proj.kind() {
ProjectionElem::Deref => {
base.ty = place_ty;
}
ProjectionElem::Field(_field_idx, _) => {
if !field_path.is_empty() {
if let Some(val) = self.field_value(place.local, &field_path).cloned() {
return Some(val);
}
}
base.ty = place_ty;
}
_ => {}
}
}
if let Some(proj) = place.projection.last() {
let prefix_is_deref = place.projection[..place.projection.len() - 1]
.iter()
.all(|p| matches!(p.kind(), ProjectionElem::Deref));
if let ProjectionElem::Index(local) = proj {
if prefix_is_deref {
if let Some(ref prov) = base.provenance {
let alloc_id = prov.alloc_id;
let decl_ty = self.body().local_decls[place.local].ty;
let inner_ty = match decl_ty.kind() {
rustc_middle::ty::TyKind::Array(inner, _) => *inner,
rustc_middle::ty::TyKind::Ref(_, inner, _) => {
match inner.kind() {
rustc_middle::ty::TyKind::Slice(e) => *e,
_ => return Some(base.clone()),
}
}
rustc_middle::ty::TyKind::Slice(e) => *e,
_ => return Some(base.clone()),
};
let elem_sz = self.size_of_ty(inner_ty) as usize;
let step = elem_sz.max(1);
if let Some(index_val) = self.local_value(*local) {
let offset = Int::mul(
self.z3_ctx,
&[&index_val.z3_term, &Int::from_u64(self.z3_ctx, step as u64)],
);
if self.memory.byte_arrays.contains_key(&alloc_id) {
let term = self.byte_read(alloc_id, &offset);
let is_uninit = offset
.simplify()
.as_u64()
.map(|off| !self.is_byte_init(alloc_id, off as usize))
.unwrap_or(false);
if !is_uninit {
return Some(VmValue {
z3_term: term,
ty: place_ty,
provenance: None,
invariants: ValueInvariants::default(),
source: ValueSource::None,
});
}
}
if let Some(off) = offset.simplify().as_u64() {
if let Some(ty) = self.alloc(alloc_id).element_ty.as_ty() {
if let Some(b) = self.byte_from_field(alloc_id, ty, off as usize) {
return Some(VmValue {
z3_term: b,
ty: place_ty,
provenance: None,
invariants: ValueInvariants::default(),
source: ValueSource::None,
});
}
}
}
}
}
}
return Some(base.clone());
}
match proj.kind() {
ProjectionElem::Deref => {
let mut val = base.clone();
val.ty = place_ty;
return Some(val);
}
ProjectionElem::Field(_field_idx, _field_ty) => {
let val = base.clone();
return Some(val);
}
_ => {
let mut val = base.clone();
val.ty = place_ty;
return Some(val);
}
}
}
if place.projection.len() > 1
&& place.projection.iter().any(|p| {
matches!(
p.kind(),
ProjectionElem::Deref | ProjectionElem::Downcast(..)
)
})
{
let mut val = base;
val.ty = place_ty;
return Some(val);
}
None
}
pub(crate) fn unknown_value_for_place(&self, place: &Place<'tcx>) -> VmValue<'z3, 'tcx> {
let ty = place.ty(self.body(), self.tcx).ty;
VmValue::new(self.fresh_int("unknown"), ty)
}
}