From 90a41050bad86608001685060bed8879426ecd38 Mon Sep 17 00:00:00 2001
From: b-naber <bn263@gmx.de>
Date: Wed, 16 Feb 2022 10:56:01 +0100
Subject: [PATCH] implement valtrees as the type-system representation for
 constant values

---
 clippy_lints/src/enum_clike.rs               |   6 +-
 clippy_lints/src/large_const_arrays.rs       |   5 +-
 clippy_lints/src/large_stack_arrays.rs       |   5 +-
 clippy_lints/src/matches/overlapping_arms.rs |   4 +-
 clippy_lints/src/non_copy_const.rs           |  12 +-
 clippy_utils/src/consts.rs                   | 116 +++++++++----------
 6 files changed, 67 insertions(+), 81 deletions(-)

diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs
index 10be245b362..5d687d06f6f 100644
--- a/clippy_lints/src/enum_clike.rs
+++ b/clippy_lints/src/enum_clike.rs
@@ -48,10 +48,10 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
                     let mut ty = cx.tcx.type_of(def_id.to_def_id());
                     let constant = cx
                         .tcx
-                        .const_eval_poly(def_id.to_def_id())
+                        .const_eval_poly_for_typeck(def_id.to_def_id())
                         .ok()
-                        .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty));
-                    if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) {
+                        .and_then(|val| val.map(|valtree| rustc_middle::ty::Const::from_value(cx.tcx, valtree, ty)));
+                    if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx.tcx, c)) {
                         if let ty::Adt(adt, _) = ty.kind() {
                             if adt.is_enum() {
                                 ty = adt.repr().discr_type().to_ty(cx.tcx);
diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs
index ed47490e230..e10993ba7dd 100644
--- a/clippy_lints/src/large_const_arrays.rs
+++ b/clippy_lints/src/large_const_arrays.rs
@@ -3,7 +3,6 @@ use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, ConstKind};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -53,8 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
             if let ItemKind::Const(hir_ty, _) = &item.kind;
             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
             if let ty::Array(element_type, cst) = ty.kind();
-            if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.kind();
-            if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
+            if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
+            if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx);
             if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes());
             if self.maximum_allowed_size < element_count * element_size;
 
diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs
index 4ca69465fad..0acbd81aec3 100644
--- a/clippy_lints/src/large_stack_arrays.rs
+++ b/clippy_lints/src/large_stack_arrays.rs
@@ -3,7 +3,6 @@ use clippy_utils::source::snippet;
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, ConstKind};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -43,8 +42,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
         if_chain! {
             if let ExprKind::Repeat(_, _) = expr.kind;
             if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind();
-            if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.kind();
-            if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
+            if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
+            if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx);
             if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes());
             if self.maximum_allowed_size < element_count * element_size;
             then {
diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs
index c0b3e95b185..afca7530556 100644
--- a/clippy_lints/src/matches/overlapping_arms.rs
+++ b/clippy_lints/src/matches/overlapping_arms.rs
@@ -34,11 +34,11 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
                     let lhs_const = match lhs {
                         Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
-                        None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
+                        None => miri_to_const(cx.tcx, ty.numeric_min_val(cx.tcx)?)?,
                     };
                     let rhs_const = match rhs {
                         Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
-                        None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
+                        None => miri_to_const(cx.tcx, ty.numeric_max_val(cx.tcx)?)?,
                     };
                     let lhs_val = lhs_const.int_value(cx, ty)?;
                     let rhs_val = rhs_const.int_value(cx, ty)?;
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index 8db41ba6ee2..9f6fca27b22 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -13,7 +13,7 @@ use rustc_hir::{
     BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass, Lint};
-use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
+use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, Const, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -133,7 +133,7 @@ fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 
 fn is_value_unfrozen_raw<'tcx>(
     cx: &LateContext<'tcx>,
-    result: Result<ConstValue<'tcx>, ErrorHandled>,
+    result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
     ty: Ty<'tcx>,
 ) -> bool {
     fn inner<'tcx>(cx: &LateContext<'tcx>, val: Const<'tcx>) -> bool {
@@ -142,7 +142,7 @@ fn is_value_unfrozen_raw<'tcx>(
             // leads us to the point checking `UnsafeCell` directly is the only option.
             ty::Adt(ty_def, ..) if Some(ty_def.did()) == cx.tcx.lang_items().unsafe_cell_type() => true,
             ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => {
-                let val = cx.tcx.destructure_const(cx.param_env.and(val));
+                let val = cx.tcx.destructure_const(val);
                 val.fields.iter().any(|field| inner(cx, *field))
             },
             _ => false,
@@ -174,19 +174,19 @@ fn is_value_unfrozen_raw<'tcx>(
             // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
             err == ErrorHandled::TooGeneric
         },
-        |val| inner(cx, Const::from_value(cx.tcx, val, ty)),
+        |val| val.map_or(false, |val| inner(cx, Const::from_value(cx.tcx, val, ty))),
     )
 }
 
 fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
-    let result = cx.tcx.const_eval_poly(body_id.hir_id.owner.to_def_id());
+    let result = cx.tcx.const_eval_poly_for_typeck(body_id.hir_id.owner.to_def_id());
     is_value_unfrozen_raw(cx, result, ty)
 }
 
 fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
     let substs = cx.typeck_results().node_substs(hir_id);
 
