Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8df7e72

Browse files
committed
Auto merge of rust-lang#99322 - GKFX:const-int-parse, r=Mark-Simulacrum
Make {integer}::from_str_radix constant This commit makes FromStr on integers constant so that `const x: u32 = "23".parse();` works. More practical use-case is with environment variables at build time as discussed in rust-lang/rfcs#1907. Tracking issue rust-lang#59133. ACP: rust-lang/libs-team#74
2 parents 70714e3 + 3855b8b commit 8df7e72

File tree

10 files changed

+239
-166
lines changed

10 files changed

+239
-166
lines changed

‎library/core/src/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
#![feature(const_heap)]
138138
#![feature(const_hint_assert_unchecked)]
139139
#![feature(const_index_range_slice_index)]
140+
#![feature(const_int_from_str)]
140141
#![feature(const_intrinsic_copy)]
141142
#![feature(const_intrinsic_forget)]
142143
#![feature(const_ipv4)]

‎library/core/src/num/error.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,9 @@ pub enum IntErrorKind {
113113
impl ParseIntError {
114114
/// Outputs the detailed cause of parsing an integer failing.
115115
#[must_use]
116+
#[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")]
116117
#[stable(feature = "int_error_matching", since = "1.55.0")]
117-
pub fn kind(&self) -> &IntErrorKind {
118+
pub constfn kind(&self) -> &IntErrorKind {
118119
&self.kind
119120
}
120121
}

‎library/core/src/num/int_macros.rs‎

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -60,32 +60,6 @@ macro_rules! int_impl {
6060
#[stable(feature = "int_bits_const", since = "1.53.0")]
6161
pub const BITS: u32 = <$UnsignedT>::BITS;
6262

63-
/// Converts a string slice in a given base to an integer.
64-
///
65-
/// The string is expected to be an optional `+` or `-` sign followed by digits.
66-
/// Leading and trailing whitespace represent an error. Digits are a subset of these characters,
67-
/// depending on `radix`:
68-
///
69-
/// * `0-9`
70-
/// * `a-z`
71-
/// * `A-Z`
72-
///
73-
/// # Panics
74-
///
75-
/// This function panics if `radix` is not in the range from 2 to 36.
76-
///
77-
/// # Examples
78-
///
79-
/// Basic usage:
80-
///
81-
/// ```
82-
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")]
83-
/// ```
84-
#[stable(feature = "rust1", since = "1.0.0")]
85-
pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
86-
from_str_radix(src, radix)
87-
}
88-
8963
/// Returns the number of ones in the binary representation of `self`.
9064
///
9165
/// # Examples

‎library/core/src/num/mod.rs‎

Lines changed: 183 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::ascii;
66
use crate::hint;
77
use crate::intrinsics;
88
use crate::mem;
9-
use crate::ops::{Add, Mul, Sub};
109
use crate::str::FromStr;
1110

1211
// Used because the `?` operator is not allowed in a const context.
@@ -1386,144 +1385,218 @@ pub enum FpCategory {
13861385
Normal,
13871386
}
13881387

1389-
#[doc(hidden)]
1390-
trait FromStrRadixHelper:
1391-
PartialOrd + Copy + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self>
1392-
{
1393-
const MIN: Self;
1394-
fn from_u32(u: u32) -> Self;
1395-
fn checked_mul(&self, other: u32) -> Option<Self>;
1396-
fn checked_sub(&self, other: u32) -> Option<Self>;
1397-
fn checked_add(&self, other: u32) -> Option<Self>;
1398-
}
1399-
14001388
macro_rules! from_str_radix_int_impl {
14011389
($($t:ty)*) => {$(
14021390
#[stable(feature = "rust1", since = "1.0.0")]
14031391
impl FromStr for $t {
14041392
type Err = ParseIntError;
14051393
fn from_str(src: &str) -> Result<Self, ParseIntError> {
1406-
from_str_radix(src, 10)
1394+
<$t>::from_str_radix(src, 10)
14071395
}
14081396
}
14091397
)*}
14101398
}
14111399
from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
14121400

1413-
macro_rules! impl_helper_for {
1414-
($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
1415-
const MIN: Self = Self::MIN;
1416-
#[inline]
1417-
fn from_u32(u: u32) -> Self { u as Self }
1418-
#[inline]
1419-
fn checked_mul(&self, other: u32) -> Option<Self> {
1420-
Self::checked_mul(*self, other as Self)
1421-
}
1422-
#[inline]
1423-
fn checked_sub(&self, other: u32) -> Option<Self> {
1424-
Self::checked_sub(*self, other as Self)
1425-
}
1426-
#[inline]
1427-
fn checked_add(&self, other: u32) -> Option<Self> {
1428-
Self::checked_add(*self, other as Self)
1429-
}
1430-
})*)
1431-
}
1432-
impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
1433-
14341401
/// Determines if a string of text of that length of that radix could be guaranteed to be
14351402
/// stored in the given type T.
14361403
/// Note that if the radix is known to the compiler, it is just the check of digits.len that
14371404
/// is done at runtime.
14381405
#[doc(hidden)]
14391406
#[inline(always)]
14401407
#[unstable(issue = "none", feature = "std_internals")]
1441-
pub fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool {
1408+
pub constfn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool {
14421409
radix <= 16 && digits.len() <= mem::size_of::<T>() * 2 - is_signed_ty as usize
14431410
}
14441411

1445-
fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, ParseIntError> {
1446-
use self::IntErrorKind::*;
1447-
use self::ParseIntError as PIE;
1412+
#[track_caller]
1413+
const fn from_str_radix_panic_ct(_radix: u32) -> ! {
1414+
panic!("from_str_radix_int: must lie in the range `[2, 36]`");
1415+
}
14481416

1449-
assert!(
1450-
(2..=36).contains(&radix),
1451-
"from_str_radix_int: must lie in the range `[2, 36]` - found {}",
1452-
radix
1453-
);
1417+
#[track_caller]
1418+
fn from_str_radix_panic_rt(radix: u32) -> ! {
1419+
panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix);
1420+
}
14541421

1455-
if src.is_empty() {
1456-
return Err(PIE { kind: Empty });
1422+
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
1423+
#[cfg_attr(feature = "panic_immediate_abort", inline)]
1424+
#[cold]
1425+
#[track_caller]
1426+
const fn from_str_radix_assert(radix: u32) {
1427+
if 2 > radix || radix > 36 {
1428+
// The only difference between these two functions is their panic message.
1429+
intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt);
14571430
}
1431+
}
14581432

1459-
let is_signed_ty = T::from_u32(0) > T::MIN;
1460-
1461-
// all valid digits are ascii, so we will just iterate over the utf8 bytes
1462-
// and cast them to chars. .to_digit() will safely return None for anything
1463-
// other than a valid ascii digit for the given radix, including the first-byte
1464-
// of multi-byte sequences
1465-
let src = src.as_bytes();
1433+
macro_rules! from_str_radix {
1434+
($($int_ty:ty)+) => {$(
1435+
impl $int_ty {
1436+
/// Converts a string slice in a given base to an integer.
1437+
///
1438+
/// The string is expected to be an optional `+` sign
1439+
/// followed by digits.
1440+
/// Leading and trailing whitespace represent an error.
1441+
/// Digits are a subset of these characters, depending on `radix`:
1442+
///
1443+
/// * `0-9`
1444+
/// * `a-z`
1445+
/// * `A-Z`
1446+
///
1447+
/// # Panics
1448+
///
1449+
/// This function panics if `radix` is not in the range from 2 to 36.
1450+
///
1451+
/// # Examples
1452+
///
1453+
/// Basic usage:
1454+
///
1455+
/// ```
1456+
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")]
1457+
/// ```
1458+
#[stable(feature = "rust1", since = "1.0.0")]
1459+
#[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")]
1460+
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> {
1461+
use self::IntErrorKind::*;
1462+
use self::ParseIntError as PIE;
1463+
1464+
from_str_radix_assert(radix);
1465+
1466+
if src.is_empty() {
1467+
return Err(PIE { kind: Empty });
1468+
}
14661469

1467-
let (is_positive, digits) = match src[0] {
1468-
b'+' | b'-' if src[1..].is_empty() => {
1469-
return Err(PIE { kind: InvalidDigit });
1470-
}
1471-
b'+' => (true, &src[1..]),
1472-
b'-' if is_signed_ty => (false, &src[1..]),
1473-
_ => (true, src),
1474-
};
1470+
#[allow(unused_comparisons)]
1471+
let is_signed_ty = 0 > <$int_ty>::MIN;
1472+
1473+
// all valid digits are ascii, so we will just iterate over the utf8 bytes
1474+
// and cast them to chars. .to_digit() will safely return None for anything
1475+
// other than a valid ascii digit for the given radix, including the first-byte
1476+
// of multi-byte sequences
1477+
let src = src.as_bytes();
1478+
1479+
let (is_positive, mut digits) = match src {
1480+
[b'+' | b'-'] => {
1481+
return Err(PIE { kind: InvalidDigit });
1482+
}
1483+
[b'+', rest @ ..] => (true, rest),
1484+
[b'-', rest @ ..] if is_signed_ty => (false, rest),
1485+
_ => (true, src),
1486+
};
1487+
1488+
let mut result = 0;
1489+
1490+
macro_rules! unwrap_or_PIE {
1491+
($option:expr, $kind:ident) => {
1492+
match $option {
1493+
Some(value) => value,
1494+
None => return Err(PIE { kind: $kind }),
1495+
}
1496+
};
1497+
}
14751498

1476-
let mut result = T::from_u32(0);
1477-
1478-
if can_not_overflow::<T>(radix, is_signed_ty, digits) {
1479-
// If the len of the str is short compared to the range of the type
1480-
// we are parsing into, then we can be certain that an overflow will not occur.
1481-
// This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition
1482-
// above is a faster (conservative) approximation of this.
1483-
//
1484-
// Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest:
1485-
// `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow.
1486-
// `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow.
1487-
macro_rules! run_unchecked_loop {
1488-
($unchecked_additive_op:expr) => {
1489-
for &c in digits {
1490-
result = result * T::from_u32(radix);
1491-
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
1492-
result = $unchecked_additive_op(result, T::from_u32(x));
1499+
if can_not_overflow::<$int_ty>(radix, is_signed_ty, digits) {
1500+
// If the len of the str is short compared to the range of the type
1501+
// we are parsing into, then we can be certain that an overflow will not occur.
1502+
// This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition
1503+
// above is a faster (conservative) approximation of this.
1504+
//
1505+
// Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest:
1506+
// `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow.
1507+
// `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow.
1508+
macro_rules! run_unchecked_loop {
1509+
($unchecked_additive_op:tt) => {{
1510+
while let [c, rest @ ..] = digits {
1511+
result = result * (radix as $int_ty);
1512+
let x = unwrap_or_PIE!((*c as char).to_digit(radix), InvalidDigit);
1513+
result = result $unchecked_additive_op (x as $int_ty);
1514+
digits = rest;
1515+
}
1516+
}};
1517+
}
1518+
if is_positive {
1519+
run_unchecked_loop!(+)
1520+
} else {
1521+
run_unchecked_loop!(-)
1522+
};
1523+
} else {
1524+
macro_rules! run_checked_loop {
1525+
($checked_additive_op:ident, $overflow_err:ident) => {{
1526+
while let [c, rest @ ..] = digits {
1527+
// When `radix` is passed in as a literal, rather than doing a slow `imul`
1528+
// the compiler can use shifts if `radix` can be expressed as a
1529+
// sum of powers of 2 (x*10 can be written as x*8 + x*2).
1530+
// When the compiler can't use these optimisations,
1531+
// the latency of the multiplication can be hidden by issuing it
1532+
// before the result is needed to improve performance on
1533+
// modern out-of-order CPU as multiplication here is slower
1534+
// than the other instructions, we can get the end result faster
1535+
// doing multiplication first and let the CPU spends other cycles
1536+
// doing other computation and get multiplication result later.
1537+
let mul = result.checked_mul(radix as $int_ty);
1538+
let x = unwrap_or_PIE!((*c as char).to_digit(radix), InvalidDigit) as $int_ty;
1539+
result = unwrap_or_PIE!(mul, $overflow_err);
1540+
result = unwrap_or_PIE!(<$int_ty>::$checked_additive_op(result, x), $overflow_err);
1541+
digits = rest;
1542+
}
1543+
}};
1544+
}
1545+
if is_positive {
1546+
run_checked_loop!(checked_add, PosOverflow)
1547+
} else {
1548+
run_checked_loop!(checked_sub, NegOverflow)
1549+
};
14931550
}
1494-
};
1551+
Ok(result)
1552+
}
14951553
}
1496-
if is_positive {
1497-
run_unchecked_loop!(<T as core::ops::Add>::add)
1498-
} else {
1499-
run_unchecked_loop!(<T as core::ops::Sub>::sub)
1500-
};
1501-
} else {
1502-
macro_rules! run_checked_loop {
1503-
($checked_additive_op:ident, $overflow_err:expr) => {
1504-
for &c in digits {
1505-
// When `radix` is passed in as a literal, rather than doing a slow `imul`
1506-
// the compiler can use shifts if `radix` can be expressed as a
1507-
// sum of powers of 2 (x*10 can be written as x*8 + x*2).
1508-
// When the compiler can't use these optimisations,
1509-
// the latency of the multiplication can be hidden by issuing it
1510-
// before the result is needed to improve performance on
1511-
// modern out-of-order CPU as multiplication here is slower
1512-
// than the other instructions, we can get the end result faster
1513-
// doing multiplication first and let the CPU spends other cycles
1514-
// doing other computation and get multiplication result later.
1515-
let mul = result.checked_mul(radix);
1516-
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
1517-
result = mul.ok_or_else($overflow_err)?;
1518-
result = T::$checked_additive_op(&result, x).ok_or_else($overflow_err)?;
1519-
}
1520-
};
1554+
)+}
1555+
}
1556+
1557+
from_str_radix! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 }
1558+
1559+
// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two
1560+
// identical functions.
1561+
macro_rules! from_str_radix_size_impl {
1562+
($($t:ident $size:ty),*) => {$(
1563+
impl $size {
1564+
/// Converts a string slice in a given base to an integer.
1565+
///
1566+
/// The string is expected to be an optional `+` sign
1567+
/// followed by digits.
1568+
/// Leading and trailing whitespace represent an error.
1569+
/// Digits are a subset of these characters, depending on `radix`:
1570+
///
1571+
/// * `0-9`
1572+
/// * `a-z`
1573+
/// * `A-Z`
1574+
///
1575+
/// # Panics
1576+
///
1577+
/// This function panics if `radix` is not in the range from 2 to 36.
1578+
///
1579+
/// # Examples
1580+
///
1581+
/// Basic usage:
1582+
///
1583+
/// ```
1584+
#[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")]
1585+
/// ```
1586+
#[stable(feature = "rust1", since = "1.0.0")]
1587+
#[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")]
1588+
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> {
1589+
match <$t>::from_str_radix(src, radix) {
1590+
Ok(x) => Ok(x as $size),
1591+
Err(e) => Err(e),
1592+
}
15211593
}
1522-
if is_positive {
1523-
run_checked_loop!(checked_add, || PIE { kind: PosOverflow })
1524-
} else {
1525-
run_checked_loop!(checked_sub, || PIE { kind: NegOverflow })
1526-
};
1527-
}
1528-
Ok(result)
1594+
})*}
15291595
}
1596+
1597+
#[cfg(target_pointer_width = "16")]
1598+
from_str_radix_size_impl! { i16 isize, u16 usize }
1599+
#[cfg(target_pointer_width = "32")]
1600+
from_str_radix_size_impl! { i32 isize, u32 usize }
1601+
#[cfg(target_pointer_width = "64")]
1602+
from_str_radix_size_impl! { i64 isize, u64 usize }

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /