in checker/src/block_visitor.rs [1987:2132]
fn visit_cast(
&mut self,
path: Rc<Path>,
cast_kind: mir::CastKind,
operand: &mir::Operand<'tcx>,
ty: rustc_middle::ty::Ty<'tcx>,
) {
match cast_kind {
mir::CastKind::Pointer(pointer_cast) => {
// The value remains unchanged, but pointers may be fat, so use copy_or_move_elements
let (is_move, place) = match operand {
mir::Operand::Copy(place) => (false, place),
mir::Operand::Move(place) => (true, place),
mir::Operand::Constant(..) => {
// Compile time constant pointers can arise from first class function values.
// Such pointers are thin.
let result = self.visit_operand(operand);
if result.is_function() {
self.bv.update_value_at(Path::new_function(path), result);
} else {
self.bv.update_value_at(path, result);
}
return;
}
};
if let PointerCast::Unsize = pointer_cast {
// Unsize a pointer/reference value, e.g., `&[T; n]` to
// `&[T]`. Note that the source could be a thin or fat pointer.
// This will do things like convert thin pointers to fat
// pointers, or convert structs containing thin pointers to
// structs containing fat pointers, or convert between fat
// pointers. We don't store the details of how the transform is
// done (in fact, we don't know that, because it might depend on
// the precise type parameters). We just store the target
// type. Codegen backends and miri figure out what has to be done
// based on the precise source/target type at hand.
//
// We do this by iterating over all paths in the environment rooted in
// source_path and upgrading thin pointers to fat pointers if the re-rooted
// path to the thin pointer infers to a type that is a slice pointer.
let value_map = self.bv.current_environment.value_map.clone();
let source_path = self.visit_lh_place(place);
if !utils::is_concrete(ty.kind()) {
let source_type = self
.type_visitor()
.get_path_rustc_type(&source_path, self.bv.current_span);
if utils::is_concrete(source_type.kind()) {
debug!("changing {:?} from {:?} to {:?}", path, ty, source_type);
self.type_visitor_mut()
.set_path_rustc_type(path.clone(), source_type);
}
}
for (p, value) in value_map
.iter()
.filter(|(p, _)| source_path == **p || p.is_rooted_by(&source_path))
{
let target_path = p
.replace_root(&source_path, path.clone())
.canonicalize(&self.bv.current_environment);
if value.expression.infer_type() != ExpressionType::ThinPointer {
// just copy
self.bv.update_value_at(target_path, value.clone());
} else {
let target_type = self
.type_visitor()
.get_path_rustc_type(&target_path, self.bv.current_span);
if self.type_visitor().is_slice_pointer(target_type.kind()) {
// convert the source thin pointer to a fat pointer if the target path is a slice pointer
let thin_pointer_path = Path::new_field(target_path.clone(), 0);
self.bv.update_value_at(thin_pointer_path, value.clone());
let source_type = self
.type_visitor()
.get_path_rustc_type(p, self.bv.current_span);
// Now write the length alongside the thin pointer
if let TyKind::Array(_, len) = self
.type_visitor()
.get_dereferenced_type(source_type)
.kind()
{
let length_path = target_path
.add_or_replace_selector(Rc::new(PathSelector::Field(1)));
let length_value = self.visit_const(len);
self.bv.update_value_at(length_path, length_value);
} else {
assume_unreachable!(
"non array thin pointer type {:?}",
source_type
);
}
} else {
// just copy
self.bv.update_value_at(target_path, value.clone());
}
}
}
return;
}
let source_path = self.visit_rh_place(place);
self.bv
.copy_or_move_elements(path, source_path, ty, is_move);
}
mir::CastKind::Misc => {
let source_type = self.get_operand_rustc_type(operand);
let mut source_value = self.visit_operand(operand);
if source_type.is_enum() {
let enum_path = Path::get_as_path(source_value);
let discr_path = Path::new_discriminant(enum_path);
source_value = self.bv.lookup_path_and_refine_result(discr_path, ty);
}
let result = source_value.cast(ExpressionType::from(ty.kind()));
if result != source_value || (source_type.is_trait() && !ty.is_trait()) {
self.type_visitor_mut()
.set_path_rustc_type(Path::get_as_path(result.clone()), ty);
}
if matches!(source_value.expression, Expression::Variable { .. })
&& self.type_visitor().is_slice_pointer(source_type.kind())
{
// If operand is a slice pointer, we can't just look it up with visit_operand
// since, if known, its value is a (thin_pointer, length) pair.
// Beats me why pointer to pointer casts are done with CastKind::Misc rather
// than CastKind::Pointer.
let mut source_path = self
.get_operand_path(operand)
.canonicalize(&self.bv.current_environment);
if !self.type_visitor().is_slice_pointer(ty.kind()) {
source_path = Path::new_field(source_path, 0);
}
if let mir::Operand::Move(..) = operand {
self.bv
.copy_or_move_elements(path, source_path.clone(), ty, true);
self.bv.current_environment.value_map =
self.bv.current_environment.value_map.remove(&source_path);
} else {
self.bv.copy_or_move_elements(path, source_path, ty, false);
}
} else {
if let mir::Operand::Move(place) = operand {
let source_path = self.visit_rh_place(place);
self.bv.current_environment.value_map =
self.bv.current_environment.value_map.remove(&source_path);
}
self.bv.update_value_at(path, result);
}
}
}
}