Skip to content

Commit 7c4e047

Browse files
committed
core: implement Add and AddAssign for ascii::Char
Implement Add and AddAsign for core::ascii::Char treating it as if it was a 7-bit unsigned type, i.e. in debug builds overflow panics and in release seven least significant bits of the result are taken. The operators can be used to convert an integer into a digit or a letter of an alphabet: use core::ascii::Char; assert_eq!(Char::Digit8, Char::Digit0 + 8); assert_eq!(Char::SmallI, Char::SmallA + 8); Issue: #110998
1 parent 04521fd commit 7c4e047

File tree

8 files changed

+117
-5
lines changed

8 files changed

+117
-5
lines changed

library/core/src/ascii/ascii_char.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
use crate::fmt::{self, Write};
77
use crate::mem::transmute;
8+
use crate::ops::{Add, AddAssign};
89

910
/// One of the 128 Unicode characters from U+0000 through U+007F,
1011
/// often known as the [ASCII] subset.
@@ -616,3 +617,71 @@ impl fmt::Debug for AsciiChar {
616617
f.write_char('\'')
617618
}
618619
}
620+
621+
#[unstable(feature = "ascii_char", issue = "110998")]
622+
impl Add<u8> for AsciiChar {
623+
type Output = AsciiChar;
624+
625+
/// Calculates sum of the ASCII value and given offset.
626+
///
627+
/// In debug builds, panics if result is greater than largest ASCII value.
628+
/// In release builds wraps the value (i.e. masks out the most significant
629+
/// bit) so the output is a valid ASCII character.
630+
///
631+
/// # Examples
632+
///
633+
/// ```
634+
/// #![feature(ascii_char, ascii_char_variants)]
635+
/// use core::ascii::Char;
636+
///
637+
/// assert_eq!(Char::Digit8, Char::Digit0 + 8);
638+
/// ```
639+
///
640+
/// ```should_panic
641+
/// #![feature(ascii_char, ascii_char_variants)]
642+
/// use core::ascii::Char;
643+
///
644+
/// // This produces value which is not a valid ASCII character and thus
645+
/// // panics in debug builds.
646+
/// let _ = Char::Digit0 + 100;
647+
/// ```
648+
#[inline]
649+
#[rustc_inherit_overflow_checks]
650+
fn add(self, rhs: u8) -> Self::Output {
651+
add_impl(self, rhs)
652+
}
653+
}
654+
655+
#[unstable(feature = "ascii_char", issue = "110998")]
656+
impl Add<AsciiChar> for u8 {
657+
type Output = AsciiChar;
658+
659+
#[inline]
660+
#[rustc_inherit_overflow_checks]
661+
fn add(self, rhs: AsciiChar) -> Self::Output {
662+
add_impl(rhs, self)
663+
}
664+
}
665+
666+
#[unstable(feature = "ascii_char", issue = "110998")]
667+
impl AddAssign<u8> for AsciiChar {
668+
#[inline]
669+
#[rustc_inherit_overflow_checks]
670+
fn add_assign(&mut self, rhs: u8) {
671+
*self = add_impl(*self, rhs)
672+
}
673+
}
674+
675+
forward_ref_binop! { impl Add, add for AsciiChar, u8 }
676+
forward_ref_binop! { impl Add, add for u8, AsciiChar }
677+
forward_ref_op_assign! { impl AddAssign, add_assign for AsciiChar, u8 }
678+
679+
#[inline]
680+
#[rustc_inherit_overflow_checks]
681+
fn add_impl(chr: AsciiChar, rhs: u8) -> AsciiChar {
682+
// Make sure we overflow if chr + rhs ≥ 128. Since we inherit overflow
683+
// checks, we’re wrap in release and panic in debug builds.
684+
let sum = u8::from(chr) + 128 + rhs;
685+
// SAFETY: `sum & 127` limits the sum to a valid ASCII value.
686+
unsafe { AsciiChar::from_u8_unchecked(sum & 127) }
687+
}

