in checker/src/block_visitor.rs [1778:1922]
fn visit_address_of(&mut self, path: Rc<Path>, place: &mir::Place<'tcx>) {
let target_type = self
.type_visitor()
.get_path_rustc_type(&path, self.bv.current_span);
let source_type = self
.type_visitor()
.get_rustc_place_type(place, self.bv.current_span);
let value_path = self.visit_lh_place(place);
match (source_type.kind(), target_type.kind()) {
(TyKind::Ref(_, st, _), TyKind::Ref(_, tt, _)) if st.eq(tt) => {
// If we are just changing the mutability or lifetime of the reference,
// the value stays the same for us.
let value = self
.bv
.lookup_path_and_refine_result(value_path, source_type);
self.bv.update_value_at(path, value);
return;
}
_ => {}
}
let value = match &value_path.value {
PathEnum::QualifiedPath {
qualifier,
selector,
..
} if *selector.as_ref() == PathSelector::Deref => {
if source_type.is_box() {
// A Box contains a Unique (transparently wrapped) pointer to the thing in the box.
// &*box means get hold of the unwrapped pointer.
// box.0 gives us a path to the Unique wrapper.
// box.0.0 gives us a path to the unwrapped pointer.
// qualifier has already been modified to be box.0.0 during path construction.
if !self.type_visitor().is_slice_pointer(target_type.kind()) {
// The unwrapped pointer is the thing to store at path.
let ptr_val = self
.bv
.lookup_path_and_refine_result(qualifier.clone(), target_type);
self.bv.update_value_at(path, ptr_val);
} else {
// The target type is a slice pointer, so the thing inside the box must be
// an array slice (or a string). In the heap model, the contents of a box
// is always stored in a heap block and the value at path box.0.0 is a
// reference to that heap block. We use that reference as the thin
// part of the target slice pointer and we assume that the length
// has been stored at path box.0.1. The latter is an artifact of the heap
// model and there is actually no such field in the Unique struct.
//
// To look up box.0.0 we need the type of the thin pointer, which we
// derive from target_type (which is known to be a slice pointer).
let deref_ty = self.type_visitor().get_dereferenced_type(target_type);
let thin_ptr_ty = self.bv.tcx.mk_ptr(rustc_middle::ty::TypeAndMut {
ty: deref_ty,
mutbl: rustc_hir::Mutability::Not,
});
let ptr_val = self
.bv
.lookup_path_and_refine_result(qualifier.clone(), thin_ptr_ty);
// Since path is the location of slice pointer, path.0 is the location of
// the thin pointer part of it.
self.bv
.update_value_at(Path::new_field(path.clone(), 0), ptr_val);
// box.0.1 is written to with the slice length when the box is initialized
// with the slice value.
if let PathEnum::QualifiedPath {
qualifier: wrapper_path,
..
} = &qualifier.value
{
let len_val = self.bv.lookup_path_and_refine_result(
Path::new_length(wrapper_path.clone()),
self.bv.tcx.types.usize,
);
// path.1 is the length part of the target slice pointer
self.bv
.update_value_at(Path::new_length(path.clone()), len_val);
} else {
assume_unreachable!("qualifier should be box.0.0 {:?}", qualifier);
}
}
return;
}
// If q does not point to a Box, then we have:
// r = *q copies the value that q points to.
// r = &*q copies q.
// The reason for this distinction seems to be that there is no need for yet another
// way to get a pointer to a copy of the thing p points to (i.e. we already have &r
// when r = *q), whereas it is useful to have a way to change the nature of p.
// For example we see things like r = &mut *p.
// Effectively, thus, we are transmuting the pointer.
let mut qualifier = qualifier;
if matches!(source_type.kind(), TyKind::Slice(..) | TyKind::Str) {
if let PathEnum::QualifiedPath {
qualifier: q,
selector,
..
} = &qualifier.value
{
if **selector == PathSelector::Field(0) {
qualifier = q;
} else {
assume_unreachable!(
"*q where q is a slice pointer should become *(q.0)"
);
}
} else {
assume_unreachable!("*q where q is a slice pointer should become *(q.0)");
}
}
let source_pointer_ty = self
.type_visitor()
.get_path_rustc_type(qualifier, self.bv.current_span);
let source_pointer_path = qualifier.canonicalize(&self.bv.current_environment);
self.bv.copy_and_transmute(
source_pointer_path,
source_pointer_ty,
path,
target_type,
);
return;
}
PathEnum::Computed { .. }
| PathEnum::Offset { .. }
| PathEnum::QualifiedPath { .. } => {
AbstractValue::make_reference(value_path.canonicalize(&self.bv.current_environment))
}
PathEnum::PromotedConstant { .. } => {
if let Some(val) = self.bv.current_environment.value_at(&value_path) {
if let Expression::HeapBlock { .. } = &val.expression {
let heap_path = Path::new_heap_block(val.clone());
AbstractValue::make_reference(heap_path)
} else {
AbstractValue::make_reference(value_path.clone())
}
} else {
AbstractValue::make_reference(value_path.clone())
}
}
PathEnum::HeapBlock { value } => value.clone(),
_ => AbstractValue::make_reference(value_path.clone()),
};
self.bv.update_value_at(path, value);
}