Skip to content

Commit 7432588

Browse files
authored
Rollup merge of rust-lang#89258 - est31:const_char_convert, r=oli-obk
Make char conversion functions unstably const The char conversion functions like `char::from_u32` do trivial computations and can easily be converted into const fns. Only smaller tricks are needed to avoid non-const standard library functions like `Result::ok` or `bool::then_some`. Tracking issue: rust-lang#89259
2 parents 548c108 + 7272b6f commit 7432588

File tree

3 files changed

+33
-15
lines changed

3 files changed

+33
-15
lines changed

library/core/src/char/convert.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,13 @@ use super::MAX;
5151
#[must_use]
5252
#[inline]
5353
#[stable(feature = "rust1", since = "1.0.0")]
54-
pub fn from_u32(i: u32) -> Option<char> {
55-
char::try_from(i).ok()
54+
#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
55+
pub const fn from_u32(i: u32) -> Option<char> {
56+
// FIXME: once Result::ok is const fn, use it here
57+
match char_try_from_u32(i) {
58+
Ok(c) => Some(c),
59+
Err(_) => None,
60+
}
5661
}
5762

5863
/// Converts a `u32` to a `char`, ignoring validity.
@@ -91,7 +96,8 @@ pub fn from_u32(i: u32) -> Option<char> {
9196
#[inline]
9297
#[must_use]
9398
#[stable(feature = "char_from_unchecked", since = "1.5.0")]
94-
pub unsafe fn from_u32_unchecked(i: u32) -> char {
99+
#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
100+
pub const unsafe fn from_u32_unchecked(i: u32) -> char {
95101
// SAFETY: the caller must guarantee that `i` is a valid char value.
96102
if cfg!(debug_assertions) { char::from_u32(i).unwrap() } else { unsafe { transmute(i) } }
97103
}
@@ -248,18 +254,23 @@ impl FromStr for char {
248254
}
249255
}
250256

257+
#[inline]
258+
const fn char_try_from_u32(i: u32) -> Result<char, CharTryFromError> {
259+
if (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF) {
260+
Err(CharTryFromError(()))
261+
} else {
262+
// SAFETY: checked that it's a legal unicode value
263+
Ok(unsafe { transmute(i) })
264+
}
265+
}
266+
251267
#[stable(feature = "try_from", since = "1.34.0")]
252268
impl TryFrom<u32> for char {
253269
type Error = CharTryFromError;
254270

255271
#[inline]
256272
fn try_from(i: u32) -> Result<Self, Self::Error> {
257-
if (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF) {
258-
Err(CharTryFromError(()))
259-
} else {
260-
// SAFETY: checked that it's a legal unicode value
261-
Ok(unsafe { transmute(i) })
262-
}
273+
char_try_from_u32(i)
263274
}
264275
}
265276

@@ -327,7 +338,8 @@ impl fmt::Display for CharTryFromError {
327338
#[inline]
328339
#[must_use]
329340
#[stable(feature = "rust1", since = "1.0.0")]
330-
pub fn from_digit(num: u32, radix: u32) -> Option<char> {
341+
#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
342+
pub const fn from_digit(num: u32, radix: u32) -> Option<char> {
331343
if radix > 36 {
332344
panic!("from_digit: radix is too high (maximum 36)");
333345
}

library/core/src/char/methods.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,10 @@ impl char {
136136
/// assert_eq!(None, c);
137137
/// ```
138138
#[stable(feature = "assoc_char_funcs", since = "1.52.0")]
139+
#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
139140
#[must_use]
140141
#[inline]
141-
pub fn from_u32(i: u32) -> Option<char> {
142+
pub const fn from_u32(i: u32) -> Option<char> {
142143
super::convert::from_u32(i)
143144
}
144145

@@ -178,9 +179,10 @@ impl char {
178179
/// assert_eq!('❤', c);
179180
/// ```
180181
#[stable(feature = "assoc_char_funcs", since = "1.52.0")]
182+
#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
181183
#[must_use]
182184
#[inline]
183-
pub unsafe fn from_u32_unchecked(i: u32) -> char {
185+
pub const unsafe fn from_u32_unchecked(i: u32) -> char {
184186
// SAFETY: the safety contract must be upheld by the caller.
185187
unsafe { super::convert::from_u32_unchecked(i) }
186188
}
@@ -235,9 +237,10 @@ impl char {
235237
/// let _c = char::from_digit(1, 37);
236238
/// ```
237239
#[stable(feature = "assoc_char_funcs", since = "1.52.0")]
240+
#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
238241
#[must_use]
239242
#[inline]
240-
pub fn from_digit(num: u32, radix: u32) -> Option<char> {
243+
pub const fn from_digit(num: u32, radix: u32) -> Option<char> {
241244
super::convert::from_digit(num, radix)
242245
}
243246

@@ -331,10 +334,11 @@ impl char {
331334
/// let _ = '1'.to_digit(37);
332335
/// ```
333336
#[stable(feature = "rust1", since = "1.0.0")]
337+
#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
334338
#[must_use = "this returns the result of the operation, \
335339
without modifying the original"]
336340
#[inline]
337-
pub fn to_digit(self, radix: u32) -> Option<u32> {
341+
pub const fn to_digit(self, radix: u32) -> Option<u32> {
338342
assert!(radix <= 36, "to_digit: radix is too high (maximum 36)");
339343
// If not a digit, a number greater than radix will be created.
340344
let mut digit = (self as u32).wrapping_sub('0' as u32);
@@ -345,7 +349,8 @@ impl char {
345349
// Force the 6th bit to be set to ensure ascii is lower case.
346350
digit = (self as u32 | 0b10_0000).wrapping_sub('a' as u32).saturating_add(10);
347351
}
348-
(digit < radix).then_some(digit)
352+
// FIXME: once then_some is const fn, use it here
353+
if digit < radix { Some(digit) } else { None }
349354
}
350355

351356
/// Returns an iterator that yields the hexadecimal Unicode escape of a

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
#![feature(const_bigint_helper_methods)]
106106
#![feature(const_caller_location)]
107107
#![feature(const_cell_into_inner)]
108+
#![feature(const_char_convert)]
108109
#![feature(const_discriminant)]
109110
#![feature(const_eval_select)]
110111
#![feature(const_float_bits_conv)]

0 commit comments

Comments
 (0)