about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_middle/src/thir.rs72
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs31
2 files changed, 65 insertions, 38 deletions
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index fefdce8e77e..9457b62f22c 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -851,17 +851,21 @@ impl<'tcx> PatRange<'tcx> {
         //
         // Also, for performance, it's important to only do the second `try_to_bits` if necessary.
         let lo_is_min = match self.lo {
+            PatRangeBoundary::NegInfinity => true,
             PatRangeBoundary::Finite(value) => {
                 let lo = value.try_to_bits(size).unwrap() ^ bias;
                 lo <= min
             }
+            PatRangeBoundary::PosInfinity => false,
         };
         if lo_is_min {
             let hi_is_max = match self.hi {
+                PatRangeBoundary::NegInfinity => false,
                 PatRangeBoundary::Finite(value) => {
                     let hi = value.try_to_bits(size).unwrap() ^ bias;
                     hi > max || hi == max && self.end == RangeEnd::Included
                 }
+                PatRangeBoundary::PosInfinity => true,
             };
             if hi_is_max {
                 return Some(true);
@@ -920,11 +924,16 @@ impl<'tcx> PatRange<'tcx> {
 
 impl<'tcx> fmt::Display for PatRange<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let PatRangeBoundary::Finite(value) = &self.lo;
-        write!(f, "{value}")?;
-        write!(f, "{}", self.end)?;
-        let PatRangeBoundary::Finite(value) = &self.hi;
-        write!(f, "{value}")?;
+        if let PatRangeBoundary::Finite(value) = &self.lo {
+            write!(f, "{value}")?;
+        }
+        if let PatRangeBoundary::Finite(value) = &self.hi {
+            write!(f, "{}", self.end)?;
+            write!(f, "{value}")?;
+        } else {
+            // `0..` is parsed as an inclusive range, we must display it correctly.
+            write!(f, "..")?;
+        }
         Ok(())
     }
 }
@@ -934,38 +943,49 @@ impl<'tcx> fmt::Display for PatRange<'tcx> {
 #[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)]
 pub enum PatRangeBoundary<'tcx> {
     Finite(mir::Const<'tcx>),
+    NegInfinity,
+    PosInfinity,
 }
 
 impl<'tcx> PatRangeBoundary<'tcx> {
     #[inline]
-    pub fn lower_bound(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
-        // Unwrap is ok because the type is known to be numeric.
-        let c = ty.numeric_min_val(tcx).unwrap();
-        let value = mir::Const::from_ty_const(c, tcx);
-        Self::Finite(value)
+    pub fn is_finite(self) -> bool {
+        matches!(self, Self::Finite(..))
     }
     #[inline]
-    pub fn upper_bound(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
-        // Unwrap is ok because the type is known to be numeric.
-        let c = ty.numeric_max_val(tcx).unwrap();
-        let value = mir::Const::from_ty_const(c, tcx);
-        Self::Finite(value)
+    pub fn as_finite(self) -> Option<mir::Const<'tcx>> {
+        match self {
+            Self::Finite(value) => Some(value),
+            Self::NegInfinity | Self::PosInfinity => None,
+        }
     }
-
     #[inline]
-    pub fn to_const(self, _ty: Ty<'tcx>, _tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
+    pub fn to_const(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
         match self {
             Self::Finite(value) => value,
+            Self::NegInfinity => {
+                // Unwrap is ok because the type is known to be numeric.
+                let c = ty.numeric_min_val(tcx).unwrap();
+                mir::Const::from_ty_const(c, tcx)
+            }
+            Self::PosInfinity => {
+                // Unwrap is ok because the type is known to be numeric.
+                let c = ty.numeric_max_val(tcx).unwrap();
+                mir::Const::from_ty_const(c, tcx)
+            }
         }
     }
-    pub fn eval_bits(
-        self,
-        _ty: Ty<'tcx>,
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-    ) -> u128 {
+    pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u128 {
         match self {
             Self::Finite(value) => value.eval_bits(tcx, param_env),
+            Self::NegInfinity => {
+                // Unwrap is ok because the type is known to be numeric.
+                ty.numeric_min_and_max_as_bits(tcx).unwrap().0
+            }
+            Self::PosInfinity => {
+                // Unwrap is ok because the type is known to be numeric.
+                ty.numeric_min_and_max_as_bits(tcx).unwrap().1
+            }
         }
     }
 
@@ -979,6 +999,12 @@ impl<'tcx> PatRangeBoundary<'tcx> {
     ) -> Option<Ordering> {
         use PatRangeBoundary::*;
         match (self, other) {
+            // When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
+            // describe the same range. These two shortcuts are ok, but for the rest we must check
+            // bit values.
+            (PosInfinity, PosInfinity) => return Some(Ordering::Equal),
+            (NegInfinity, NegInfinity) => return Some(Ordering::Equal),
+
             // This code is hot when compiling matches with many ranges. So we
             // special-case extraction of evaluated scalars for speed, for types where
             // raw data comparisons are appropriate. E.g. `unicode-normalization` has
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index c530c0a772c..0811ab6a0a6 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -187,24 +187,25 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?;
         let (hi, hi_ascr, hi_inline) = self.lower_pattern_range_endpoint(hi_expr)?;
 
-        let lo = lo.unwrap_or_else(|| PatRangeBoundary::lower_bound(ty, self.tcx));
-        let hi = hi.unwrap_or_else(|| PatRangeBoundary::upper_bound(ty, self.tcx));
+        let lo = lo.unwrap_or(PatRangeBoundary::NegInfinity);
+        let hi = hi.unwrap_or(PatRangeBoundary::PosInfinity);
 
         let cmp = lo.compare_with(hi, ty, self.tcx, self.param_env);
-        let mut kind = match (end, cmp) {
+        let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty }));
+        match (end, cmp) {
             // `x..y` where `x < y`.
-            // Non-empty because the range includes at least `x`.
-            (RangeEnd::Excluded, Some(Ordering::Less)) => {
-                PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
-            }
-            // `x..=y` where `x == y`.
-            (RangeEnd::Included, Some(Ordering::Equal)) => {
-                PatKind::Constant { value: lo.to_const(ty, self.tcx) }
-            }
+            (RangeEnd::Excluded, Some(Ordering::Less)) => {}
             // `x..=y` where `x < y`.
-            (RangeEnd::Included, Some(Ordering::Less)) => {
-                PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
-            }
+            (RangeEnd::Included, Some(Ordering::Less)) => {}
+            // `x..=y` where `x == y` and `x` and `y` are finite.
+            (RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() && hi.is_finite() => {
+                kind = PatKind::Constant { value: lo.as_finite().unwrap() };
+            }
+            // `..=x` where `x == ty::MIN`.
+            (RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {}
+            // `x..` where `x == ty::MAX` (yes, `x..` gives `RangeEnd::Included` since it is meant
+            // to include `ty::MAX`).
+            (RangeEnd::Included, Some(Ordering::Equal)) if !hi.is_finite() => {}
             // `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
             _ => {
                 // Emit a more appropriate message if there was overflow.
@@ -223,7 +224,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                 };
                 return Err(e);
             }
-        };
+        }
 
         // If we are handling a range with associated constants (e.g.
         // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated