Skip to content

core: implement Add and AddAssign for ascii::Char #120219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions library/core/src/ascii/ascii_char.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use crate::fmt::{self, Write};
use crate::mem::transmute;
use crate::ops::{Add, AddAssign};

/// One of the 128 Unicode characters from U+0000 through U+007F,
/// often known as the [ASCII] subset.
Expand Down Expand Up @@ -616,3 +617,71 @@ impl fmt::Debug for AsciiChar {
f.write_char('\'')
}
}

#[unstable(feature = "ascii_char", issue = "110998")]
impl Add<u8> for AsciiChar {
type Output = AsciiChar;

/// Calculates sum of the ASCII value and given offset.
///
/// In debug builds, panics if result is greater than largest ASCII value.
/// In release builds wraps the value (i.e. masks out the most significant
/// bit) so the output is a valid ASCII character.
///
/// # Examples
///
/// ```
/// #![feature(ascii_char, ascii_char_variants)]
/// use core::ascii::Char;
///
/// assert_eq!(Char::Digit8, Char::Digit0 + 8);
/// ```
///
/// ```should_panic
/// #![feature(ascii_char, ascii_char_variants)]
/// use core::ascii::Char;
///
/// // This produces value which is not a valid ASCII character and thus
/// // panics in debug builds.
/// let _ = Char::Digit0 + 100;
/// ```
#[inline]
#[rustc_inherit_overflow_checks]
fn add(self, rhs: u8) -> Self::Output {
add_impl(self, rhs)
}
}

#[unstable(feature = "ascii_char", issue = "110998")]
impl Add<AsciiChar> for u8 {
type Output = AsciiChar;

#[inline]
#[rustc_inherit_overflow_checks]
fn add(self, rhs: AsciiChar) -> Self::Output {
add_impl(rhs, self)
}
}

#[unstable(feature = "ascii_char", issue = "110998")]
impl AddAssign<u8> for AsciiChar {
#[inline]
#[rustc_inherit_overflow_checks]
fn add_assign(&mut self, rhs: u8) {
*self = add_impl(*self, rhs)
}
}

forward_ref_binop! { impl Add, add for AsciiChar, u8 }
forward_ref_binop! { impl Add, add for u8, AsciiChar }
forward_ref_op_assign! { impl AddAssign, add_assign for AsciiChar, u8 }

#[inline]
#[rustc_inherit_overflow_checks]
fn add_impl(chr: AsciiChar, rhs: u8) -> AsciiChar {
// Make sure we overflow if chr + rhs ≥ 128. Since we inherit overflow
// checks, we’re wrap in release and panic in debug builds.
let sum = u8::from(chr) + 128 + rhs;
// SAFETY: `sum & 127` limits the sum to a valid ASCII value.
unsafe { AsciiChar::from_u8_unchecked(sum & 127) }
}
38 changes: 38 additions & 0 deletions library/core/tests/ascii_char.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use core::ascii::Char;

/// Tests addition of u8 values to ascii::Char;
#[test]
fn test_arithmetic_ok() {
assert_eq!(Char::Digit8, Char::Digit0 + 8);
assert_eq!(Char::Colon, Char::Digit0 + 10);
assert_eq!(Char::Digit8, 8 + Char::Digit0);
assert_eq!(Char::Colon, 10 + Char::Digit0);

let mut digit = Char::Digit0;
digit += 8;
assert_eq!(Char::Digit8, digit);
}

/// Tests addition wraps values when built in release mode.
#[test]
#[cfg_attr(debug_assertions, ignore = "works in release builds only")]
fn test_arithmetic_wrapping() {
assert_eq!(Char::Digit0, Char::Digit8 + 120);
assert_eq!(Char::Digit0, Char::Digit8 + 248);
}

/// Tests addition panics in debug build when it produces an invalid ASCII char.
#[test]
#[cfg_attr(not(debug_assertions), ignore = "works in debug builds only")]
#[should_panic]
fn test_arithmetic_non_ascii() {
let _ = Char::Digit0 + 120;
}

/// Tests addition panics in debug build when it overflowing u8.
#[test]
#[cfg_attr(not(debug_assertions), ignore = "works in debug builds only")]
#[should_panic]
fn test_arithmetic_overflow() {
let _ = Char::Digit0 + 250;
}
1 change: 1 addition & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ mod alloc;
mod any;
mod array;
mod ascii;
mod ascii_char;
mod asserting;
mod async_iter;
mod atomic;
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/issues/issue-11771.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ LL | 1 +
<i16 as Add<&i16>>
<i32 as Add>
<i32 as Add<&i32>>
and 48 others
and 52 others

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

error: aborting due to 2 previous errors

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/issues/issue-50582.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ LL | Vec::<[(); 1 + for x in 0..1 {}]>::new();
<i16 as Add<&i16>>
<i32 as Add>
<i32 as Add<&i32>>
and 48 others
and 52 others

error: aborting due to 2 previous errors

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/mismatched_types/binops.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ LL | 1 + Some(1);
<i16 as Add<&i16>>
<i32 as Add>
<i32 as Add<&i32>>
and 48 others
and 52 others

error[E0277]: cannot subtract `Option<{integer}>` from `usize`
--> $DIR/binops.rs:3:16
Expand Down
4 changes: 4 additions & 0 deletions tests/ui/numbers-arithmetic/not-suggest-float-literal.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ LL | x + 100.0
= help: the trait `Add<{float}>` is not implemented for `u8`
= help: the following other types implement trait `Add<Rhs>`:
<u8 as Add>
<u8 as Add<Char>>
<u8 as Add<&u8>>
<u8 as Add<&Char>>
<&'a u8 as Add<u8>>
<&'a u8 as Add<Char>>
<&u8 as Add<&u8>>
<&u8 as Add<&Char>>

error[E0277]: cannot add `&str` to `f64`
--> $DIR/not-suggest-float-literal.rs:6:7
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/typeck/escaping_bound_vars.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>,
<i16 as Add<&i16>>
<i32 as Add>
<i32 as Add<&i32>>
and 48 others
and 52 others

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