-    let result = cx.tcx.const_eval_resolve(
+    let result = cx.tcx.const_eval_resolve_for_typeck(
         cx.param_env,
         ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs),
         None,
diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs
index 159c5d53d02..c31c560f427 100644
--- a/clippy_utils/src/consts.rs
+++ b/clippy_utils/src/consts.rs
@@ -7,7 +7,6 @@ use rustc_data_structures::sync::Lrc;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
 use rustc_lint::LateContext;
-use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::ty::subst::{Subst, SubstsRef};
 use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
@@ -423,14 +422,14 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                 let result = self
                     .lcx
                     .tcx
-                    .const_eval_resolve(
+                    .const_eval_resolve_for_typeck(
                         self.param_env,
                         ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs),
                         None,
                     )
                     .ok()
-                    .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
-                let result = miri_to_const(result);
+                    .and_then(|val| val.map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty)))?;
+                let result = miri_to_const(self.lcx.tcx, result);
                 if result.is_some() {
                     self.needed_resolution = true;
                 }
@@ -580,80 +579,69 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
     }
 }
 
-pub fn miri_to_const(result: ty::Const<'_>) -> Option<Constant> {
-    use rustc_middle::mir::interpret::ConstValue;
+pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: ty::Const<'tcx>) -> Option<Constant> {
     match result.kind() {
-        ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
-            match result.ty().kind() {
-                ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
-                ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
-                ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
+        ty::ConstKind::Value(valtree) => {
+            match (valtree, result.ty().kind()) {
+                (ty::ValTree::Leaf(int), ty::Bool) => Some(Constant::Bool(int == ScalarInt::TRUE)),
+                (ty::ValTree::Leaf(int), ty::Uint(_) | ty::Int(_)) => Some(Constant::Int(int.assert_bits(int.size()))),
+                (ty::ValTree::Leaf(int), ty::Float(FloatTy::F32)) => Some(Constant::F32(f32::from_bits(
                     int.try_into().expect("invalid f32 bit representation"),
                 ))),
-                ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
+                (ty::ValTree::Leaf(int), ty::Float(FloatTy::F64)) => Some(Constant::F64(f64::from_bits(
                     int.try_into().expect("invalid f64 bit representation"),
                 ))),
-                ty::RawPtr(type_and_mut) => {
+                (ty::ValTree::Leaf(int), ty::RawPtr(type_and_mut)) => {
                     if let ty::Uint(_) = type_and_mut.ty.kind() {
                         return Some(Constant::RawPtr(int.assert_bits(int.size())));
                     }
                     None
                 },
+                (ty::ValTree::Branch(_), ty::Ref(_, inner_ty, _)) if *inner_ty == tcx.types.str_ => valtree
+                    .try_to_raw_bytes(tcx, result.ty())
+                    .and_then(|bytes| String::from_utf8(bytes.to_owned()).ok().map(Constant::Str)),
+                (ty::ValTree::Branch(_), ty::Array(arr_ty, len)) => match arr_ty.kind() {
+                    ty::Float(float_ty) => {
+                        let chunk_size = match float_ty {
+                            FloatTy::F32 => 4,
+                            FloatTy::F64 => 8,
+                        };
+
+                        match miri_to_const(tcx, *len) {
+                            Some(Constant::Int(_)) => valtree.try_to_raw_bytes(tcx, result.ty()).and_then(|bytes| {
+                                bytes
+                                    .to_owned()
+                                    .chunks(chunk_size)
+                                    .map(|chunk| match float_ty {
+                                        FloatTy::F32 => {
+                                            let float = f32::from_le_bytes(
+                                                chunk
+                                                    .try_into()
+                                                    .expect(&format!("expected to construct f32 from {:?}", chunk)),
+                                            );
+                                            Some(Constant::F32(float))
+                                        },
+                                        FloatTy::F64 => {
+                                            let float = f64::from_le_bytes(
+                                                chunk
+                                                    .try_into()
+                                                    .expect(&format!("expected to construct f64 from {:?}", chunk)),
+                                            );
+                                            Some(Constant::F64(float))
+                                        },
+                                    })
+                                    .collect::<Option<Vec<Constant>>>()
+                                    .map(Constant::Vec)
+                            }),
+                            _ => None,
+                        }
+                    },
+                    _ => None,
+                },
                 // FIXME: implement other conversions.
                 _ => None,
             }
         },
-        ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty().kind() {
-            ty::Ref(_, tam, _) => match tam.kind() {
-                ty::Str => String::from_utf8(
-                    data.inner()
-                        .inspect_with_uninit_and_ptr_outside_interpreter(start..end)
-                        .to_owned(),
-                )
-                .ok()
-                .map(Constant::Str),
-                _ => None,
-            },
-            _ => None,
-        },
-        ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty().kind() {
-            ty::Array(sub_type, len) => match sub_type.kind() {
-                ty::Float(FloatTy::F32) => match miri_to_const(*len) {
-                    Some(Constant::Int(len)) => alloc
-                        .inner()
-                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
-                        .to_owned()
-                        .chunks(4)
-                        .map(|chunk| {
-                            Some(Constant::F32(f32::from_le_bytes(
-                                chunk.try_into().expect("this shouldn't happen"),
-                            )))
-                        })
-                        .collect::<Option<Vec<Constant>>>()
-                        .map(Constant::Vec),
-                    _ => None,
-                },
-                ty::Float(FloatTy::F64) => match miri_to_const(*len) {
-                    Some(Constant::Int(len)) => alloc
-                        .inner()
-                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
-                        .to_owned()
-                        .chunks(8)
-                        .map(|chunk| {
-                            Some(Constant::F64(f64::from_le_bytes(
-                                chunk.try_into().expect("this shouldn't happen"),
-                            )))
-                        })
-                        .collect::<Option<Vec<Constant>>>()
-                        .map(Constant::Vec),
-                    _ => None,
-                },
-                // FIXME: implement other array type conversions.
-                _ => None,
-            },
-            _ => None,
-        },
-        // FIXME: implement other conversions.
         _ => None,
     }
 }