From 96b36d6eb21daaca2c2d04fd8d7c27bef5eb90c6 Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 12 Apr 2022 18:14:28 +0200 Subject: [PATCH] use GlobalId in eval_to_valtree query and introduce query for valtree_to_const_val --- .../src/const_eval/eval_queries.rs | 2 +- .../rustc_const_eval/src/const_eval/mod.rs | 130 +++++++++++++++- .../src/const_eval/valtrees.rs | 143 ++++++++++-------- .../rustc_const_eval/src/interpret/place.rs | 20 ++- compiler/rustc_const_eval/src/lib.rs | 22 ++- .../rustc_middle/src/mir/interpret/error.rs | 3 +- .../rustc_middle/src/mir/interpret/mod.rs | 6 +- .../rustc_middle/src/mir/interpret/queries.rs | 13 +- compiler/rustc_middle/src/mir/mod.rs | 54 ++++++- compiler/rustc_middle/src/mir/query.rs | 11 +- compiler/rustc_middle/src/query/mod.rs | 35 +++-- compiler/rustc_middle/src/thir.rs | 6 +- compiler/rustc_middle/src/ty/query.rs | 6 +- compiler/rustc_middle/src/ty/util.rs | 12 +- .../src/build/expr/as_constant.rs | 57 +------ .../rustc_mir_build/src/build/matches/mod.rs | 4 +- .../src/build/matches/simplify.rs | 4 +- .../rustc_mir_build/src/build/matches/test.rs | 12 +- compiler/rustc_mir_build/src/build/misc.rs | 7 +- compiler/rustc_mir_build/src/build/mod.rs | 56 +++++++ compiler/rustc_mir_build/src/lib.rs | 1 + compiler/rustc_mir_build/src/thir/cx/mod.rs | 23 ++- .../src/thir/pattern/const_to_pat.rs | 31 ++-- .../src/thir/pattern/deconstruct_pat.rs | 42 +++-- .../rustc_mir_build/src/thir/pattern/mod.rs | 66 ++++---- .../inline-const/const-match-pat-generic.rs | 3 +- .../const-match-pat-generic.stderr | 15 +- 27 files changed, 540 insertions(+), 244 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5c56e6ee5f5b7..d7cbc48e03281 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -135,7 +135,7 @@ pub(super) fn op_to_const<'tcx>( } else { // It is guaranteed that any non-slice scalar pair is actually ByRef here. // When we come back from raw const eval, we are always by-ref. The only way our op here is - // by-val is if we are in destructure_const, i.e., if this is (a field of) something that we + // by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or // structs containing such. op.try_as_mplace() diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 96c18d488ee8c..db43f7a425ce6 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -4,6 +4,7 @@ use std::convert::TryFrom; use rustc_hir::Mutability; use rustc_middle::mir; +use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; @@ -22,7 +23,7 @@ pub use error::*; pub use eval_queries::*; pub use fn_queries::*; pub use machine::*; -pub(crate) use valtrees::{const_to_valtree, valtree_to_const_value}; +pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value}; pub(crate) fn const_caller_location( tcx: TyCtxt<'_>, @@ -38,6 +39,57 @@ pub(crate) fn const_caller_location( ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr, &tcx)) } +// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. +const VALTREE_MAX_NODES: usize = 1000; + +pub(crate) enum ValTreeCreationError { + NodesOverflow, + NonSupportedType, + Other, +} +pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError>; + +/// Evaluates a constant and turns it into a type-level constant value. +pub(crate) fn eval_to_valtree<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + cid: GlobalId<'tcx>, +) -> EvalToValTreeResult<'tcx> { + let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?; + let ecx = mk_eval_cx( + tcx, DUMMY_SP, param_env, + // It is absolutely crucial for soundness that + // we do not read from static items or other mutable memory. + false, + ); + let place = ecx.raw_const_to_mplace(const_alloc).unwrap(); + debug!(?place); + + let mut num_nodes = 0; + let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes); + + match valtree_result { + Ok(valtree) => Ok(Some(valtree)), + Err(err) => { + let did = cid.instance.def_id(); + let s = cid.display(tcx); + match err { + ValTreeCreationError::NodesOverflow => { + let msg = format!("maximum number of nodes exceeded in constant {}", &s); + let mut diag = match tcx.hir().span_if_local(did) { + Some(span) => tcx.sess.struct_span_err(span, &msg), + None => tcx.sess.struct_err(&msg), + }; + diag.emit(); + + Ok(None) + } + ValTreeCreationError::NonSupportedType | ValTreeCreationError::Other => Ok(None), + } + } + } +} + /// This function should never fail for validated constants. However, it is also invoked from the /// pretty printer which might attempt to format invalid constants and in that case it might fail. pub(crate) fn try_destructure_const<'tcx>( @@ -48,7 +100,6 @@ pub(crate) fn try_destructure_const<'tcx>( trace!("destructure_const: {:?}", val); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); let op = ecx.const_to_op(val, None)?; - // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match val.ty().kind() { ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op), @@ -64,7 +115,6 @@ pub(crate) fn try_destructure_const<'tcx>( ty::Tuple(substs) => (substs.len(), None, op), _ => bug!("cannot destructure constant {:?}", val), }; - let fields = (0..field_count) .map(|i| { let field_op = ecx.operand_field(&down, i)?; @@ -73,10 +123,46 @@ pub(crate) fn try_destructure_const<'tcx>( }) .collect::>>()?; let fields = tcx.arena.alloc_from_iter(fields); - Ok(mir::DestructuredConst { variant, fields }) } +#[instrument(skip(tcx), level = "debug")] +pub(crate) fn try_destructure_mir_constant<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + val: mir::ConstantKind<'tcx>, +) -> InterpResult<'tcx, mir::DestructuredMirConstant<'tcx>> { + trace!("destructure_mir_constant: {:?}", val); + let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); + let op = ecx.mir_const_to_op(&val, None)?; + + // We go to `usize` as we cannot allocate anything bigger anyway. + let (field_count, variant, down) = match val.ty().kind() { + ty::Array(_, len) => (len.eval_usize(tcx, param_env) as usize, None, op), + ty::Adt(def, _) if def.variants().is_empty() => { + throw_ub!(Unreachable) + } + ty::Adt(def, _) => { + let variant = ecx.read_discriminant(&op).unwrap().1; + let down = ecx.operand_downcast(&op, variant).unwrap(); + (def.variants()[variant].fields.len(), Some(variant), down) + } + ty::Tuple(substs) => (substs.len(), None, op), + _ => bug!("cannot destructure mir constant {:?}", val), + }; + + let fields_iter = (0..field_count) + .map(|i| { + let field_op = ecx.operand_field(&down, i)?; + let val = op_to_const(&ecx, &field_op); + Ok(mir::ConstantKind::Val(val, field_op.layout.ty)) + }) + .collect::>>()?; + let fields = tcx.arena.alloc_from_iter(fields_iter); + + Ok(mir::DestructuredMirConstant { variant, fields }) +} + #[instrument(skip(tcx), level = "debug")] pub(crate) fn deref_const<'tcx>( tcx: TyCtxt<'tcx>, @@ -113,3 +199,39 @@ pub(crate) fn deref_const<'tcx>( tcx.mk_const(ty::ConstS { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty }) } + +#[instrument(skip(tcx), level = "debug")] +pub(crate) fn deref_mir_constant<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + val: mir::ConstantKind<'tcx>, +) -> mir::ConstantKind<'tcx> { + let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); + let op = ecx.mir_const_to_op(&val, None).unwrap(); + let mplace = ecx.deref_operand(&op).unwrap(); + if let Some(alloc_id) = mplace.ptr.provenance { + assert_eq!( + tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().0.0.mutability, + Mutability::Not, + "deref_const cannot be used with mutable allocations as \ + that could allow pattern matching to observe mutable statics", + ); + } + + let ty = match mplace.meta { + MemPlaceMeta::None => mplace.layout.ty, + MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace), + // In case of unsized types, figure out the real type behind. + MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() { + ty::Str => bug!("there's no sized equivalent of a `str`"), + ty::Slice(elem_ty) => tcx.mk_array(*elem_ty, scalar.to_machine_usize(&tcx).unwrap()), + _ => bug!( + "type {} should not have metadata, but had {:?}", + mplace.layout.ty, + mplace.meta + ), + }, + }; + + mir::ConstantKind::Val(op_to_const(&ecx, &mplace.into()), ty) +} diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index f57f25c19f90f..7346d69bb5de3 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,33 +1,16 @@ use super::eval_queries::{mk_eval_cx, op_to_const}; use super::machine::CompileTimeEvalContext; +use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, MemoryKind, PlaceTy, Scalar, ScalarMaybeUninit, }; -use rustc_middle::mir::interpret::ConstAlloc; -use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_span::source_map::DUMMY_SP; use rustc_target::abi::{Align, VariantIdx}; use crate::interpret::MPlaceTy; use crate::interpret::Value; - -/// Convert an evaluated constant to a type level constant -#[instrument(skip(tcx), level = "debug")] -pub(crate) fn const_to_valtree<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - raw: ConstAlloc<'tcx>, -) -> Option> { - let ecx = mk_eval_cx( - tcx, DUMMY_SP, param_env, - // It is absolutely crucial for soundness that - // we do not read from static items or other mutable memory. - false, - ); - let place = ecx.raw_const_to_mplace(raw).unwrap(); - const_to_valtree_inner(&ecx, &place) -} +use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; #[instrument(skip(ecx), level = "debug")] fn branches<'tcx>( @@ -35,7 +18,8 @@ fn branches<'tcx>( place: &MPlaceTy<'tcx>, n: usize, variant: Option, -) -> Option> { + num_nodes: &mut usize, +) -> ValTreeCreationResult<'tcx> { let place = match variant { Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(), None => *place, @@ -43,82 +27,116 @@ fn branches<'tcx>( let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32())))); debug!(?place, ?variant); - let fields = (0..n).map(|i| { + let mut fields = Vec::with_capacity(n); + for i in 0..n { let field = ecx.mplace_field(&place, i).unwrap(); - const_to_valtree_inner(ecx, &field) - }); - // For enums, we preped their variant index before the variant's fields so we can figure out + let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; + fields.push(Some(valtree)); + } + + // For enums, we prepend their variant index before the variant's fields so we can figure out // the variant again when just seeing a valtree. - let branches = variant.into_iter().chain(fields); - Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::>>()?))) + let branches = variant + .into_iter() + .chain(fields.into_iter()) + .collect::>>() + .expect("should have already checked for errors in ValTree creation"); + + // Have to account for ZSTs here + if branches.len() == 0 { + *num_nodes += 1; + } + + Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches))) } #[instrument(skip(ecx), level = "debug")] fn slice_branches<'tcx>( ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>, -) -> Option> { + num_nodes: &mut usize, +) -> ValTreeCreationResult<'tcx> { let n = place .len(&ecx.tcx.tcx) .unwrap_or_else(|_| panic!("expected to use len of place {:?}", place)); - let branches = (0..n).map(|i| { + + let mut elems = Vec::with_capacity(n as usize); + for i in 0..n { let place_elem = ecx.mplace_index(place, i).unwrap(); - const_to_valtree_inner(ecx, &place_elem) - }); + let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?; + elems.push(valtree); + } - Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::>>()?))) + Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(elems))) } #[instrument(skip(ecx), level = "debug")] -fn const_to_valtree_inner<'tcx>( +pub(crate) fn const_to_valtree_inner<'tcx>( ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>, -) -> Option> { - match place.layout.ty.kind() { - ty::FnDef(..) => Some(ty::ValTree::zst()), + num_nodes: &mut usize, +) -> ValTreeCreationResult<'tcx> { + if *num_nodes >= VALTREE_MAX_NODES { + return Err(ValTreeCreationError::NodesOverflow); + } + + let ty = place.layout.ty; + debug!("ty kind: {:?}", ty.kind()); + + match ty.kind() { + ty::FnDef(..) => { + *num_nodes += 1; + Ok(ty::ValTree::zst()) + } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { - let val = ecx.read_immediate(&place.into()).unwrap(); + let Ok(val) = ecx.read_immediate(&place.into()) else { + return Err(ValTreeCreationError::Other); + }; let val = val.to_scalar().unwrap(); - Some(ty::ValTree::Leaf(val.assert_int())) + *num_nodes += 1; + + Ok(ty::ValTree::Leaf(val.assert_int())) } // Raw pointers are not allowed in type level constants, as we cannot properly test them for // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`). // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to // agree with runtime equality tests. - ty::FnPtr(_) | ty::RawPtr(_) => None, + ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType), ty::Ref(_, _, _) => { - let derefd_place = ecx.deref_operand(&place.into()).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e)); + let Ok(derefd_place)= ecx.deref_operand(&place.into()) else { + return Err(ValTreeCreationError::Other); + }; debug!(?derefd_place); - const_to_valtree_inner(ecx, &derefd_place) + const_to_valtree_inner(ecx, &derefd_place, num_nodes) } ty::Str | ty::Slice(_) | ty::Array(_, _) => { - let valtree = slice_branches(ecx, place); - debug!(?valtree); - - valtree + slice_branches(ecx, place, num_nodes) } // Trait objects are not allowed in type level constants, as we have no concept for // resolving their backing type, even if we can do that at const eval time. We may // hypothetically be able to allow `dyn StructuralEq` trait objects in the future, // but it is unclear if this is useful. - ty::Dynamic(..) => None, + ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType), - ty::Tuple(substs) => branches(ecx, place, substs.len(), None), + ty::Tuple(elem_tys) => { + branches(ecx, place, elem_tys.len(), None, num_nodes) + } ty::Adt(def, _) => { if def.is_union() { - return None + return Err(ValTreeCreationError::NonSupportedType); } else if def.variants().is_empty() { bug!("uninhabited types should have errored and never gotten converted to valtree") } - let variant = ecx.read_discriminant(&place.into()).unwrap().1; - - branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant)) + let Ok((_, variant)) = ecx.read_discriminant(&place.into()) else { + return Err(ValTreeCreationError::Other); + }; + branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) } ty::Never @@ -136,7 +154,7 @@ fn const_to_valtree_inner<'tcx>( // FIXME(oli-obk): we can probably encode closures just like structs | ty::Closure(..) | ty::Generator(..) - | ty::GeneratorWitness(..) => None, + | ty::GeneratorWitness(..) => Err(ValTreeCreationError::NonSupportedType), } } @@ -225,8 +243,11 @@ fn create_pointee_place<'tcx>( .unwrap(); debug!(?ptr); - let mut place = MPlaceTy::from_aligned_ptr(ptr.into(), layout); - place.meta = MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64)); + let place = MPlaceTy::from_aligned_ptr_with_meta( + ptr.into(), + layout, + MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64)), + ); debug!(?place); place @@ -237,11 +258,11 @@ fn create_pointee_place<'tcx>( /// Converts a `ValTree` to a `ConstValue`, which is needed after mir /// construction has finished. -// FIXME Merge `valtree_to_const_value` and `fill_place_recursively` into one function +// FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function #[instrument(skip(tcx), level = "debug")] pub fn valtree_to_const_value<'tcx>( tcx: TyCtxt<'tcx>, - param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + ty: Ty<'tcx>, valtree: ty::ValTree<'tcx>, ) -> ConstValue<'tcx> { // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s @@ -251,8 +272,8 @@ pub fn valtree_to_const_value<'tcx>( // create inner `MPlace`s which are filled recursively. // FIXME Does this need an example? - let (param_env, ty) = param_env_ty.into_parts(); - let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); + let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::empty(), false); + let param_env_ty = ty::ParamEnv::empty().and(ty); match ty.kind() { ty::FnDef(..) => { @@ -275,7 +296,7 @@ pub fn valtree_to_const_value<'tcx>( }; debug!(?place); - fill_place_recursively(&mut ecx, &mut place, valtree); + valtree_into_mplace(&mut ecx, &mut place, valtree); dump_place(&ecx, place.into()); intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); @@ -317,7 +338,7 @@ pub fn valtree_to_const_value<'tcx>( // FIXME Needs a better/correct name #[instrument(skip(ecx), level = "debug")] -fn fill_place_recursively<'tcx>( +fn valtree_into_mplace<'tcx>( ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, place: &mut MPlaceTy<'tcx>, valtree: ty::ValTree<'tcx>, @@ -349,7 +370,7 @@ fn fill_place_recursively<'tcx>( let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree); debug!(?pointee_place); - fill_place_recursively(ecx, &mut pointee_place, valtree); + valtree_into_mplace(ecx, &mut pointee_place, valtree); dump_place(ecx, pointee_place.into()); intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap(); @@ -437,7 +458,7 @@ fn fill_place_recursively<'tcx>( }; debug!(?place_inner); - fill_place_recursively(ecx, &mut place_inner, *inner_valtree); + valtree_into_mplace(ecx, &mut place_inner, *inner_valtree); dump_place(&ecx, place_inner.into()); } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 95d6f431391fc..62f9c8f990d70 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -115,12 +115,6 @@ impl<'tcx, Tag: Provenance> std::ops::Deref for MPlaceTy<'tcx, Tag> { } } -impl<'tcx, Tag: Provenance> std::ops::DerefMut for MPlaceTy<'tcx, Tag> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.mplace - } -} - impl<'tcx, Tag: Provenance> From> for PlaceTy<'tcx, Tag> { #[inline(always)] fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { @@ -196,6 +190,18 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout } } + #[inline] + pub fn from_aligned_ptr_with_meta( + ptr: Pointer>, + layout: TyAndLayout<'tcx>, + meta: MemPlaceMeta, + ) -> Self { + let mut mplace = MemPlace::from_ptr(ptr, layout.align.abi); + mplace.meta = meta; + + MPlaceTy { mplace, layout } + } + #[inline] pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { if self.layout.is_unsized() { @@ -495,7 +501,7 @@ where /// Project into an mplace #[instrument(skip(self), level = "debug")] - pub(crate) fn mplace_projection( + pub(super) fn mplace_projection( &self, base: &MPlaceTy<'tcx, M::PointerTag>, proj_elem: mir::PlaceElem<'tcx>, diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1ab461a942129..8b6689ca213ab 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -34,26 +34,32 @@ pub mod transform; pub mod util; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::ParamEnv; pub fn provide(providers: &mut Providers) { const_eval::provide(providers); providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; providers.const_caller_location = const_eval::const_caller_location; - providers.try_destructure_const = |tcx, param_env_and_value| { - let (param_env, value) = param_env_and_value.into_parts(); - const_eval::try_destructure_const(tcx, param_env, value).ok() + providers.try_destructure_const = |tcx, param_env_and_val| { + let (param_env, c) = param_env_and_val.into_parts(); + const_eval::try_destructure_const(tcx, param_env, c).ok() }; - providers.const_to_valtree = |tcx, param_env_and_value| { + providers.eval_to_valtree = |tcx, param_env_and_value| { let (param_env, raw) = param_env_and_value.into_parts(); - const_eval::const_to_valtree(tcx, param_env, raw) + const_eval::eval_to_valtree(tcx, param_env, raw) }; - providers.valtree_to_const_val = |tcx, (ty, valtree)| { - const_eval::valtree_to_const_value(tcx, ParamEnv::empty().and(ty), valtree) + providers.try_destructure_mir_constant = |tcx, param_env_and_value| { + let (param_env, value) = param_env_and_value.into_parts(); + const_eval::try_destructure_mir_constant(tcx, param_env, value).ok() }; + providers.valtree_to_const_val = + |tcx, (ty, valtree)| const_eval::valtree_to_const_value(tcx, ty, valtree); providers.deref_const = |tcx, param_env_and_value| { let (param_env, value) = param_env_and_value.into_parts(); const_eval::deref_const(tcx, param_env, value) }; + providers.deref_mir_constant = |tcx, param_env_and_value| { + let (param_env, value) = param_env_and_value.into_parts(); + const_eval::deref_mir_constant(tcx, param_env, value) + }; } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 9afe9523fcab0..bb6b10149ab49 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -1,7 +1,7 @@ use super::{AllocId, ConstAlloc, Pointer, Scalar}; use crate::mir::interpret::ConstValue; -use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty}; +use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree}; use rustc_data_structures::sync::Lock; use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed}; @@ -35,6 +35,7 @@ TrivialTypeFoldableAndLiftImpls! { pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; +pub type EvalToValTreeResult<'tcx> = Result>, ErrorHandled>; pub fn struct_error<'tcx>( tcx: TyCtxtAt<'tcx>, diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index d8cba39c6d97b..0fa4b10399a4b 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -119,9 +119,9 @@ use crate::ty::{self, Instance, Ty, TyCtxt}; pub use self::error::{ struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, - InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, - ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess, - UnsupportedOpInfo, + EvalToValTreeResult, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, + MachineStopType, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, + UninitBytesAccess, UnsupportedOpInfo, }; pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar, ScalarMaybeUninit}; diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 7e5989b4112cf..5fb8e911124d2 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -110,11 +110,22 @@ impl<'tcx> TyCtxt<'tcx> { Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory()) } - /// Destructure a constant ADT or array into its variant index and its field values. + /// Destructure a type-level constant ADT or array into its variant index and its field values. + /// Panics if the destructuring fails, use `try_destructure_const` for fallible version. pub fn destructure_const( self, param_env_and_val: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>, ) -> mir::DestructuredConst<'tcx> { self.try_destructure_const(param_env_and_val).unwrap() } + + /// Destructure a mir constant ADT or array into its variant index and its field values. + /// Panics if the destructuring fails, use `try_destructure_const` for fallible version. + pub fn destructure_mir_constant( + self, + param_env: ty::ParamEnv<'tcx>, + constant: mir::ConstantKind<'tcx>, + ) -> mir::DestructuredMirConstant<'tcx> { + self.try_destructure_mir_constant(param_env.and(constant)).unwrap() + } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index af18adac2ff79..e6304b820949b 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -3,7 +3,7 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html use crate::mir::coverage::{CodeRegion, CoverageKind}; -use crate::mir::interpret::{ConstAllocation, ConstValue, GlobalAlloc, Scalar}; +use crate::mir::interpret::{ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar}; use crate::mir::visit::MirVisitable; use crate::ty::adjustment::PointerCast; use crate::ty::codec::{TyDecoder, TyEncoder}; @@ -3078,6 +3078,58 @@ impl<'tcx> ConstantKind<'tcx> { Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id), param_env) } + #[instrument(skip(tcx), level = "debug")] + pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let body_id = match tcx.hir().get(hir_id) { + hir::Node::AnonConst(ac) => ac.body, + _ => span_bug!( + tcx.def_span(def_id.to_def_id()), + "from_inline_const can only process anonymous constants" + ), + }; + let expr = &tcx.hir().body(body_id).value; + let ty = tcx.typeck(def_id).node_type(hir_id); + + let lit_input = match expr.kind { + hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), + hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind { + hir::ExprKind::Lit(ref lit) => { + Some(LitToConstInput { lit: &lit.node, ty, neg: true }) + } + _ => None, + }, + _ => None, + }; + if let Some(lit_input) = lit_input { + // If an error occurred, ignore that it's a literal and leave reporting the error up to + // mir. + match tcx.at(expr.span).lit_to_mir_constant(lit_input) { + Ok(c) => return c, + Err(_) => {} + } + } + + let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()); + let parent_substs = + tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id)); + let substs = + ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty }) + .substs; + let uneval_const = tcx.mk_const(ty::ConstS { + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def: ty::WithOptConstParam::unknown(def_id).to_global(), + substs, + promoted: None, + }), + ty, + }); + debug!(?uneval_const); + debug_assert!(!uneval_const.has_free_regions()); + + Self::Ty(uneval_const) + } + #[instrument(skip(tcx), level = "debug")] fn from_opt_const_arg_anon_const( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 4d4eed179ca9d..7f7b8bdfc1461 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -1,6 +1,6 @@ //! Values computed by queries that use MIR. -use crate::mir::{Body, Promoted}; +use crate::mir::{self, Body, Promoted}; use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt}; use rustc_data_structures::stable_map::FxHashMap; use rustc_data_structures::vec_map::VecMap; @@ -413,13 +413,20 @@ pub enum ClosureOutlivesSubject<'tcx> { Region(ty::RegionVid), } -/// The constituent parts of an ADT or array. +/// The constituent parts of a type level constant of kind ADT or array. #[derive(Copy, Clone, Debug, HashStable)] pub struct DestructuredConst<'tcx> { pub variant: Option, pub fields: &'tcx [ty::Const<'tcx>], } +/// The constituent parts of a mir constant of kind ADT or array. +#[derive(Copy, Clone, Debug, HashStable)] +pub struct DestructuredMirConstant<'tcx> { + pub variant: Option, + pub fields: &'tcx [mir::ConstantKind<'tcx>], +} + /// Coverage information summarized from a MIR if instrumented for source code coverage (see /// compiler option `-Cinstrument-coverage`). This information is generated by the /// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query. diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 6d7ec247d0452..c2b974f85ccf4 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -946,12 +946,12 @@ rustc_queries! { cache_on_disk_if { true } } - /// Convert an evaluated constant to a type level constant or + /// Evaluate a constant and convert it to a type level constant or /// return `None` if that is not possible. - query const_to_valtree( - key: ty::ParamEnvAnd<'tcx, ConstAlloc<'tcx>> - ) -> Option> { - desc { "destructure constant" } + query eval_to_valtree( + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>> + ) -> EvalToValTreeResult<'tcx> { + desc { "evaluate type-level constant" } remap_env_constness } @@ -964,10 +964,14 @@ rustc_queries! { /// field values or return `None` if constant is invalid. /// /// Use infallible `TyCtxt::destructure_const` when you know that constant is valid. - query try_destructure_const( - key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>> - ) -> Option> { - desc { "destructure constant" } + query try_destructure_const(key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>) -> Option> { + desc { "destructure type level constant"} + } + + /// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index + /// and its field values. + query try_destructure_mir_constant(key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>) -> Option> { + desc { "destructure mir constant"} remap_env_constness } @@ -980,6 +984,15 @@ rustc_queries! { remap_env_constness } + /// Dereference a constant reference or raw pointer and turn the result into a constant + /// again. + query deref_mir_constant( + key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>> + ) -> mir::ConstantKind<'tcx> { + desc { "deref constant" } + remap_env_constness + } + query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { desc { "get a &core::panic::Location referring to a span" } } @@ -991,6 +1004,10 @@ rustc_queries! { desc { "converting literal to const" } } + query lit_to_mir_constant(key: LitToConstInput<'tcx>) -> Result, LitToConstError> { + desc { "converting literal to mir constant" } + } + query check_match(key: DefId) { desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index fdf5ecfdaf7ef..26e070af76406 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -662,7 +662,7 @@ pub enum PatKind<'tcx> { /// * Opaque constants, that must not be matched structurally. So anything that does not derive /// `PartialEq` and `Eq`. Constant { - value: ty::Const<'tcx>, + value: mir::ConstantKind<'tcx>, }, Range(PatRange<'tcx>), @@ -692,8 +692,8 @@ pub enum PatKind<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, HashStable)] pub struct PatRange<'tcx> { - pub lo: ty::Const<'tcx>, - pub hi: ty::Const<'tcx>, + pub lo: mir::ConstantKind<'tcx>, + pub hi: mir::ConstantKind<'tcx>, pub end: RangeEnd, } diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index fb937ded65af1..0398a83e22020 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -13,8 +13,10 @@ use crate::middle::resolve_lifetime::{ use crate::middle::stability::{self, DeprecationEntry}; use crate::mir; use crate::mir::interpret::GlobalId; -use crate::mir::interpret::{ConstAlloc, LitToConstError, LitToConstInput}; -use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult}; +use crate::mir::interpret::{ + ConstValue, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, +}; +use crate::mir::interpret::{LitToConstError, LitToConstInput}; use crate::mir::mono::CodegenUnit; use crate::thir; use crate::traits::query::{ diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 10520670069b4..cd099279fc78f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -6,7 +6,7 @@ use crate::ty::layout::IntegerExt; use crate::ty::query::TyCtxtAt; use crate::ty::subst::{GenericArgKind, Subst, SubstsRef}; use crate::ty::{ - self, Const, DebruijnIndex, DefIdTree, EarlyBinder, List, ReEarlyBound, Ty, TyCtxt, TyKind::*, + self, DebruijnIndex, DefIdTree, EarlyBinder, List, ReEarlyBound, Ty, TyCtxt, TyKind::*, TypeFoldable, }; use rustc_apfloat::Float as _; @@ -689,7 +689,7 @@ impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> { impl<'tcx> Ty<'tcx> { /// Returns the maximum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option> { + pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option> { let val = match self.kind() { ty::Int(_) | ty::Uint(_) => { let (size, signed) = int_size_and_signed(tcx, self); @@ -704,12 +704,13 @@ impl<'tcx> Ty<'tcx> { }), _ => None, }; - val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) + + val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) } /// Returns the minimum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option> { + pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option> { let val = match self.kind() { ty::Int(_) | ty::Uint(_) => { let (size, signed) = int_size_and_signed(tcx, self); @@ -723,7 +724,8 @@ impl<'tcx> Ty<'tcx> { }), _ => None, }; - val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) + + val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) } /// Checks whether values of this type `T` are *moved* or *copied* diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 3a6e59db90b91..25ba5d570b83b 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -1,16 +1,12 @@ //! See docs in build/expr/mod.rs -use crate::build::Builder; -use crate::thir::constant::parse_float; -use rustc_ast as ast; +use crate::build::{lit_to_mir_constant, Builder}; use rustc_hir::def_id::DefId; -use rustc_middle::mir::interpret::Allocation; use rustc_middle::mir::interpret::{ConstValue, LitToConstError, LitToConstInput, Scalar}; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt}; -use rustc_target::abi::Size; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, yielding a compile-time constant. Assumes that @@ -88,54 +84,3 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } - -#[instrument(skip(tcx, lit_input))] -fn lit_to_mir_constant<'tcx>( - tcx: TyCtxt<'tcx>, - lit_input: LitToConstInput<'tcx>, -) -> Result, LitToConstError> { - let LitToConstInput { lit, ty, neg } = lit_input; - let trunc = |n| { - let param_ty = ty::ParamEnv::reveal_all().and(ty); - let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size; - trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); - let result = width.truncate(n); - trace!("trunc result: {}", result); - Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) - }; - - let value = match (lit, &ty.kind()) { - (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { - let s = s.as_str(); - let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes()); - let allocation = tcx.intern_const_alloc(allocation); - ConstValue::Slice { data: allocation, start: 0, end: s.len() } - } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) - if matches!(inner_ty.kind(), ty::Slice(_)) => - { - let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); - let allocation = tcx.intern_const_alloc(allocation); - ConstValue::Slice { data: allocation, start: 0, end: data.len() } - } - (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { - let id = tcx.allocate_bytes(data); - ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) - } - (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { - ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) - } - (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { - trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })? - } - (ast::LitKind::Float(n, _), ty::Float(fty)) => { - parse_float(*n, *fty, neg).ok_or(LitToConstError::Reported)? - } - (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)), - (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)), - (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported), - _ => return Err(LitToConstError::TypeError), - }; - - Ok(ConstantKind::Val(value, ty)) -} diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index fa5ec22926efc..05b1342cf8c07 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -966,13 +966,13 @@ enum TestKind<'tcx> { /// /// For `bool` we always generate two edges, one for `true` and one for /// `false`. - options: FxIndexMap, u128>, + options: FxIndexMap, u128>, }, /// Test for equality with value, possibly after an unsizing coercion to /// `ty`, Eq { - value: ty::Const<'tcx>, + value: ConstantKind<'tcx>, // Integer types are handled by `SwitchInt`, and constants with ADT // types are converted back into patterns, so this can only be `&str`, // `&[T]`, `f32` or `f64`. diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 7f53d9dd70502..895df5808dbe1 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -228,9 +228,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => (None, 0), }; if let Some((min, max, sz)) = range { - if let (Some(lo), Some(hi)) = - (lo.val().try_to_bits(sz), hi.val().try_to_bits(sz)) - { + if let (Some(lo), Some(hi)) = (lo.try_to_bits(sz), hi.try_to_bits(sz)) { // We want to compare ranges numerically, but the order of the bitwise // representation of signed integers does not match their numeric order. // Thus, to correct the ordering, we need to shift the range of signed diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index d7993ce1cf4fa..565345595d500 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { test_place: &PlaceBuilder<'tcx>, candidate: &Candidate<'pat, 'tcx>, switch_ty: Ty<'tcx>, - options: &mut FxIndexMap, u128>, + options: &mut FxIndexMap, u128>, ) -> bool { let Some(match_pair) = candidate.match_pairs.iter().find(|mp| mp.place == *test_place) else { return false; @@ -366,7 +366,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block: BasicBlock, make_target_blocks: impl FnOnce(&mut Self) -> Vec, source_info: SourceInfo, - value: ty::Const<'tcx>, + value: ConstantKind<'tcx>, place: Place<'tcx>, mut ty: Ty<'tcx>, ) { @@ -760,7 +760,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern) } - fn const_range_contains(&self, range: PatRange<'tcx>, value: ty::Const<'tcx>) -> Option { + fn const_range_contains( + &self, + range: PatRange<'tcx>, + value: ConstantKind<'tcx>, + ) -> Option { use std::cmp::Ordering::*; let tcx = self.tcx; @@ -777,7 +781,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn values_not_contained_in_range( &self, range: PatRange<'tcx>, - options: &FxIndexMap, u128>, + options: &FxIndexMap, u128>, ) -> Option { for &val in options.keys() { if self.const_range_contains(range, val)? { diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs index 84762d602f8db..8b1ab482ee8be 100644 --- a/compiler/rustc_mir_build/src/build/misc.rs +++ b/compiler/rustc_mir_build/src/build/misc.rs @@ -3,7 +3,6 @@ use crate::build::Builder; -use rustc_middle::mir; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, DUMMY_SP}; @@ -26,11 +25,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Convenience function for creating a literal operand, one /// without any user type annotation. - crate fn literal_operand( - &mut self, - span: Span, - literal: mir::ConstantKind<'tcx>, - ) -> Operand<'tcx> { + crate fn literal_operand(&mut self, span: Span, literal: ConstantKind<'tcx>) -> Operand<'tcx> { let constant = Box::new(Constant { span, user_ty: None, literal }); Operand::Constant(constant) } diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index a9c8943ec1804..1cbe8c5a68af4 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -1,7 +1,9 @@ use crate::build; use crate::build::expr::as_place::PlaceBuilder; use crate::build::scope::DropKind; +use crate::thir::constant::parse_float; use crate::thir::pattern::pat_from_hir; +use rustc_ast as ast; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -11,12 +13,15 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; +use rustc_middle::mir::interpret::Allocation; +use rustc_middle::mir::interpret::{ConstValue, LitToConstError, LitToConstInput, Scalar}; use rustc_middle::mir::*; use rustc_middle::thir::{BindingMode, Expr, ExprId, LintLevel, PatKind, Thir}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults}; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use super::lints; @@ -260,6 +265,57 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_ }) } +#[instrument(skip(tcx, lit_input))] +pub(crate) fn lit_to_mir_constant<'tcx>( + tcx: TyCtxt<'tcx>, + lit_input: LitToConstInput<'tcx>, +) -> Result, LitToConstError> { + let LitToConstInput { lit, ty, neg } = lit_input; + let trunc = |n| { + let param_ty = ty::ParamEnv::reveal_all().and(ty); + let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size; + trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); + let result = width.truncate(n); + trace!("trunc result: {}", result); + Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) + }; + + let value = match (lit, &ty.kind()) { + (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { + let s = s.as_str(); + let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes()); + let allocation = tcx.intern_const_alloc(allocation); + ConstValue::Slice { data: allocation, start: 0, end: s.len() } + } + (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) + if matches!(inner_ty.kind(), ty::Slice(_)) => + { + let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); + let allocation = tcx.intern_const_alloc(allocation); + ConstValue::Slice { data: allocation, start: 0, end: data.len() } + } + (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { + let id = tcx.allocate_bytes(data); + ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) + } + (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { + ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) + } + (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { + trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })? + } + (ast::LitKind::Float(n, _), ty::Float(fty)) => { + parse_float(*n, *fty, neg).ok_or(LitToConstError::Reported)? + } + (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)), + (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)), + (ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported), + _ => return Err(LitToConstError::TypeError), + }; + + Ok(ConstantKind::Val(value, ty)) +} + /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 4ffee59a96274..4f0402bfa8bc7 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -27,6 +27,7 @@ use rustc_middle::ty::query::Providers; pub fn provide(providers: &mut Providers) { providers.check_match = thir::pattern::check_match; providers.lit_to_const = thir::constant::lit_to_const; + providers.lit_to_mir_constant = build::lit_to_mir_constant; providers.mir_built = build::mir_built; providers.thir_check_unsafety = check_unsafety::thir_check_unsafety; providers.thir_check_unsafety_for_const_arg = check_unsafety::thir_check_unsafety_for_const_arg; diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index f17fe38b292cb..aafc368d3fdaa 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -5,6 +5,7 @@ use crate::thir::pattern::pat_from_hir; use crate::thir::util::UserAnnotatedTyHelpers; +use rustc_ast as ast; use rustc_data_structures::steal::Steal; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; @@ -12,8 +13,10 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::HirId; use rustc_hir::Node; use rustc_middle::middle::region; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::ConstantKind; use rustc_middle::thir::*; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; crate fn thir_body<'tcx>( @@ -75,6 +78,24 @@ impl<'tcx> Cx<'tcx> { } } + #[instrument(skip(self), level = "debug")] + crate fn const_eval_literal( + &mut self, + lit: &'tcx ast::LitKind, + ty: Ty<'tcx>, + sp: Span, + neg: bool, + ) -> ConstantKind<'tcx> { + match self.tcx.at(sp).lit_to_mir_constant(LitToConstInput { lit, ty, neg }) { + Ok(c) => c, + Err(LitToConstError::Reported) => { + // create a dummy value and continue compiling + ConstantKind::Ty(self.tcx.const_error(ty)) + } + Err(LitToConstError::TypeError) => bug!("const_eval_literal: had type error"), + } + } + crate fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> { let p = match self.tcx.hir().get(p.hir_id) { Node::Pat(p) | Node::Binding(p) => p, diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 2298cc7cddf47..880f86aff5d01 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,7 +1,7 @@ use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_middle::mir::Field; +use rustc_middle::mir::{self, Field}; use rustc_middle::thir::{FieldPat, Pat, PatKind}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; @@ -22,7 +22,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub(super) fn const_to_pat( &self, - cv: ty::Const<'tcx>, + cv: mir::ConstantKind<'tcx>, id: hir::HirId, span: Span, mir_structural_match_violation: bool, @@ -152,7 +152,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { ty.is_structural_eq_shallow(self.infcx.tcx) } - fn to_pat(&mut self, cv: ty::Const<'tcx>, mir_structural_match_violation: bool) -> Pat<'tcx> { + fn to_pat( + &mut self, + cv: mir::ConstantKind<'tcx>, + mir_structural_match_violation: bool, + ) -> Pat<'tcx> { trace!(self.treat_byte_string_as_slice); // This method is just a wrapper handling a validity check; the heavy lifting is // performed by the recursive `recur` method, which is not meant to be @@ -246,7 +250,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { fn field_pats( &self, - vals: impl Iterator>, + vals: impl Iterator>, ) -> Result>, FallbackToConstRef> { vals.enumerate() .map(|(idx, val)| { @@ -257,9 +261,10 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } // Recursive helper for `to_pat`; invoke that (instead of calling this directly). + #[instrument(skip(self), level = "debug")] fn recur( &self, - cv: ty::Const<'tcx>, + cv: mir::ConstantKind<'tcx>, mir_structural_match_violation: bool, ) -> Result, FallbackToConstRef> { let id = self.id; @@ -365,7 +370,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { PatKind::Wild } ty::Adt(adt_def, substs) if adt_def.is_enum() => { - let destructured = tcx.destructure_const(param_env.and(cv)); + let destructured = tcx.destructure_mir_constant(param_env, cv); PatKind::Variant { adt_def: *adt_def, substs, @@ -376,12 +381,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } } ty::Tuple(_) | ty::Adt(_, _) => { - let destructured = tcx.destructure_const(param_env.and(cv)); + let destructured = tcx.destructure_mir_constant(param_env, cv); PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? } } ty::Array(..) => PatKind::Array { prefix: tcx - .destructure_const(param_env.and(cv)) + .destructure_mir_constant(param_env, cv) .fields .iter() .map(|val| self.recur(*val, false)) @@ -412,12 +417,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // arrays. ty::Array(..) if !self.treat_byte_string_as_slice => { let old = self.behind_reference.replace(true); - let array = tcx.deref_const(self.param_env.and(cv)); + let array = tcx.deref_mir_constant(self.param_env.and(cv)); let val = PatKind::Deref { subpattern: Pat { kind: Box::new(PatKind::Array { prefix: tcx - .destructure_const(param_env.and(array)) + .destructure_mir_constant(param_env, array) .fields .iter() .map(|val| self.recur(*val, false)) @@ -438,12 +443,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // pattern. ty::Slice(elem_ty) => { let old = self.behind_reference.replace(true); - let array = tcx.deref_const(self.param_env.and(cv)); + let array = tcx.deref_mir_constant(self.param_env.and(cv)); let val = PatKind::Deref { subpattern: Pat { kind: Box::new(PatKind::Slice { prefix: tcx - .destructure_const(param_env.and(array)) + .destructure_mir_constant(param_env, array) .fields .iter() .map(|val| self.recur(*val, false)) @@ -512,7 +517,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // we fall back to a const pattern. If we do not do this, we may end up with // a !structural-match constant that is not of reference type, which makes it // very hard to invoke `PartialEq::eq` on it as a fallback. - let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) { + let val = match self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false) { Ok(subpattern) => PatKind::Deref { subpattern }, Err(_) => PatKind::Constant { value: cv }, }; diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 60eead69a1b05..b7de3f28872e5 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -52,7 +52,7 @@ use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::mir::Field; +use rustc_middle::mir::{self, Field}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; @@ -133,23 +133,35 @@ impl IntRange { } #[inline] - fn from_const<'tcx>( + fn from_constant<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - value: ty::Const<'tcx>, + value: mir::ConstantKind<'tcx>, ) -> Option { let ty = value.ty(); if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) { let val = (|| { - if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val() { - // For this specific pattern we can skip a lot of effort and go - // straight to the result, after doing a bit of checking. (We - // could remove this branch and just fall through, which - // is more general but much slower.) - if let Ok(bits) = scalar.to_bits_or_ptr_internal(target_size).unwrap() { - return Some(bits); + match value { + mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) => { + // For this specific pattern we can skip a lot of effort and go + // straight to the result, after doing a bit of checking. (We + // could remove this branch and just fall through, which + // is more general but much slower.) + if let Ok(Ok(bits)) = scalar.to_bits_or_ptr_internal(target_size) { + return Some(bits); + } else { + return None; + } } + mir::ConstantKind::Ty(c) => match c.val() { + ty::ConstKind::Value(_) => bug!( + "encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val" + ), + _ => {} + }, + _ => {} } + // This is a more general form of the previous case. value.try_eval_bits(tcx, param_env, ty) })()?; @@ -234,8 +246,8 @@ impl IntRange { let (lo, hi) = (lo ^ bias, hi ^ bias); let env = ty::ParamEnv::empty().and(ty); - let lo_const = ty::Const::from_bits(tcx, lo, env); - let hi_const = ty::Const::from_bits(tcx, hi, env); + let lo_const = mir::ConstantKind::from_bits(tcx, lo, env); + let hi_const = mir::ConstantKind::from_bits(tcx, hi, env); let kind = if lo == hi { PatKind::Constant { value: lo_const } @@ -635,9 +647,9 @@ pub(super) enum Constructor<'tcx> { /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange), /// Ranges of floating-point literal values (`2.0..=5.2`). - FloatRange(ty::Const<'tcx>, ty::Const<'tcx>, RangeEnd), + FloatRange(mir::ConstantKind<'tcx>, mir::ConstantKind<'tcx>, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. - Str(ty::Const<'tcx>), + Str(mir::ConstantKind<'tcx>), /// Array and slice patterns. Slice(Slice), /// Constants that must not be matched structurally. They are treated as black @@ -1376,7 +1388,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } PatKind::Constant { value } => { - if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, *value) { + if let Some(int_range) = IntRange::from_constant(cx.tcx, cx.param_env, *value) { ctor = IntRange(int_range); fields = Fields::empty(); } else { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 5b0aa4309a8ed..55d84782c485e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -17,7 +17,7 @@ use rustc_hir::RangeEnd; use rustc_index::vec::Idx; use rustc_middle::mir::interpret::{get_slice_bytes, ConstValue}; use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput}; -use rustc_middle::mir::UserTypeProjection; +use rustc_middle::mir::{self, UserTypeProjection}; use rustc_middle::mir::{BorrowKind, Field, Mutability}; use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; use rustc_middle::ty::subst::{GenericArg, SubstsRef}; @@ -121,8 +121,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_range( &mut self, ty: Ty<'tcx>, - lo: ty::Const<'tcx>, - hi: ty::Const<'tcx>, + lo: mir::ConstantKind<'tcx>, + hi: mir::ConstantKind<'tcx>, end: RangeEnd, span: Span, ) -> PatKind<'tcx> { @@ -177,16 +177,18 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ty: Ty<'tcx>, lo: Option<&PatKind<'tcx>>, hi: Option<&PatKind<'tcx>>, - ) -> Option<(ty::Const<'tcx>, ty::Const<'tcx>)> { + ) -> Option<(mir::ConstantKind<'tcx>, mir::ConstantKind<'tcx>)> { match (lo, hi) { (Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => { Some((*lo, *hi)) } (Some(PatKind::Constant { value: lo }), None) => { - Some((*lo, ty.numeric_max_val(self.tcx)?)) + let hi = ty.numeric_max_val(self.tcx)?; + Some((*lo, hi.into())) } (None, Some(PatKind::Constant { value: hi })) => { - Some((ty.numeric_min_val(self.tcx)?, *hi)) + let lo = ty.numeric_min_val(self.tcx)?; + Some((lo.into(), *hi)) } _ => None, } @@ -446,6 +448,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Takes a HIR Path. If the path is a constant, evaluates it and feeds /// it to `const_to_pat`. Any other path (like enum variants without fields) /// is converted to the corresponding pattern via `lower_variant_or_leaf`. + #[instrument(skip(self), level = "debug")] fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> { let ty = self.typeck_results.node_type(id); let res = self.typeck_results.qpath_res(qpath, id); @@ -487,8 +490,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation); match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) { - Ok(value) => { - let const_ = ty::Const::from_value(self.tcx, value, ty); + Ok(literal) => { + let const_ = mir::ConstantKind::Val(literal, ty); let pattern = self.const_to_pat(const_, id, span, mir_structural_match_violation); if !is_associated_const { @@ -537,25 +540,30 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { span: Span, ) -> PatKind<'tcx> { let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); - let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id); + let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const_def_id); // Evaluate early like we do in `lower_path`. let value = value.eval(self.tcx, self.param_env); - match value.val() { - ConstKind::Param(_) => { - self.errors.push(PatternError::ConstParamInPattern(span)); - return PatKind::Wild; - } - ConstKind::Unevaluated(_) => { - // If we land here it means the const can't be evaluated because it's `TooGeneric`. - self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter"); - return PatKind::Wild; + match value { + mir::ConstantKind::Ty(c) => { + match c.val() { + ConstKind::Param(_) => { + self.errors.push(PatternError::ConstParamInPattern(span)); + return PatKind::Wild; + } + ConstKind::Unevaluated(_) => { + // If we land here it means the const can't be evaluated because it's `TooGeneric`. + self.tcx + .sess + .span_err(span, "constant pattern depends on a generic parameter"); + return PatKind::Wild; + } + _ => bug!("Expected either ConstKind::Param or ConstKind::Unevaluated"), + } } - _ => (), + mir::ConstantKind::Val(_, _) => *self.const_to_pat(value, id, span, false).kind, } - - *self.const_to_pat(value, id, span, false).kind } /// Converts literals, paths and negation of literals to patterns. @@ -582,7 +590,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let lit_input = LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; - match self.tcx.at(expr.span).lit_to_const(lit_input) { + match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) { Ok(constant) => *self.const_to_pat(constant, expr.hir_id, lit.span, false).kind, Err(LitToConstError::Reported) => PatKind::Wild, Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), @@ -740,8 +748,8 @@ impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { #[instrument(skip(tcx), level = "debug")] crate fn compare_const_vals<'tcx>( tcx: TyCtxt<'tcx>, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, + a: mir::ConstantKind<'tcx>, + b: mir::ConstantKind<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Option { @@ -754,9 +762,7 @@ crate fn compare_const_vals<'tcx>( return fallback(); } - // Early return for equal constants (so e.g. references to ZSTs can be compared, even if they - // are just integer addresses). - if a.val() == b.val() { + if a == b { return from_bool(true); } @@ -788,9 +794,9 @@ crate fn compare_const_vals<'tcx>( } if let ty::Str = ty.kind() && let ( - ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }), - ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }), - ) = (a.val(), b.val()) + Some(a_val @ ConstValue::Slice { .. }), + Some(b_val @ ConstValue::Slice { .. }), + ) = (a.try_val(), b.try_val()) { let a_bytes = get_slice_bytes(&tcx, a_val); let b_bytes = get_slice_bytes(&tcx, b_val); diff --git a/src/test/ui/inline-const/const-match-pat-generic.rs b/src/test/ui/inline-const/const-match-pat-generic.rs index e1946467583e9..7c0d83516ea29 100644 --- a/src/test/ui/inline-const/const-match-pat-generic.rs +++ b/src/test/ui/inline-const/const-match-pat-generic.rs @@ -6,7 +6,8 @@ fn foo() { match 0 { const { V } => {}, - //~^ ERROR const parameters cannot be referenced in patterns [E0158] + //~^ ERROR constant pattern depends on a generic parameter + //~| ERROR constant pattern depends on a generic parameter _ => {}, } } diff --git a/src/test/ui/inline-const/const-match-pat-generic.stderr b/src/test/ui/inline-const/const-match-pat-generic.stderr index ade200d99ba39..77267f12fb11a 100644 --- a/src/test/ui/inline-const/const-match-pat-generic.stderr +++ b/src/test/ui/inline-const/const-match-pat-generic.stderr @@ -1,21 +1,26 @@ -error[E0158]: const parameters cannot be referenced in patterns +error: constant pattern depends on a generic parameter --> $DIR/const-match-pat-generic.rs:8:9 | LL | const { V } => {}, | ^^^^^^^^^^^ error: constant pattern depends on a generic parameter - --> $DIR/const-match-pat-generic.rs:20:9 + --> $DIR/const-match-pat-generic.rs:21:9 | LL | const { f(V) } => {}, | ^^^^^^^^^^^^^^ error: constant pattern depends on a generic parameter - --> $DIR/const-match-pat-generic.rs:20:9 + --> $DIR/const-match-pat-generic.rs:8:9 + | +LL | const { V } => {}, + | ^^^^^^^^^^^ + +error: constant pattern depends on a generic parameter + --> $DIR/const-match-pat-generic.rs:21:9 | LL | const { f(V) } => {}, | ^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0158`.