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, } }