From ef3305449b7166153cce085d1cc5ffd30f470d0b Mon Sep 17 00:00:00 2001
From: ltdk <usr@ltdk.xyz>
Date: Mon, 3 Jul 2023 13:11:39 -0400
Subject: [PATCH] Implement Step for AsciiChar

---
 library/core/src/iter/range.rs   | 45 +++++++++++++++++++++++++++++++-
 library/core/tests/iter/range.rs | 18 ++++++++++++-
 library/core/tests/lib.rs        |  2 ++
 tests/ui/range/range-1.stderr    |  2 +-
 4 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs
index 462f7170a55..44feb0a5638 100644
--- a/library/core/src/iter/range.rs
+++ b/library/core/src/iter/range.rs
@@ -1,3 +1,4 @@
+use crate::ascii::Char as AsciiChar;
 use crate::convert::TryFrom;
 use crate::mem;
 use crate::num::NonZeroUsize;
@@ -14,7 +15,7 @@ macro_rules! unsafe_impl_trusted_step {
         unsafe impl TrustedStep for $type {}
     )*};
 }
-unsafe_impl_trusted_step![char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize];
+unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize];
 
 /// Objects that have a notion of *successor* and *predecessor* operations.
 ///
@@ -484,6 +485,48 @@ impl Step for char {
     }
 }
 
+#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+impl Step for AsciiChar {
+    #[inline]
+    fn steps_between(&start: &AsciiChar, &end: &AsciiChar) -> Option<usize> {
+        Step::steps_between(&start.to_u8(), &end.to_u8())
+    }
+
+    #[inline]
+    fn forward_checked(start: AsciiChar, count: usize) -> Option<AsciiChar> {
+        let end = Step::forward_checked(start.to_u8(), count)?;
+        AsciiChar::from_u8(end)
+    }
+
+    #[inline]
+    fn backward_checked(start: AsciiChar, count: usize) -> Option<AsciiChar> {
+        let end = Step::backward_checked(start.to_u8(), count)?;
+
+        // SAFETY: Values below that of a valid ASCII character are also valid ASCII
+        Some(unsafe { AsciiChar::from_u8_unchecked(end) })
+    }
+
+    #[inline]
+    unsafe fn forward_unchecked(start: AsciiChar, count: usize) -> AsciiChar {
+        // SAFETY: Caller asserts that result is a valid ASCII character,
+        // and therefore it is a valid u8.
+        let end = unsafe { Step::forward_unchecked(start.to_u8(), count) };
+
+        // SAFETY: Caller asserts that result is a valid ASCII character.
+        unsafe { AsciiChar::from_u8_unchecked(end) }
+    }
+
+    #[inline]
+    unsafe fn backward_unchecked(start: AsciiChar, count: usize) -> AsciiChar {
+        // SAFETY: Caller asserts that result is a valid ASCII character,
+        // and therefore it is a valid u8.
+        let end = unsafe { Step::backward_unchecked(start.to_u8(), count) };
+
+        // SAFETY: Caller asserts that result is a valid ASCII character.
+        unsafe { AsciiChar::from_u8_unchecked(end) }
+    }
+}
+
 macro_rules! range_exact_iter_impl {
     ($($t:ty)*) => ($(
         #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs
index 0a77ecddb84..5b87d6c1fa0 100644
--- a/library/core/tests/iter/range.rs
+++ b/library/core/tests/iter/range.rs
@@ -1,5 +1,6 @@
-use core::num::NonZeroUsize;
 use super::*;
+use core::ascii::Char as AsciiChar;
+use core::num::NonZeroUsize;
 
 #[test]
 fn test_range() {
@@ -39,6 +40,21 @@ fn test_char_range() {
     assert_eq!(('\u{D7FF}'..'\u{E000}').size_hint(), (1, Some(1)));
 }
 
+#[test]
+fn test_ascii_char_range() {
+    let from = AsciiChar::Null;
+    let to = AsciiChar::Delete;
+    assert!((from..=to).eq((from as u8..=to as u8).filter_map(AsciiChar::from_u8)));
+    assert!((from..=to).rev().eq((from as u8..=to as u8).filter_map(AsciiChar::from_u8).rev()));
+
+    assert_eq!((AsciiChar::CapitalA..=AsciiChar::CapitalZ).count(), 26);
+    assert_eq!((AsciiChar::CapitalA..=AsciiChar::CapitalZ).size_hint(), (26, Some(26)));
+    assert_eq!((AsciiChar::SmallA..=AsciiChar::SmallZ).count(), 26);
+    assert_eq!((AsciiChar::SmallA..=AsciiChar::SmallZ).size_hint(), (26, Some(26)));
+    assert_eq!((AsciiChar::Digit0..=AsciiChar::Digit9).count(), 10);
+    assert_eq!((AsciiChar::Digit0..=AsciiChar::Digit9).size_hint(), (10, Some(10)));
+}
+
 #[test]
 fn test_range_exhaustion() {
     let mut r = 10..10;
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index d849561bb21..aac9250aa4f 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -2,6 +2,8 @@
 #![feature(array_chunks)]
 #![feature(array_methods)]
 #![feature(array_windows)]
+#![feature(ascii_char)]
+#![feature(ascii_char_variants)]
 #![feature(bigint_helper_methods)]
 #![feature(cell_update)]
 #![feature(const_align_offset)]
diff --git a/tests/ui/range/range-1.stderr b/tests/ui/range/range-1.stderr
index 277d9b2682d..ecfc56961ee 100644
--- a/tests/ui/range/range-1.stderr
+++ b/tests/ui/range/range-1.stderr
@@ -19,7 +19,7 @@ LL |     for i in false..true {}
              i64
              i128
              usize
-           and 5 others
+           and 6 others
    = note: required for `std::ops::Range<bool>` to implement `Iterator`
    = note: required for `std::ops::Range<bool>` to implement `IntoIterator`