Skip to content

Commit 8d6f9d5

Browse files
committed
fix wrapping
1 parent 2a24f7d commit 8d6f9d5

File tree

2 files changed

+36
-18
lines changed

2 files changed

+36
-18
lines changed

library/core/src/ascii/ascii_char.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -617,17 +617,26 @@ impl Add<u8> for AsciiChar {
617617
/// Calculates sum of the ASCII value and given offset.
618618
///
619619
/// In debug builds, panics if result is greater than largest ASCII value.
620-
///
621620
/// In release builds wraps the value (i.e. masks out the most significant
622621
/// bit) so the output is a valid ASCII character.
623622
///
623+
/// # Examples
624+
///
624625
/// ```
625626
/// #![feature(ascii_char, ascii_char_variants)]
626627
/// use core::ascii::Char;
627628
///
628629
/// assert_eq!(Char::Digit8, Char::Digit0 + 8);
629630
/// ```
631+
///
632+
/// ```should_panic
633+
/// #![feature(ascii_char, ascii_char_variants)]
634+
/// use core::ascii::Char;
635+
///
636+
/// let _ = Char::Digit0 + 100;
637+
/// ```
630638
#[inline]
639+
#[rustc_inherit_overflow_checks]
631640
fn add(self, rhs: u8) -> Self::Output {
632641
add_impl(self, rhs)
633642
}
@@ -638,6 +647,7 @@ impl Add<AsciiChar> for u8 {
638647
type Output = AsciiChar;
639648

640649
#[inline]
650+
#[rustc_inherit_overflow_checks]
641651
fn add(self, rhs: AsciiChar) -> Self::Output {
642652
add_impl(rhs, self)
643653
}
@@ -646,6 +656,7 @@ impl Add<AsciiChar> for u8 {
646656
#[unstable(feature = "ascii_char", issue = "110998")]
647657
impl AddAssign<u8> for AsciiChar {
648658
#[inline]
659+
#[rustc_inherit_overflow_checks]
649660
fn add_assign(&mut self, rhs: u8) {
650661
*self = add_impl(*self, rhs)
651662
}
@@ -656,12 +667,11 @@ forward_ref_binop! { impl Add, add for u8, AsciiChar }
656667
forward_ref_op_assign! { impl AddAssign, add_assign for AsciiChar, u8 }
657668

658669
#[inline]
670+
#[rustc_inherit_overflow_checks]
659671
fn add_impl(chr: AsciiChar, rhs: u8) -> AsciiChar {
660-
let sum = u16::from(u8::from(chr)) + u16::from(rhs);
661-
if !cfg!(debug_assertions) || sum < 128 {
662-
// SAFETY: `& 127` limits the sum to a valid ASCII value.
663-
unsafe { AsciiChar::from_u8_unchecked((sum as u8) & 127) }
664-
} else {
665-
panic!("{} + {} overflows ASCII value", u8::from(chr), rhs)
666-
}
672+
// Make sure we overflow if chr + rhs ≥ 128. Since we inherit overflow
673+
// checks, we’re wrap in release and panic in debug builds.
674+
let sum = u8::from(chr) + 128 + rhs;
675+
// SAFETY: `sum & 127` limits the sum to a valid ASCII value.
676+
unsafe { AsciiChar::from_u8_unchecked(sum & 127) }
667677
}

library/core/tests/ascii_char.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,32 @@ fn test_arithmetic_ok() {
1313
assert_eq!(Char::Digit8, digit);
1414
}
1515

16-
/// Tests addition resulting in invalid ASCII character results in a panic.
16+
/// Tests addition wraps values when built in release mode.
17+
#[test]
18+
#[cfg_attr(debug_assertions, ignore)]
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.
1725
///
18-
/// Note: In release builds the result is wrapped instead of panicking. Since
19-
/// everything is built in debug mode when testing, this behaviour is not
20-
/// currently tested.
26+
/// Note: Rust tests (at least when run via `./x.py test library/core`) are
27+
/// built in release mode so this test is normally ignored. However, we have
28+
/// a doctest which checks the behaviour.
2129
#[test]
22-
#[should_panic]
30+
#[cfg_attr(not(debug_assertions), ignore)]
2331
fn test_arithmetic_non_ascii() {
2432
let _ = Char::Digit0 + 120;
2533
}
2634

27-
/// Tests addition overflowing u8.
35+
/// Tests addition panics in debug build when it overflowing u8.
2836
///
29-
/// Note: In release builds the result is wrapped instead of panicking. Since
30-
/// everything is built in debug mode when testing, this behaviour is not
31-
/// currently tested.
37+
/// Note: Rust tests (at least when run via `./x.py test library/core`) are
38+
/// built in release mode so this test is normally ignored. However, we have
39+
/// a doctest which checks the behaviour.
3240
#[test]
33-
#[should_panic]
41+
#[cfg_attr(not(debug_assertions), ignore)]
3442
fn test_arithmetic_overflow() {
3543
let _ = Char::Digit0 + 250;
3644
}

0 commit comments

Comments
 (0)