fn visit_cast()

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);
                }
            }
        }
    }