use rustc_hir::def_id::DefId;
use rustc_middle::{
mir::{Body, Local, Operand, Place, ProjectionElem},
ty::{Region, Ty, TyCtxt},
};
use z3::{
Context,
ast::{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<'ctx> {
Field,
Element(Int<'ctx>),
Byte,
}
#[derive(Clone, Debug)]
pub(crate) struct Provenance<'ctx> {
pub alloc_id: AllocId,
pub offset: Int<'ctx>,
pub offset_kind: Option<OffsetKind<'ctx>>,
}
#[derive(Clone, Debug, Default)]
pub(crate) struct ValueInvariants<'ctx> {
pub non_null: bool,
pub init: bool,
pub in_bounds: bool,
pub align_n: Option<Int<'ctx>>,
}
#[derive(Clone, Debug)]
pub(crate) struct VmValue<'ctx, 'tcx> {
pub term: Int<'ctx>,
pub ty: Ty<'tcx>,
pub provenance: Option<Provenance<'ctx>>,
pub invariants: ValueInvariants<'ctx>,
pub source: ValueSource<'ctx>,
}
impl<'ctx, 'tcx> VmValue<'ctx, 'tcx> {
pub(crate) fn new(term: Int<'ctx>, ty: Ty<'tcx>) -> Self {
VmValue {
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<'ctx>> {
match &self.source {
ValueSource::Discriminant(d) => Some(d),
_ => None,
}
}
pub(crate) fn bool_cond(&self) -> Option<&Bool<'ctx>> {
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<'ctx> {
Object,
Slice { len: Int<'ctx> },
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<'ctx, 'tcx> {
pub target_ty: Option<Ty<'tcx>>,
pub aligned_ty: Option<Ty<'tcx>>,
pub allocated: Option<(Ty<'tcx>, Int<'ctx>)>,
pub owning: bool,
}
#[derive(Clone, Debug)]
pub(crate) struct Allocation<'ctx, 'tcx> {
pub base: Int<'ctx>,
pub size: Int<'ctx>,
pub align: Int<'ctx>,
pub element_ty: ContentTy<'tcx>,
pub kind: AllocKind<'ctx>,
pub dead: bool,
pub initialized: bool,
pub liveness: Option<Region<'tcx>>,
pub nul_terminated: bool,
pub for_each: ForEachFacts<'ctx, 'tcx>,
pub parent: Option<AllocId>,
pub slice_data: Option<AllocId>,
}
impl<'ctx, 'tcx> Allocation<'ctx, 'tcx> {
pub(crate) fn new(
base: Int<'ctx>,
size: Int<'ctx>,
align: Int<'ctx>,
element_ty: Option<Ty<'tcx>>,
kind: AllocKind<'ctx>,
) -> Self {
Allocation {
base,
size,
align,
element_ty: element_ty.into(),
kind,
dead: false,
initialized: false,
liveness: None,
nul_terminated: false,
for_each: ForEachFacts::default(),
parent: None,
slice_data: None,
}
}
pub(crate) fn is_external(&self) -> bool {
matches!(self.kind, AllocKind::External)
}
pub(crate) fn slice_len(&self) -> Option<&Int<'ctx>> {
match &self.kind {
AllocKind::Slice { len } => Some(len),
_ => None,
}
}
pub(crate) fn set_slice_len(&mut self, len: Int<'ctx>) {
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(Clone, Debug, Default)]
pub(crate) struct ByteInfo<'ctx> {
pub value: Option<Int<'ctx>>,
pub init: bool,
pub nul: Option<bool>,
}
#[derive(Default)]
pub(crate) struct InlineCtx<'ctx, 'tcx> {
pub inline_depth: usize,
pub arg_referents: Vec<Option<Local>>,
pub deferred_field_writes: Vec<(Local, Vec<usize>, VmValue<'ctx, 'tcx>)>,
}
#[derive(Clone, Debug)]
pub(crate) enum ValueSource<'ctx> {
None,
FieldOffset,
Discriminant(Int<'ctx>),
Comparison {
lhs: Option<PlaceKey>,
rhs: Option<PlaceKey>,
op: rustc_middle::mir::BinOp,
cond: Bool<'ctx>,
},
BinaryOp {
lhs: Option<PlaceKey>,
rhs: Option<PlaceKey>,
op: rustc_middle::mir::BinOp,
},
}
impl<'ctx> ValueSource<'ctx> {
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<'ctx> {
match self {
ValueSource::FieldOffset => ValueSource::FieldOffset,
_ => ValueSource::None,
}
}
}
#[derive(Default)]
pub(crate) struct Memory<'ctx, 'tcx> {
pub(crate) allocations: Vec<Allocation<'ctx, 'tcx>>,
pub(crate) bytes: FxHashMap<(AllocId, usize), ByteInfo<'ctx>>,
pub(crate) fields: FxHashMap<(AllocId, Ty<'tcx>, Vec<usize>), VmValue<'ctx, 'tcx>>,
}
#[derive(Default)]
pub(crate) struct Constraints<'ctx, 'tcx> {
pub(crate) assertions: Vec<Bool<'ctx>>,
pub(crate) term_caches: TermCaches<'ctx, 'tcx>,
}
#[derive(Default)]
pub(crate) struct TermCaches<'ctx, 'tcx> {
pub(crate) sizes: FxHashMap<Ty<'tcx>, Int<'ctx>>,
pub(crate) aligns: FxHashMap<Ty<'tcx>, Int<'ctx>>,
pub(crate) not_mask_terms: FxHashSet<Int<'ctx>>,
pub(crate) exact_div_roots: FxHashMap<Int<'ctx>, Int<'ctx>>,
pub(crate) div_roots: FxHashMap<Int<'ctx>, (Int<'ctx>, Int<'ctx>)>,
pub(crate) iter_ptr_offset: FxHashMap<AllocId, (Int<'ctx>, Option<Int<'ctx>>)>,
}
pub(crate) struct FrameState<'ctx, 'tcx> {
pub(crate) current_def_id: DefId,
pub(crate) local_values: FxHashMap<Local, VmValue<'ctx, 'tcx>>,
pub(crate) field_values: FxHashMap<(Local, Vec<usize>), VmValue<'ctx, 'tcx>>,
pub(crate) local_alloc: FxHashMap<Local, AllocId>,
}
pub(crate) struct VmState<'ctx, 'tcx> {
pub(crate) ctx: &'ctx Context,
pub(crate) tcx: TyCtxt<'tcx>,
pub(crate) current_frame: FrameState<'ctx, 'tcx>,
pub(crate) caller_frames: Vec<FrameState<'ctx, 'tcx>>,
pub(crate) memory: Memory<'ctx, 'tcx>,
pub(crate) constraints: Constraints<'ctx, 'tcx>,
pub(crate) inline: InlineCtx<'ctx, 'tcx>,
pub(crate) path_facts: PathFacts,
}
impl<'ctx, 'tcx> VmState<'ctx, 'tcx> {
pub(crate) fn new(
ctx: &'ctx Context,
tcx: TyCtxt<'tcx>,
path: &Path,
caller_def_id: DefId,
) -> Self {
let reenter = path.reenters();
Self {
ctx,
tcx,
current_frame: FrameState {
current_def_id: caller_def_id,
local_values: FxHashMap::default(),
field_values: FxHashMap::default(),
local_alloc: FxHashMap::default(),
},
caller_frames: Vec::default(),
memory: Memory::default(),
inline: InlineCtx::default(),
constraints: Constraints::default(),
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<'ctx, 'tcx> {
FrameState {
current_def_id: self.current_frame.current_def_id,
local_values: std::mem::take(&mut self.current_frame.local_values),
field_values: std::mem::take(&mut self.current_frame.field_values),
local_alloc: std::mem::take(&mut self.current_frame.local_alloc),
}
}
pub(crate) fn restore_frame(&mut self, frame: FrameState<'ctx, 'tcx>) {
self.current_frame = frame;
}
pub(crate) fn local_value(&self, local: Local) -> Option<&VmValue<'ctx, 'tcx>> {
self.current_frame.local_values.get(&local)
}
pub(crate) fn set_local(&mut self, local: Local, value: VmValue<'ctx, 'tcx>) {
self.current_frame.local_values.insert(local, value);
}
pub(crate) fn local_address(&mut self, local: Local) -> Int<'ctx> {
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<'ctx>,
align: Int<'ctx>,
element_ty: Option<Ty<'tcx>>,
) -> (AllocId, Int<'ctx>) {
self.allocate_internal(size, align, element_ty, AllocKind::Object)
}
pub(crate) fn allocate_external(
&mut self,
size: Int<'ctx>,
align: Int<'ctx>,
element_ty: Option<Ty<'tcx>>,
) -> (AllocId, Int<'ctx>) {
self.allocate_internal(size, align, element_ty, AllocKind::External)
}
pub(crate) fn allocate_slice(
&mut self,
len: Int<'ctx>,
elem_size: Int<'ctx>,
align: Int<'ctx>,
element_ty: Option<Ty<'tcx>>,
) -> (AllocId, Int<'ctx>) {
let size = Int::mul(self.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<'ctx>,
align: Int<'ctx>,
element_ty: Option<Ty<'tcx>>,
kind: AllocKind<'ctx>,
) -> (AllocId, Int<'ctx>) {
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.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<'ctx, 'tcx> {
&self.memory.allocations[id.0]
}
pub(crate) fn alloc_mut(&mut self, id: AllocId) -> &mut Allocation<'ctx, 'tcx> {
&mut self.memory.allocations[id.0]
}
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<'ctx> {
Int::fresh_const(self.ctx, prefix)
}
pub(crate) fn field_value(&self, local: Local, path: &[usize]) -> Option<&VmValue<'ctx, 'tcx>> {
self.current_frame.field_values.get(&(local, path.to_vec()))
}
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<'ctx>)> {
let mut best: Option<(usize, AllocId, Int<'ctx>)> = None;
for ((l, path), v) in &self.current_frame.field_values {
if *l != local || path.last() != Some(&1) {
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<'ctx, 'tcx>> {
self.field_value(local, &[0, 0])
.filter(|v| v.provenance_alloc_id().is_some())
.or_else(|| {
self.current_frame
.field_values
.iter()
.find(|((l, _), v)| *l == local && v.provenance_alloc_id().is_some())
.map(|(_, v)| v)
})
}
pub(crate) fn invalidate_owner_field(&mut self, local: Local) {
let owner_path = if self
.field_value(local, &[0, 0])
.is_some_and(|v| v.provenance_alloc_id().is_some())
{
Some(vec![0, 0])
} else {
self.current_frame
.field_values
.iter()
.find(|((l, _), v)| *l == local && v.provenance_alloc_id().is_some())
.map(|((_, path), _)| path.clone())
};
if let Some(path) = owner_path {
if let Some(mut fv) = self.field_value(local, &path).cloned() {
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<'ctx, 'tcx>,
) {
self.current_frame.field_values.insert((local, path), value);
}
pub(crate) fn assert_all(&self, solver: &z3::Solver<'ctx>) {
for cond in &self.constraints.assertions {
solver.assert(cond);
}
let zero = Int::from_u64(self.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));
}
}
for (_local, value) in self.current_frame.local_values.iter() {
self.assert_value_constraints(solver, value);
}
for value in self.current_frame.field_values.values() {
self.assert_value_constraints(solver, value);
}
}
fn assert_value_constraints(&self, solver: &z3::Solver<'ctx>, value: &VmValue<'ctx, 'tcx>) {
let zero = Int::from_u64(self.ctx, 0);
if value.invariants.non_null {
solver.assert(&value.term._eq(&zero).not());
}
if let Some(ref prov) = value.provenance {
let alloc = self.alloc(prov.alloc_id);
let expected = Int::add(self.ctx, &[&alloc.base, &prov.offset]);
solver.assert(&value.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.term.ge(&zero));
}
if matches!(value.ty.kind(), rustc_middle::ty::TyKind::Bool) {
let one = Int::from_u64(self.ctx, 1);
solver.assert(&value.term.le(&one));
}
if matches!(value.ty.kind(), rustc_middle::ty::TyKind::Char) {
let max = Int::from_u64(self.ctx, 0x10FFFF);
solver.assert(&value.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_values.len())
.field("allocations_count", &self.memory.allocations.len())
.field("assertions", &self.constraints.assertions.len())
.finish()
}
}
impl<'ctx, 'tcx> VmState<'ctx, 'tcx> {
pub(crate) fn value_of_operand(&self, operand: &Operand<'tcx>) -> VmValue<'ctx, '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.ctx, v as i64)
} else {
Int::from_u64(self.ctx, v as u64)
}
} else {
let name = format!("const_{}", text.replace([':', '#', ' '], "_"));
Int::new_const(self.ctx, name.as_str())
};
let ty = constant.const_.ty();
VmValue {
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,
),
}
}
pub(crate) fn value_of_place(&self, place: &Place<'tcx>) -> Option<VmValue<'ctx, 'tcx>> {
if place.projection.is_empty() {
return self.current_frame.local_values.get(&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.current_frame.local_values.get(&place.local) {
if let Some(ref prov) = base_val.provenance {
return Some(VmValue {
term: base_val.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.current_frame.local_values.get(&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_field(alloc_id, view_ty, &field_path).cloned() {
return Some(val);
}
}
}
}
}
let mut base = self.current_frame.local_values.get(&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 byte_vals: Vec<_> = self.alloc_byte_values(alloc_id);
if !byte_vals.is_empty() {
let inner_ty = {
let decl_ty = self.body().local_decls[place.local].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.current_frame.local_values.get(local) {
if let Some(concrete_idx) = index_val.term.as_u64() {
let offset = concrete_idx as usize * step;
let term = self
.get_byte_value(alloc_id, offset)
.cloned()
.unwrap_or_else(|| self.fresh_int("arr_elem"));
return Some(VmValue {
term,
ty: place_ty,
provenance: None,
invariants: ValueInvariants::default(),
source: ValueSource::None,
});
} else {
let mut chain = self.fresh_int("arr_elem");
for (offset, term) in byte_vals.iter().rev() {
let vidx = offset / step;
let idx_term = Int::from_u64(self.ctx, vidx as u64);
let cond = index_val.term._eq(&idx_term);
chain = Bool::ite(&cond, term, &chain);
}
return Some(VmValue {
term: chain,
ty: place_ty,
provenance: None,
invariants: ValueInvariants::default(),
source: ValueSource::None,
});
}
}
}
}
}
return Some(base.clone());
}
match proj.kind() {
ProjectionElem::Deref => {
if let Some(v) = self.field_value(place.local, &[]).cloned() {
return Some(v);
}
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<'ctx, 'tcx> {
let ty = place.ty(self.body(), self.tcx).ty;
VmValue::new(self.fresh_int("unknown"), ty)
}
}