library/core/tests/ascii_char.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use core::ascii::Char;
2+
3+
/// Tests addition of u8 values to ascii::Char;
4+
#[test]
5+
fn test_arithmetic_ok() {
6+
assert_eq!(Char::Digit8, Char::Digit0 + 8);
7+
assert_eq!(Char::Colon, Char::Digit0 + 10);
8+
assert_eq!(Char::Digit8, 8 + Char::Digit0);
9+
assert_eq!(Char::Colon, 10 + Char::Digit0);
10+
11+
let mut digit = Char::Digit0;
12+
digit += 8;
13+
assert_eq!(Char::Digit8, digit);
14+
}
15+
16+
/// Tests addition wraps values when built in release mode.
17+
#[test]
18+
#[cfg_attr(debug_assertions, ignore = "works in release builds only")]
19+
fn test_arithmetic_wrapping() {
20+
assert_eq!(Char::Digit0, Char::Digit8 + 120);
21+
assert_eq!(Char::Digit0, Char::Digit8 + 248);
22+
}
23+
24+
/// Tests addition panics in debug build when it produces an invalid ASCII char.
25+
#[test]
26+
#[cfg_attr(not(debug_assertions), ignore = "works in debug builds only")]
27+
#[should_panic]
28+
fn test_arithmetic_non_ascii() {
29+
let _ = Char::Digit0 + 120;
30+
}
31+
32+
/// Tests addition panics in debug build when it overflowing u8.
33+
#[test]
34+
#[cfg_attr(not(debug_assertions), ignore = "works in debug builds only")]
35+
#[should_panic]
36+
fn test_arithmetic_overflow() {
37+
let _ = Char::Digit0 + 250;
38+
}

library/core/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ mod alloc;
124124
mod any;
125125
mod array;
126126
mod ascii;
127+
mod ascii_char;
127128
mod asserting;
128129
mod async_iter;
129130
mod atomic;

tests/ui/issues/issue-11771.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ LL | 1 +
1414
<i16 as Add<&i16>>
1515
<i32 as Add>
1616
<i32 as Add<&i32>>
17-
and 48 others
17+
and 52 others
1818

1919
error[E0277]: cannot add `()` to `{integer}`
2020
--> $DIR/issue-11771.rs:8:7
@@ -32,7 +32,7 @@ LL | 1 +
3232
<i16 as Add<&i16>>
3333
<i32 as Add>
3434
<i32 as Add<&i32>>
35-
and 48 others
35+
and 52 others
3636

3737
error: aborting due to 2 previous errors
3838

tests/ui/issues/issue-50582.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ LL | Vec::<[(); 1 + for x in 0..1 {}]>::new();
2424
<i16 as Add<&i16>>
2525
<i32 as Add>
2626
<i32 as Add<&i32>>
27-
and 48 others
27+
and 52 others
2828

2929
error: aborting due to 2 previous errors
3030

tests/ui/mismatched_types/binops.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ LL | 1 + Some(1);
1414
<i16 as Add<&i16>>
1515
<i32 as Add>
1616
<i32 as Add<&i32>>
17-
and 48 others
17+
and 52 others
1818

1919
error[E0277]: cannot subtract `Option<{integer}>` from `usize`
2020
--> $DIR/binops.rs:3:16

tests/ui/numbers-arithmetic/not-suggest-float-literal.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ LL | x + 100.0
77
= help: the trait `Add<{float}>` is not implemented for `u8`
88
= help: the following other types implement trait `Add<Rhs>`:
99
<u8 as Add>
10+
<u8 as Add<Char>>
1011
<u8 as Add<&u8>>
12+
<u8 as Add<&Char>>
1113
<&'a u8 as Add<u8>>
14+
<&'a u8 as Add<Char>>
1215
<&u8 as Add<&u8>>
16+
<&u8 as Add<&Char>>
1317

1418
error[E0277]: cannot add `&str` to `f64`
1519
--> $DIR/not-suggest-float-literal.rs:6:7

tests/ui/typeck/escaping_bound_vars.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>,
4040
<i16 as Add<&i16>>
4141
<i32 as Add>
4242
<i32 as Add<&i32>>
43-
and 48 others
43+
and 52 others
4444

4545
error[E0277]: the trait bound `(): Elide<(&(),)>` is not satisfied
4646
--> $DIR/escaping_bound_vars.rs:11:18

0 commit comments

Comments
 (0)