From fac50e8fb333a6a23e0bcbd6bd612860e8ad1b62 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 29 Sep 2023 18:33:07 +0200 Subject: [PATCH 1/5] Evaluate float consts eagerly --- .../src/thir/pattern/deconstruct_pat.rs | 98 +++++++++++-------- tests/ui/pattern/usefulness/floats.rs | 40 ++++++-- tests/ui/pattern/usefulness/floats.stderr | 52 ++++++++-- 3 files changed, 136 insertions(+), 54 deletions(-) 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 b79beb1c537ae..fba3c7ee9c156 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -50,6 +50,7 @@ use std::ops::RangeInclusive; use smallvec::{smallvec, SmallVec}; +use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; use rustc_data_structures::captures::Captures; use rustc_hir::{HirId, RangeEnd}; use rustc_index::Idx; @@ -65,7 +66,6 @@ use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx, FIRST_VARIANT}; use self::Constructor::*; use self::SliceKind::*; -use super::compare_const_vals; use super::usefulness::{MatchCheckCtxt, PatCtxt}; use crate::errors::{Overlap, OverlappingRangeEndpoints}; @@ -619,7 +619,8 @@ 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(mir::Const<'tcx>, mir::Const<'tcx>, RangeEnd), + F32Range(IeeeFloat, IeeeFloat, RangeEnd), + F64Range(IeeeFloat, IeeeFloat, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. Str(mir::Const<'tcx>), /// Array and slice patterns. @@ -634,7 +635,9 @@ pub(super) enum Constructor<'tcx> { /// Stands for constructors that are not seen in the matrix, as explained in the documentation /// for [`SplitWildcard`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns` /// lint. - Missing { nonexhaustive_enum_missing_real_variants: bool }, + Missing { + nonexhaustive_enum_missing_real_variants: bool, + }, /// Wildcard pattern. Wildcard, /// Or-pattern. @@ -722,7 +725,8 @@ impl<'tcx> Constructor<'tcx> { }, Slice(slice) => slice.arity(), Str(..) - | FloatRange(..) + | F32Range(..) + | F64Range(..) | IntRange(..) | NonExhaustive | Opaque @@ -795,21 +799,21 @@ impl<'tcx> Constructor<'tcx> { (Variant(self_id), Variant(other_id)) => self_id == other_id, (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range), - ( - FloatRange(self_from, self_to, self_end), - FloatRange(other_from, other_to, other_end), - ) => { - match ( - compare_const_vals(pcx.cx.tcx, *self_to, *other_to, pcx.cx.param_env), - compare_const_vals(pcx.cx.tcx, *self_from, *other_from, pcx.cx.param_env), - ) { - (Some(to), Some(from)) => { - (from == Ordering::Greater || from == Ordering::Equal) - && (to == Ordering::Less - || (other_end == self_end && to == Ordering::Equal)) + (F32Range(self_from, self_to, self_end), F32Range(other_from, other_to, other_end)) => { + self_from.ge(other_from) + && match self_to.partial_cmp(other_to) { + Some(Ordering::Less) => true, + Some(Ordering::Equal) => other_end == self_end, + _ => false, + } + } + (F64Range(self_from, self_to, self_end), F64Range(other_from, other_to, other_end)) => { + self_from.ge(other_from) + && match self_to.partial_cmp(other_to) { + Some(Ordering::Less) => true, + Some(Ordering::Equal) => other_end == self_end, + _ => false, } - _ => false, - } } (Str(self_val), Str(other_val)) => { // FIXME Once valtrees are available we can directly use the bytes @@ -859,7 +863,7 @@ impl<'tcx> Constructor<'tcx> { .any(|other| slice.is_covered_by(other)), // This constructor is never covered by anything else NonExhaustive => false, - Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard | Or => { + Str(..) | F32Range(..) | F64Range(..) | Opaque | Missing { .. } | Wildcard | Or => { span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self) } } @@ -1203,7 +1207,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { _ => bug!("bad slice pattern {:?} {:?}", constructor, pcx), }, Str(..) - | FloatRange(..) + | F32Range(..) + | F64Range(..) | IntRange(..) | NonExhaustive | Opaque @@ -1348,8 +1353,19 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { fields = Fields::empty(); } else { match pat.ty.kind() { - ty::Float(_) => { - ctor = FloatRange(*value, *value, RangeEnd::Included); + ty::Float(float_ty) => { + let bits = value.eval_bits(cx.tcx, cx.param_env); + use rustc_apfloat::Float; + ctor = match float_ty { + ty::FloatTy::F32 => { + let value = rustc_apfloat::ieee::Single::from_bits(bits); + F32Range(value, value, RangeEnd::Included) + } + ty::FloatTy::F64 => { + let value = rustc_apfloat::ieee::Double::from_bits(bits); + F64Range(value, value, RangeEnd::Included) + } + }; fields = Fields::empty(); } ty::Ref(_, t, _) if t.is_str() => { @@ -1376,17 +1392,25 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } &PatKind::Range(box PatRange { lo, hi, end }) => { + use rustc_apfloat::Float; let ty = lo.ty(); - ctor = if let Some(int_range) = IntRange::from_range( - cx.tcx, - lo.eval_bits(cx.tcx, cx.param_env), - hi.eval_bits(cx.tcx, cx.param_env), - ty, - &end, - ) { - IntRange(int_range) - } else { - FloatRange(lo, hi, end) + let lo = lo.eval_bits(cx.tcx, cx.param_env); + let hi = hi.eval_bits(cx.tcx, cx.param_env); + ctor = match ty.kind() { + ty::Char | ty::Int(_) | ty::Uint(_) => { + IntRange(IntRange::from_range(cx.tcx, lo, hi, ty, &end).unwrap()) + } + ty::Float(ty::FloatTy::F32) => { + let lo = rustc_apfloat::ieee::Single::from_bits(lo); + let hi = rustc_apfloat::ieee::Single::from_bits(hi); + F32Range(lo, hi, *end) + } + ty::Float(ty::FloatTy::F64) => { + let lo = rustc_apfloat::ieee::Double::from_bits(lo); + let hi = rustc_apfloat::ieee::Double::from_bits(hi); + F64Range(lo, hi, *end) + } + _ => bug!("invalid type for range pattern: {}", ty), }; fields = Fields::empty(); } @@ -1491,14 +1515,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } &Str(value) => PatKind::Constant { value }, - &FloatRange(lo, hi, end) => PatKind::Range(Box::new(PatRange { lo, hi, end })), IntRange(range) => return range.to_pat(cx.tcx, self.ty), Wildcard | NonExhaustive => PatKind::Wild, Missing { .. } => bug!( "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, `Missing` should have been processed in `apply_constructors`" ), - Opaque | Or => { + F32Range(..) | F64Range(..) | Opaque | Or => { bug!("can't convert to pattern: {:?}", self) } }; @@ -1673,11 +1696,8 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } write!(f, "]") } - &FloatRange(lo, hi, end) => { - write!(f, "{lo}")?; - write!(f, "{end}")?; - write!(f, "{hi}") - } + F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), + F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), IntRange(range) => write!(f, "{range:?}"), // Best-effort, will render e.g. `false` as `0..=0` Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty), Or => { diff --git a/tests/ui/pattern/usefulness/floats.rs b/tests/ui/pattern/usefulness/floats.rs index 095f5ac9a8970..2616dfadb85e8 100644 --- a/tests/ui/pattern/usefulness/floats.rs +++ b/tests/ui/pattern/usefulness/floats.rs @@ -1,19 +1,45 @@ +#![feature(exclusive_range_pattern)] #![allow(illegal_floating_point_literal_pattern)] #![deny(unreachable_patterns)] fn main() { match 0.0 { - 0.0..=1.0 => {} - _ => {} // ok + 0.0..=1.0 => {} + _ => {} // ok } - match 0.0 { //~ ERROR non-exhaustive patterns - 0.0..=1.0 => {} + match 0.0 { + //~^ ERROR non-exhaustive patterns + 0.0..=1.0 => {} } match 1.0f64 { - 0.01f64 ..= 6.5f64 => {} - 0.02f64 => {} //~ ERROR unreachable pattern - _ => {} + 0.01f64..=6.5f64 => {} + 0.005f64 => {} + 0.01f64 => {} //~ ERROR unreachable pattern + 0.02f64 => {} //~ ERROR unreachable pattern + 6.5f64 => {} //~ ERROR unreachable pattern + 6.6f64 => {} + 1.0f64..=4.0f64 => {} //~ ERROR unreachable pattern + 5.0f64..=7.0f64 => {} + _ => {} + }; + match 1.0f64 { + 0.01f64..6.5f64 => {} + 6.5f64 => {} // this is reachable + _ => {} + }; + + match 1.0f32 { + 0.01f32..=6.5f32 => {} + 0.01f32 => {} //~ ERROR unreachable pattern + 0.02f32 => {} //~ ERROR unreachable pattern + 6.5f32 => {} //~ ERROR unreachable pattern + _ => {} + }; + match 1.0f32 { + 0.01f32..6.5f32 => {} + 6.5f32 => {} // this is reachable + _ => {} }; } diff --git a/tests/ui/pattern/usefulness/floats.stderr b/tests/ui/pattern/usefulness/floats.stderr index d66d4ba298bea..f50419118246b 100644 --- a/tests/ui/pattern/usefulness/floats.stderr +++ b/tests/ui/pattern/usefulness/floats.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/floats.rs:10:11 + --> $DIR/floats.rs:11:11 | LL | match 0.0 { | ^^^ pattern `_` not covered @@ -7,22 +7,58 @@ LL | match 0.0 { = note: the matched value is of type `f64` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | -LL ~ 0.0..=1.0 => {}, -LL + _ => todo!() +LL ~ 0.0..=1.0 => {}, +LL + _ => todo!() | error: unreachable pattern - --> $DIR/floats.rs:16:7 + --> $DIR/floats.rs:19:9 | -LL | 0.02f64 => {} - | ^^^^^^^ +LL | 0.01f64 => {} + | ^^^^^^^ | note: the lint level is defined here - --> $DIR/floats.rs:2:9 + --> $DIR/floats.rs:3:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: unreachable pattern + --> $DIR/floats.rs:20:9 + | +LL | 0.02f64 => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/floats.rs:21:9 + | +LL | 6.5f64 => {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/floats.rs:23:9 + | +LL | 1.0f64..=4.0f64 => {} + | ^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/floats.rs:35:9 + | +LL | 0.01f32 => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/floats.rs:36:9 + | +LL | 0.02f32 => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/floats.rs:37:9 + | +LL | 6.5f32 => {} + | ^^^^^^ + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0004`. From d6e9b321b3f3de5fafb4cfd2b6a5af75197df657 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 29 Sep 2023 17:43:06 +0200 Subject: [PATCH 2/5] No need to carry `bias` in `IntRange` --- .../src/thir/pattern/deconstruct_pat.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) 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 fba3c7ee9c156..7c1ae4ad3cfa1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -99,10 +99,6 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { #[derive(Clone, PartialEq, Eq)] pub(crate) struct IntRange { range: RangeInclusive, - /// Keeps the bias used for encoding the range. It depends on the type of the range and - /// possibly the pointer size of the current architecture. The algorithm ensures we never - /// compare `IntRange`s with different types/architectures. - bias: u128, } impl IntRange { @@ -150,7 +146,7 @@ impl IntRange { }?; let val = val ^ bias; - Some(IntRange { range: val..=val, bias }) + Some(IntRange { range: val..=val }) } #[inline] @@ -171,7 +167,7 @@ impl IntRange { // This should have been caught earlier by E0030. bug!("malformed range pattern: {}..={}", lo, (hi - offset)); } - IntRange { range: lo..=(hi - offset), bias } + IntRange { range: lo..=(hi - offset) } }) } @@ -194,7 +190,7 @@ impl IntRange { let (lo, hi) = self.boundaries(); let (other_lo, other_hi) = other.boundaries(); if lo <= other_hi && other_lo <= hi { - Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), bias: self.bias }) + Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi) }) } else { None } @@ -221,7 +217,7 @@ impl IntRange { fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { let (lo, hi) = self.boundaries(); - let bias = self.bias; + let bias = IntRange::signed_bias(tcx, ty); let (lo, hi) = (lo ^ bias, hi ^ bias); let env = ty::ParamEnv::empty().and(ty); @@ -304,8 +300,6 @@ impl IntRange { impl fmt::Debug for IntRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (lo, hi) = self.boundaries(); - let bias = self.bias; - let (lo, hi) = (lo ^ bias, hi ^ bias); write!(f, "{lo}")?; write!(f, "{}", RangeEnd::Included)?; write!(f, "{hi}") @@ -402,7 +396,7 @@ impl SplitIntRange { (JustBefore(n), AfterMax) => n..=u128::MAX, _ => unreachable!(), // Ruled out by the sorting and filtering we did }; - IntRange { range, bias: self.range.bias } + IntRange { range } }) } } From 0a6d794d0bc8872a935be4179aa3c2da33708c40 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 29 Sep 2023 19:44:12 +0200 Subject: [PATCH 3/5] Cleanup number literal evaluation --- .../src/thir/pattern/deconstruct_pat.rs | 193 +++++++++--------- 1 file changed, 102 insertions(+), 91 deletions(-) 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 7c1ae4ad3cfa1..8344ddb2a6967 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -61,7 +61,7 @@ use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx, FIRST_VARIANT}; +use rustc_target::abi::{FieldIdx, Integer, Primitive, Size, VariantIdx, FIRST_VARIANT}; use self::Constructor::*; use self::SliceKind::*; @@ -86,6 +86,35 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { pats } +/// Evaluate an int constant, with a faster branch for a common case. +#[inline] +fn fast_try_eval_bits<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &mir::Const<'tcx>, +) -> Option { + let int = match value { + // If the constant is already evaluated, we shortcut here. + mir::Const::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => { + valtree.unwrap_leaf() + }, + // This is a more general form of the previous case. + _ => { + value.try_eval_scalar_int(tcx, param_env)? + }, + }; + let size = match value.ty().kind() { + ty::Bool => Size::from_bytes(1), + ty::Char => Size::from_bytes(4), + ty::Int(ity) => Integer::from_int_ty(&tcx, *ity).size(), + ty::Uint(uty) => Integer::from_uint_ty(&tcx, *uty).size(), + ty::Float(ty::FloatTy::F32) => Primitive::F32.size(&tcx), + ty::Float(ty::FloatTy::F64) => Primitive::F64.size(&tcx), + _ => return None, + }; + int.to_bits(size).ok() +} + /// An inclusive interval, used for precise integer exhaustiveness checking. /// `IntRange`s always store a contiguous range. This means that values are /// encoded such that `0` encodes the minimum value for the integer, @@ -116,37 +145,12 @@ impl IntRange { } #[inline] - fn integral_size_and_signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Size, u128)> { - match *ty.kind() { - ty::Bool => Some((Size::from_bytes(1), 0)), - ty::Char => Some((Size::from_bytes(4), 0)), - ty::Int(ity) => { - let size = Integer::from_int_ty(&tcx, ity).size(); - Some((size, 1u128 << (size.bits() as u128 - 1))) - } - ty::Uint(uty) => Some((Integer::from_uint_ty(&tcx, uty).size(), 0)), - _ => None, - } - } - - #[inline] - fn from_constant<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: mir::Const<'tcx>, - ) -> Option { - let ty = value.ty(); - let (target_size, bias) = Self::integral_size_and_signed_bias(tcx, ty)?; - let val = match value { - mir::Const::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => { - valtree.unwrap_leaf().to_bits(target_size).ok() - }, - // This is a more general form of the previous case. - _ => value.try_eval_bits(tcx, param_env), - }?; - - let val = val ^ bias; - Some(IntRange { range: val..=val }) + fn from_bits<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, bits: u128) -> IntRange { + let bias = IntRange::signed_bias(tcx, ty); + // Perform a shift if the underlying types are signed, + // which makes the interval arithmetic simpler. + let val = bits ^ bias; + IntRange { range: val..=val } } #[inline] @@ -155,20 +159,18 @@ impl IntRange { lo: u128, hi: u128, ty: Ty<'tcx>, - end: &RangeEnd, - ) -> Option { - Self::is_integral(ty).then(|| { - // Perform a shift if the underlying types are signed, - // which makes the interval arithmetic simpler. - let bias = IntRange::signed_bias(tcx, ty); - let (lo, hi) = (lo ^ bias, hi ^ bias); - let offset = (*end == RangeEnd::Excluded) as u128; - if lo > hi || (lo == hi && *end == RangeEnd::Excluded) { - // This should have been caught earlier by E0030. - bug!("malformed range pattern: {}..={}", lo, (hi - offset)); - } - IntRange { range: lo..=(hi - offset) } - }) + end: RangeEnd, + ) -> IntRange { + // Perform a shift if the underlying types are signed, + // which makes the interval arithmetic simpler. + let bias = IntRange::signed_bias(tcx, ty); + let (lo, hi) = (lo ^ bias, hi ^ bias); + let offset = (end == RangeEnd::Excluded) as u128; + if lo > hi || (lo == hi && end == RangeEnd::Excluded) { + // This should have been caught earlier by E0030. + bug!("malformed range pattern: {}..={}", lo, (hi - offset)); + } + IntRange { range: lo..=(hi - offset) } } // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. @@ -894,7 +896,7 @@ impl<'tcx> SplitWildcard<'tcx> { let make_range = |start, end| { IntRange( // `unwrap()` is ok because we know the type is an integer. - IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included).unwrap(), + IntRange::from_range(cx.tcx, start, end, pcx.ty, RangeEnd::Included), ) }; // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, @@ -1342,57 +1344,66 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } PatKind::Constant { value } => { - if let Some(int_range) = IntRange::from_constant(cx.tcx, cx.param_env, *value) { - ctor = IntRange(int_range); - fields = Fields::empty(); - } else { - match pat.ty.kind() { - ty::Float(float_ty) => { - let bits = value.eval_bits(cx.tcx, cx.param_env); - use rustc_apfloat::Float; - ctor = match float_ty { - ty::FloatTy::F32 => { - let value = rustc_apfloat::ieee::Single::from_bits(bits); - F32Range(value, value, RangeEnd::Included) - } - ty::FloatTy::F64 => { - let value = rustc_apfloat::ieee::Double::from_bits(bits); - F64Range(value, value, RangeEnd::Included) - } - }; - fields = Fields::empty(); - } - ty::Ref(_, t, _) if t.is_str() => { - // We want a `&str` constant to behave like a `Deref` pattern, to be compatible - // with other `Deref` patterns. This could have been done in `const_to_pat`, - // but that causes issues with the rest of the matching code. - // So here, the constructor for a `"foo"` pattern is `&` (represented by - // `Single`), and has one field. That field has constructor `Str(value)` and no - // fields. - // Note: `t` is `str`, not `&str`. - let subpattern = - DeconstructedPat::new(Str(*value), Fields::empty(), *t, pat.span); - ctor = Single; - fields = Fields::singleton(cx, subpattern) - } - // All constants that can be structurally matched have already been expanded - // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are - // opaque. - _ => { - ctor = Opaque; - fields = Fields::empty(); - } + match pat.ty.kind() { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) => { + ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) { + Some(bits) => IntRange(IntRange::from_bits(cx.tcx, pat.ty, bits)), + None => Opaque, + }; + fields = Fields::empty(); + } + ty::Float(ty::FloatTy::F32) => { + ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) { + Some(bits) => { + use rustc_apfloat::Float; + let value = rustc_apfloat::ieee::Single::from_bits(bits); + F32Range(value, value, RangeEnd::Included) + } + None => Opaque, + }; + fields = Fields::empty(); + } + ty::Float(ty::FloatTy::F64) => { + ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) { + Some(bits) => { + use rustc_apfloat::Float; + let value = rustc_apfloat::ieee::Double::from_bits(bits); + F64Range(value, value, RangeEnd::Included) + } + None => Opaque, + }; + fields = Fields::empty(); + } + ty::Ref(_, t, _) if t.is_str() => { + // We want a `&str` constant to behave like a `Deref` pattern, to be compatible + // with other `Deref` patterns. This could have been done in `const_to_pat`, + // but that causes issues with the rest of the matching code. + // So here, the constructor for a `"foo"` pattern is `&` (represented by + // `Single`), and has one field. That field has constructor `Str(value)` and no + // fields. + // Note: `t` is `str`, not `&str`. + let subpattern = + DeconstructedPat::new(Str(*value), Fields::empty(), *t, pat.span); + ctor = Single; + fields = Fields::singleton(cx, subpattern) + } + // All constants that can be structurally matched have already been expanded + // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are + // opaque. + _ => { + ctor = Opaque; + fields = Fields::empty(); } } } - &PatKind::Range(box PatRange { lo, hi, end }) => { + PatKind::Range(box PatRange { lo, hi, end }) => { use rustc_apfloat::Float; let ty = lo.ty(); - let lo = lo.eval_bits(cx.tcx, cx.param_env); - let hi = hi.eval_bits(cx.tcx, cx.param_env); + let lo = fast_try_eval_bits(cx.tcx, cx.param_env, lo).unwrap(); + let hi = fast_try_eval_bits(cx.tcx, cx.param_env, hi).unwrap(); ctor = match ty.kind() { ty::Char | ty::Int(_) | ty::Uint(_) => { - IntRange(IntRange::from_range(cx.tcx, lo, hi, ty, &end).unwrap()) + IntRange(IntRange::from_range(cx.tcx, lo, hi, ty, *end)) } ty::Float(ty::FloatTy::F32) => { let lo = rustc_apfloat::ieee::Single::from_bits(lo); From a5637e59665015be09d7d3bcc14d24dfc2a0b414 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 30 Sep 2023 23:54:31 +0200 Subject: [PATCH 4/5] test: is try_eval_bits fast enough actually? --- .../src/thir/pattern/deconstruct_pat.rs | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) 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 8344ddb2a6967..d0a18b01a8082 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -61,7 +61,7 @@ use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::{FieldIdx, Integer, Primitive, Size, VariantIdx, FIRST_VARIANT}; +use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; use self::Constructor::*; use self::SliceKind::*; @@ -93,26 +93,27 @@ fn fast_try_eval_bits<'tcx>( param_env: ty::ParamEnv<'tcx>, value: &mir::Const<'tcx>, ) -> Option { - let int = match value { - // If the constant is already evaluated, we shortcut here. - mir::Const::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => { - valtree.unwrap_leaf() - }, - // This is a more general form of the previous case. - _ => { - value.try_eval_scalar_int(tcx, param_env)? - }, - }; - let size = match value.ty().kind() { - ty::Bool => Size::from_bytes(1), - ty::Char => Size::from_bytes(4), - ty::Int(ity) => Integer::from_int_ty(&tcx, *ity).size(), - ty::Uint(uty) => Integer::from_uint_ty(&tcx, *uty).size(), - ty::Float(ty::FloatTy::F32) => Primitive::F32.size(&tcx), - ty::Float(ty::FloatTy::F64) => Primitive::F64.size(&tcx), - _ => return None, - }; - int.to_bits(size).ok() + value.try_eval_bits(tcx, param_env) + // let int = match value { + // // If the constant is already evaluated, we shortcut here. + // mir::Const::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => { + // valtree.unwrap_leaf() + // }, + // // This is a more general form of the previous case. + // _ => { + // value.try_eval_scalar_int(tcx, param_env)? + // }, + // }; + // let size = match value.ty().kind() { + // ty::Bool => Size::from_bytes(1), + // ty::Char => Size::from_bytes(4), + // ty::Int(ity) => Integer::from_int_ty(&tcx, *ity).size(), + // ty::Uint(uty) => Integer::from_uint_ty(&tcx, *uty).size(), + // ty::Float(ty::FloatTy::F32) => Primitive::F32.size(&tcx), + // ty::Float(ty::FloatTy::F64) => Primitive::F64.size(&tcx), + // _ => return None, + // }; + // int.to_bits(size).ok() } /// An inclusive interval, used for precise integer exhaustiveness checking. From bc3ee3798c65fba08fa49defbe67cb992e15fb92 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 1 Oct 2023 03:11:03 +0200 Subject: [PATCH 5/5] Optim was useful! Let's share it with everyone --- compiler/rustc_middle/src/mir/consts.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 7c8a57b840b74..5356d1f7c42ca 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -293,7 +293,20 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn try_eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option { - let int = self.try_eval_scalar_int(tcx, param_env)?; + debug_assert!(matches!( + self.ty().kind(), + ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char + )); + let int = match self { + // If the constant is already evaluated, we shortcut here. + Const::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => { + valtree.unwrap_leaf() + }, + // This is a more general form of the previous case. + _ => { + self.try_eval_scalar_int(tcx, param_env)? + }, + }; let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size; int.to_bits(size).ok()