Skip to content

Commit 082b98d

Browse files
authoredNov 4, 2024
Rollup merge of rust-lang#132423 - RalfJung:const-eval-align-offset, r=dtolnay
remove const-support for align_offset and is_aligned As part of the recent discussion to stabilize `ptr.is_null()` in const context, the general vibe was that it's okay for a const function to panic when the same operation would work at runtime (that's just a case of "dynamically detecting that something is not supported as a const operation"), but it is *not* okay for a const function to just return a different result. Following that, `is_aligned` and `is_aligned_to` have their const status revoked in this PR, since they do return actively wrong results at const time. In the future we can consider having a new intrinsic or so that can check whether a pointer is "guaranteed to be aligned", but the current implementation based on `align_offset` does not have the behavior we want. In fact `align_offset` itself behaves quite strangely in const, and that support needs a bunch of special hacks. That doesn't seem worth it. Instead, the users that can fall back to a different implementation should just use const_eval_select directly, and everything else should not be made const-callable. So this PR does exactly that, and entirely removes const support for align_offset. Closes some tracking issues by removing the associated features: Closes rust-lang#90962 Closes rust-lang#104203 Cc `@rust-lang/wg-const-eval` `@rust-lang/libs-api`
2 parents 2bb8ea3 + d8bca01 commit 082b98d

12 files changed

+177
-931
lines changed
 

‎core/src/lib.rs

-2
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@
112112
#![feature(asm_experimental_arch)]
113113
#![feature(const_align_of_val)]
114114
#![feature(const_align_of_val_raw)]
115-
#![feature(const_align_offset)]
116115
#![feature(const_alloc_layout)]
117116
#![feature(const_black_box)]
118117
#![feature(const_char_encode_utf16)]
@@ -123,7 +122,6 @@
123122
#![feature(const_nonnull_new)]
124123
#![feature(const_option_ext)]
125124
#![feature(const_pin_2)]
126-
#![feature(const_pointer_is_aligned)]
127125
#![feature(const_ptr_is_null)]
128126
#![feature(const_ptr_sub_ptr)]
129127
#![feature(const_raw_ptr_comparison)]

‎core/src/ptr/const_ptr.rs

+4-188
Original file line numberDiff line numberDiff line change
@@ -1358,15 +1358,6 @@ impl<T: ?Sized> *const T {
13581358
/// beyond the allocation that the pointer points into. It is up to the caller to ensure that
13591359
/// the returned offset is correct in all terms other than alignment.
13601360
///
1361-
/// When this is called during compile-time evaluation (which is unstable), the implementation
1362-
/// may return `usize::MAX` in cases where that can never happen at runtime. This is because the
1363-
/// actual alignment of pointers is not known yet during compile-time, so an offset with
1364-
/// guaranteed alignment can sometimes not be computed. For example, a buffer declared as `[u8;
1365-
/// N]` might be allocated at an odd or an even address, but at compile-time this is not yet
1366-
/// known, so the execution has to be correct for either choice. It is therefore impossible to
1367-
/// find an offset that is guaranteed to be 2-aligned. (This behavior is subject to change, as usual
1368-
/// for unstable APIs.)
1369-
///
13701361
/// # Panics
13711362
///
13721363
/// The function panics if `align` is not a power-of-two.
@@ -1395,8 +1386,7 @@ impl<T: ?Sized> *const T {
13951386
#[must_use]
13961387
#[inline]
13971388
#[stable(feature = "align_offset", since = "1.36.0")]
1398-
#[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
1399-
pub const fn align_offset(self, align: usize) -> usize
1389+
pub fn align_offset(self, align: usize) -> usize
14001390
where
14011391
T: Sized,
14021392
{
@@ -1431,94 +1421,10 @@ impl<T: ?Sized> *const T {
14311421
/// assert!(ptr.is_aligned());
14321422
/// assert!(!ptr.wrapping_byte_add(1).is_aligned());
14331423
/// ```
1434-
///
1435-
/// # At compiletime
1436-
/// **Note: Alignment at compiletime is experimental and subject to change. See the
1437-
/// [tracking issue] for details.**
1438-
///
1439-
/// At compiletime, the compiler may not know where a value will end up in memory.
1440-
/// Calling this function on a pointer created from a reference at compiletime will only
1441-
/// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1442-
/// is never aligned if cast to a type with a stricter alignment than the reference's
1443-
/// underlying allocation.
1444-
///
1445-
/// ```
1446-
/// #![feature(const_pointer_is_aligned)]
1447-
///
1448-
/// // On some platforms, the alignment of primitives is less than their size.
1449-
/// #[repr(align(4))]
1450-
/// struct AlignedI32(i32);
1451-
/// #[repr(align(8))]
1452-
/// struct AlignedI64(i64);
1453-
///
1454-
/// const _: () = {
1455-
/// let data = AlignedI32(42);
1456-
/// let ptr = &data as *const AlignedI32;
1457-
/// assert!(ptr.is_aligned());
1458-
///
1459-
/// // At runtime either `ptr1` or `ptr2` would be aligned, but at compiletime neither is aligned.
1460-
/// let ptr1 = ptr.cast::<AlignedI64>();
1461-
/// let ptr2 = ptr.wrapping_add(1).cast::<AlignedI64>();
1462-
/// assert!(!ptr1.is_aligned());
1463-
/// assert!(!ptr2.is_aligned());
1464-
/// };
1465-
/// ```
1466-
///
1467-
/// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1468-
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
1469-
///
1470-
/// ```
1471-
/// #![feature(const_pointer_is_aligned)]
1472-
///
1473-
/// // On some platforms, the alignment of primitives is less than their size.
1474-
/// #[repr(align(4))]
1475-
/// struct AlignedI32(i32);
1476-
/// #[repr(align(8))]
1477-
/// struct AlignedI64(i64);
1478-
///
1479-
/// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1480-
/// const COMPTIME_PTR: *const AlignedI32 = &AlignedI32(42);
1481-
/// const _: () = assert!(!COMPTIME_PTR.cast::<AlignedI64>().is_aligned());
1482-
/// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).cast::<AlignedI64>().is_aligned());
1483-
///
1484-
/// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1485-
/// let runtime_ptr = COMPTIME_PTR;
1486-
/// assert_ne!(
1487-
/// runtime_ptr.cast::<AlignedI64>().is_aligned(),
1488-
/// runtime_ptr.wrapping_add(1).cast::<AlignedI64>().is_aligned(),
1489-
/// );
1490-
/// ```
1491-
///
1492-
/// If a pointer is created from a fixed address, this function behaves the same during
1493-
/// runtime and compiletime.
1494-
///
1495-
/// ```
1496-
/// #![feature(const_pointer_is_aligned)]
1497-
///
1498-
/// // On some platforms, the alignment of primitives is less than their size.
1499-
/// #[repr(align(4))]
1500-
/// struct AlignedI32(i32);
1501-
/// #[repr(align(8))]
1502-
/// struct AlignedI64(i64);
1503-
///
1504-
/// const _: () = {
1505-
/// let ptr = 40 as *const AlignedI32;
1506-
/// assert!(ptr.is_aligned());
1507-
///
1508-
/// // For pointers with a known address, runtime and compiletime behavior are identical.
1509-
/// let ptr1 = ptr.cast::<AlignedI64>();
1510-
/// let ptr2 = ptr.wrapping_add(1).cast::<AlignedI64>();
1511-
/// assert!(ptr1.is_aligned());
1512-
/// assert!(!ptr2.is_aligned());
1513-
/// };
1514-
/// ```
1515-
///
1516-
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
15171424
#[must_use]
15181425
#[inline]
15191426
#[stable(feature = "pointer_is_aligned", since = "1.79.0")]
1520-
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1521-
pub const fn is_aligned(self) -> bool
1427+
pub fn is_aligned(self) -> bool
15221428
where
15231429
T: Sized,
15241430
{
@@ -1555,105 +1461,15 @@ impl<T: ?Sized> *const T {
15551461
///
15561462
/// assert_ne!(ptr.is_aligned_to(8), ptr.wrapping_add(1).is_aligned_to(8));
15571463
/// ```
1558-
///
1559-
/// # At compiletime
1560-
/// **Note: Alignment at compiletime is experimental and subject to change. See the
1561-
/// [tracking issue] for details.**
1562-
///
1563-
/// At compiletime, the compiler may not know where a value will end up in memory.
1564-
/// Calling this function on a pointer created from a reference at compiletime will only
1565-
/// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1566-
/// cannot be stricter aligned than the reference's underlying allocation.
1567-
///
1568-
/// ```
1569-
/// #![feature(pointer_is_aligned_to)]
1570-
/// #![feature(const_pointer_is_aligned)]
1571-
///
1572-
/// // On some platforms, the alignment of i32 is less than 4.
1573-
/// #[repr(align(4))]
1574-
/// struct AlignedI32(i32);
1575-
///
1576-
/// const _: () = {
1577-
/// let data = AlignedI32(42);
1578-
/// let ptr = &data as *const AlignedI32;
1579-
///
1580-
/// assert!(ptr.is_aligned_to(1));
1581-
/// assert!(ptr.is_aligned_to(2));
1582-
/// assert!(ptr.is_aligned_to(4));
1583-
///
1584-
/// // At compiletime, we know for sure that the pointer isn't aligned to 8.
1585-
/// assert!(!ptr.is_aligned_to(8));
1586-
/// assert!(!ptr.wrapping_add(1).is_aligned_to(8));
1587-
/// };
1588-
/// ```
1589-
///
1590-
/// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1591-
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
1592-
///
1593-
/// ```
1594-
/// #![feature(pointer_is_aligned_to)]
1595-
/// #![feature(const_pointer_is_aligned)]
1596-
///
1597-
/// // On some platforms, the alignment of i32 is less than 4.
1598-
/// #[repr(align(4))]
1599-
/// struct AlignedI32(i32);
1600-
///
1601-
/// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1602-
/// const COMPTIME_PTR: *const AlignedI32 = &AlignedI32(42);
1603-
/// const _: () = assert!(!COMPTIME_PTR.is_aligned_to(8));
1604-
/// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).is_aligned_to(8));
1605-
///
1606-
/// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1607-
/// let runtime_ptr = COMPTIME_PTR;
1608-
/// assert_ne!(
1609-
/// runtime_ptr.is_aligned_to(8),
1610-
/// runtime_ptr.wrapping_add(1).is_aligned_to(8),
1611-
/// );
1612-
/// ```
1613-
///
1614-
/// If a pointer is created from a fixed address, this function behaves the same during
1615-
/// runtime and compiletime.
1616-
///
1617-
/// ```
1618-
/// #![feature(pointer_is_aligned_to)]
1619-
/// #![feature(const_pointer_is_aligned)]
1620-
///
1621-
/// const _: () = {
1622-
/// let ptr = 40 as *const u8;
1623-
/// assert!(ptr.is_aligned_to(1));
1624-
/// assert!(ptr.is_aligned_to(2));
1625-
/// assert!(ptr.is_aligned_to(4));
1626-
/// assert!(ptr.is_aligned_to(8));
1627-
/// assert!(!ptr.is_aligned_to(16));
1628-
/// };
1629-
/// ```
1630-
///
1631-
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
16321464
#[must_use]
16331465
#[inline]
16341466
#[unstable(feature = "pointer_is_aligned_to", issue = "96284")]
1635-
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1636-
pub const fn is_aligned_to(self, align: usize) -> bool {
1467+
pub fn is_aligned_to(self, align: usize) -> bool {
16371468
if !align.is_power_of_two() {
16381469
panic!("is_aligned_to: align is not a power-of-two");
16391470
}
16401471

1641-
#[inline]
1642-
fn runtime_impl(ptr: *const (), align: usize) -> bool {
1643-
ptr.addr() & (align - 1) == 0
1644-
}
1645-
1646-
#[inline]
1647-
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1648-
const fn const_impl(ptr: *const (), align: usize) -> bool {
1649-
// We can't use the address of `self` in a `const fn`, so we use `align_offset` instead.
1650-
ptr.align_offset(align) == 0
1651-
}
1652-
1653-
// The cast to `()` is used to
1654-
// 1. deal with fat pointers; and
1655-
// 2. ensure that `align_offset` (in `const_impl`) doesn't actually try to compute an offset.
1656-
const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl)
1472+
self.addr() & (align - 1) == 0
16571473
}
16581474
}
16591475

‎core/src/ptr/mod.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -1852,9 +1852,7 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
18521852
///
18531853
/// Any questions go to @nagisa.
18541854
#[allow(ptr_to_integer_transmute_in_consts)]
1855-
#[lang = "align_offset"]
1856-
#[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
1857-
pub(crate) const unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
1855+
pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
18581856
// FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <=
18591857
// 1, where the method versions of these operations are not inlined.
18601858
use intrinsics::{
@@ -1915,11 +1913,7 @@ pub(crate) const unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usiz
19151913

19161914
let stride = mem::size_of::<T>();
19171915

1918-
// SAFETY: This is just an inlined `p.addr()` (which is not
1919-
// a `const fn` so we cannot call it).
1920-
// During const eval, we hook this function to ensure that the pointer never
1921-
// has provenance, making this sound.
1922-
let addr: usize = unsafe { mem::transmute(p) };
1916+
let addr: usize = p.addr();
19231917

19241918
// SAFETY: `a` is a power-of-two, therefore non-zero.
19251919
let a_minus_one = unsafe { unchecked_sub(a, 1) };

‎core/src/ptr/mut_ptr.rs

+4-182
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use super::*;
22
use crate::cmp::Ordering::{Equal, Greater, Less};
3-
use crate::intrinsics::const_eval_select;
43
use crate::mem::SizedTypeProperties;
54
use crate::slice::{self, SliceIndex};
65

@@ -1636,8 +1635,7 @@ impl<T: ?Sized> *mut T {
16361635
#[must_use]
16371636
#[inline]
16381637
#[stable(feature = "align_offset", since = "1.36.0")]
1639-
#[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
1640-
pub const fn align_offset(self, align: usize) -> usize
1638+
pub fn align_offset(self, align: usize) -> usize
16411639
where
16421640
T: Sized,
16431641
{
@@ -1675,95 +1673,10 @@ impl<T: ?Sized> *mut T {
16751673
/// assert!(ptr.is_aligned());
16761674
/// assert!(!ptr.wrapping_byte_add(1).is_aligned());
16771675
/// ```
1678-
///
1679-
/// # At compiletime
1680-
/// **Note: Alignment at compiletime is experimental and subject to change. See the
1681-
/// [tracking issue] for details.**
1682-
///
1683-
/// At compiletime, the compiler may not know where a value will end up in memory.
1684-
/// Calling this function on a pointer created from a reference at compiletime will only
1685-
/// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1686-
/// is never aligned if cast to a type with a stricter alignment than the reference's
1687-
/// underlying allocation.
1688-
///
1689-
/// ```
1690-
/// #![feature(const_pointer_is_aligned)]
1691-
///
1692-
/// // On some platforms, the alignment of primitives is less than their size.
1693-
/// #[repr(align(4))]
1694-
/// struct AlignedI32(i32);
1695-
/// #[repr(align(8))]
1696-
/// struct AlignedI64(i64);
1697-
///
1698-
/// const _: () = {
1699-
/// let mut data = AlignedI32(42);
1700-
/// let ptr = &mut data as *mut AlignedI32;
1701-
/// assert!(ptr.is_aligned());
1702-
///
1703-
/// // At runtime either `ptr1` or `ptr2` would be aligned, but at compiletime neither is aligned.
1704-
/// let ptr1 = ptr.cast::<AlignedI64>();
1705-
/// let ptr2 = ptr.wrapping_add(1).cast::<AlignedI64>();
1706-
/// assert!(!ptr1.is_aligned());
1707-
/// assert!(!ptr2.is_aligned());
1708-
/// };
1709-
/// ```
1710-
///
1711-
/// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1712-
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
1713-
///
1714-
/// ```
1715-
/// #![feature(const_pointer_is_aligned)]
1716-
///
1717-
/// // On some platforms, the alignment of primitives is less than their size.
1718-
/// #[repr(align(4))]
1719-
/// struct AlignedI32(i32);
1720-
/// #[repr(align(8))]
1721-
/// struct AlignedI64(i64);
1722-
///
1723-
/// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1724-
/// // Also, note that mutable references are not allowed in the final value of constants.
1725-
/// const COMPTIME_PTR: *mut AlignedI32 = (&AlignedI32(42) as *const AlignedI32).cast_mut();
1726-
/// const _: () = assert!(!COMPTIME_PTR.cast::<AlignedI64>().is_aligned());
1727-
/// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).cast::<AlignedI64>().is_aligned());
1728-
///
1729-
/// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1730-
/// let runtime_ptr = COMPTIME_PTR;
1731-
/// assert_ne!(
1732-
/// runtime_ptr.cast::<AlignedI64>().is_aligned(),
1733-
/// runtime_ptr.wrapping_add(1).cast::<AlignedI64>().is_aligned(),
1734-
/// );
1735-
/// ```
1736-
///
1737-
/// If a pointer is created from a fixed address, this function behaves the same during
1738-
/// runtime and compiletime.
1739-
///
1740-
/// ```
1741-
/// #![feature(const_pointer_is_aligned)]
1742-
///
1743-
/// // On some platforms, the alignment of primitives is less than their size.
1744-
/// #[repr(align(4))]
1745-
/// struct AlignedI32(i32);
1746-
/// #[repr(align(8))]
1747-
/// struct AlignedI64(i64);
1748-
///
1749-
/// const _: () = {
1750-
/// let ptr = 40 as *mut AlignedI32;
1751-
/// assert!(ptr.is_aligned());
1752-
///
1753-
/// // For pointers with a known address, runtime and compiletime behavior are identical.
1754-
/// let ptr1 = ptr.cast::<AlignedI64>();
1755-
/// let ptr2 = ptr.wrapping_add(1).cast::<AlignedI64>();
1756-
/// assert!(ptr1.is_aligned());
1757-
/// assert!(!ptr2.is_aligned());
1758-
/// };
1759-
/// ```
1760-
///
1761-
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
17621676
#[must_use]
17631677
#[inline]
17641678
#[stable(feature = "pointer_is_aligned", since = "1.79.0")]
1765-
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1766-
pub const fn is_aligned(self) -> bool
1679+
pub fn is_aligned(self) -> bool
17671680
where
17681681
T: Sized,
17691682
{
@@ -1800,106 +1713,15 @@ impl<T: ?Sized> *mut T {
18001713
///
18011714
/// assert_ne!(ptr.is_aligned_to(8), ptr.wrapping_add(1).is_aligned_to(8));
18021715
/// ```
1803-
///
1804-
/// # At compiletime
1805-
/// **Note: Alignment at compiletime is experimental and subject to change. See the
1806-
/// [tracking issue] for details.**
1807-
///
1808-
/// At compiletime, the compiler may not know where a value will end up in memory.
1809-
/// Calling this function on a pointer created from a reference at compiletime will only
1810-
/// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1811-
/// cannot be stricter aligned than the reference's underlying allocation.
1812-
///
1813-
/// ```
1814-
/// #![feature(pointer_is_aligned_to)]
1815-
/// #![feature(const_pointer_is_aligned)]
1816-
///
1817-
/// // On some platforms, the alignment of i32 is less than 4.
1818-
/// #[repr(align(4))]
1819-
/// struct AlignedI32(i32);
1820-
///
1821-
/// const _: () = {
1822-
/// let mut data = AlignedI32(42);
1823-
/// let ptr = &mut data as *mut AlignedI32;
1824-
///
1825-
/// assert!(ptr.is_aligned_to(1));
1826-
/// assert!(ptr.is_aligned_to(2));
1827-
/// assert!(ptr.is_aligned_to(4));
1828-
///
1829-
/// // At compiletime, we know for sure that the pointer isn't aligned to 8.
1830-
/// assert!(!ptr.is_aligned_to(8));
1831-
/// assert!(!ptr.wrapping_add(1).is_aligned_to(8));
1832-
/// };
1833-
/// ```
1834-
///
1835-
/// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1836-
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
1837-
///
1838-
/// ```
1839-
/// #![feature(pointer_is_aligned_to)]
1840-
/// #![feature(const_pointer_is_aligned)]
1841-
///
1842-
/// // On some platforms, the alignment of i32 is less than 4.
1843-
/// #[repr(align(4))]
1844-
/// struct AlignedI32(i32);
1845-
///
1846-
/// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1847-
/// // Also, note that mutable references are not allowed in the final value of constants.
1848-
/// const COMPTIME_PTR: *mut AlignedI32 = (&AlignedI32(42) as *const AlignedI32).cast_mut();
1849-
/// const _: () = assert!(!COMPTIME_PTR.is_aligned_to(8));
1850-
/// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).is_aligned_to(8));
1851-
///
1852-
/// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1853-
/// let runtime_ptr = COMPTIME_PTR;
1854-
/// assert_ne!(
1855-
/// runtime_ptr.is_aligned_to(8),
1856-
/// runtime_ptr.wrapping_add(1).is_aligned_to(8),
1857-
/// );
1858-
/// ```
1859-
///
1860-
/// If a pointer is created from a fixed address, this function behaves the same during
1861-
/// runtime and compiletime.
1862-
///
1863-
/// ```
1864-
/// #![feature(pointer_is_aligned_to)]
1865-
/// #![feature(const_pointer_is_aligned)]
1866-
///
1867-
/// const _: () = {
1868-
/// let ptr = 40 as *mut u8;
1869-
/// assert!(ptr.is_aligned_to(1));
1870-
/// assert!(ptr.is_aligned_to(2));
1871-
/// assert!(ptr.is_aligned_to(4));
1872-
/// assert!(ptr.is_aligned_to(8));
1873-
/// assert!(!ptr.is_aligned_to(16));
1874-
/// };
1875-
/// ```
1876-
///
1877-
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
18781716
#[must_use]
18791717
#[inline]
18801718
#[unstable(feature = "pointer_is_aligned_to", issue = "96284")]
1881-
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1882-
pub const fn is_aligned_to(self, align: usize) -> bool {
1719+
pub fn is_aligned_to(self, align: usize) -> bool {
18831720
if !align.is_power_of_two() {
18841721
panic!("is_aligned_to: align is not a power-of-two");
18851722
}
18861723

1887-
#[inline]
1888-
fn runtime_impl(ptr: *mut (), align: usize) -> bool {
1889-
ptr.addr() & (align - 1) == 0
1890-
}
1891-
1892-
#[inline]
1893-
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1894-
const fn const_impl(ptr: *mut (), align: usize) -> bool {
1895-
// We can't use the address of `self` in a `const fn`, so we use `align_offset` instead.
1896-
ptr.align_offset(align) == 0
1897-
}
1898-
1899-
// The cast to `()` is used to
1900-
// 1. deal with fat pointers; and
1901-
// 2. ensure that `align_offset` (in `const_impl`) doesn't actually try to compute an offset.
1902-
const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl)
1724+
self.addr() & (align - 1) == 0
19031725
}
19041726
}
19051727

‎core/src/ptr/non_null.rs

+3-167
Original file line numberDiff line numberDiff line change
@@ -1192,8 +1192,7 @@ impl<T: ?Sized> NonNull<T> {
11921192
#[inline]
11931193
#[must_use]
11941194
#[stable(feature = "non_null_convenience", since = "1.80.0")]
1195-
#[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
1196-
pub const fn align_offset(self, align: usize) -> usize
1195+
pub fn align_offset(self, align: usize) -> usize
11971196
where
11981197
T: Sized,
11991198
{
@@ -1224,98 +1223,10 @@ impl<T: ?Sized> NonNull<T> {
12241223
/// assert!(ptr.is_aligned());
12251224
/// assert!(!NonNull::new(ptr.as_ptr().wrapping_byte_add(1)).unwrap().is_aligned());
12261225
/// ```
1227-
///
1228-
/// # At compiletime
1229-
/// **Note: Alignment at compiletime is experimental and subject to change. See the
1230-
/// [tracking issue] for details.**
1231-
///
1232-
/// At compiletime, the compiler may not know where a value will end up in memory.
1233-
/// Calling this function on a pointer created from a reference at compiletime will only
1234-
/// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1235-
/// is never aligned if cast to a type with a stricter alignment than the reference's
1236-
/// underlying allocation.
1237-
///
1238-
/// ```
1239-
/// #![feature(const_nonnull_new)]
1240-
/// #![feature(const_pointer_is_aligned)]
1241-
/// use std::ptr::NonNull;
1242-
///
1243-
/// // On some platforms, the alignment of primitives is less than their size.
1244-
/// #[repr(align(4))]
1245-
/// struct AlignedI32(i32);
1246-
/// #[repr(align(8))]
1247-
/// struct AlignedI64(i64);
1248-
///
1249-
/// const _: () = {
1250-
/// let data = [AlignedI32(42), AlignedI32(42)];
1251-
/// let ptr = NonNull::<AlignedI32>::new(&data[0] as *const _ as *mut _).unwrap();
1252-
/// assert!(ptr.is_aligned());
1253-
///
1254-
/// // At runtime either `ptr1` or `ptr2` would be aligned, but at compiletime neither is aligned.
1255-
/// let ptr1 = ptr.cast::<AlignedI64>();
1256-
/// let ptr2 = unsafe { ptr.add(1).cast::<AlignedI64>() };
1257-
/// assert!(!ptr1.is_aligned());
1258-
/// assert!(!ptr2.is_aligned());
1259-
/// };
1260-
/// ```
1261-
///
1262-
/// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1263-
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
1264-
///
1265-
/// ```
1266-
/// #![feature(const_pointer_is_aligned)]
1267-
///
1268-
/// // On some platforms, the alignment of primitives is less than their size.
1269-
/// #[repr(align(4))]
1270-
/// struct AlignedI32(i32);
1271-
/// #[repr(align(8))]
1272-
/// struct AlignedI64(i64);
1273-
///
1274-
/// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1275-
/// const COMPTIME_PTR: *const AlignedI32 = &AlignedI32(42);
1276-
/// const _: () = assert!(!COMPTIME_PTR.cast::<AlignedI64>().is_aligned());
1277-
/// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).cast::<AlignedI64>().is_aligned());
1278-
///
1279-
/// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1280-
/// let runtime_ptr = COMPTIME_PTR;
1281-
/// assert_ne!(
1282-
/// runtime_ptr.cast::<AlignedI64>().is_aligned(),
1283-
/// runtime_ptr.wrapping_add(1).cast::<AlignedI64>().is_aligned(),
1284-
/// );
1285-
/// ```
1286-
///
1287-
/// If a pointer is created from a fixed address, this function behaves the same during
1288-
/// runtime and compiletime.
1289-
///
1290-
/// ```
1291-
/// #![feature(const_pointer_is_aligned)]
1292-
/// #![feature(const_nonnull_new)]
1293-
/// use std::ptr::NonNull;
1294-
///
1295-
/// // On some platforms, the alignment of primitives is less than their size.
1296-
/// #[repr(align(4))]
1297-
/// struct AlignedI32(i32);
1298-
/// #[repr(align(8))]
1299-
/// struct AlignedI64(i64);
1300-
///
1301-
/// const _: () = {
1302-
/// let ptr = NonNull::new(40 as *mut AlignedI32).unwrap();
1303-
/// assert!(ptr.is_aligned());
1304-
///
1305-
/// // For pointers with a known address, runtime and compiletime behavior are identical.
1306-
/// let ptr1 = ptr.cast::<AlignedI64>();
1307-
/// let ptr2 = NonNull::new(ptr.as_ptr().wrapping_add(1)).unwrap().cast::<AlignedI64>();
1308-
/// assert!(ptr1.is_aligned());
1309-
/// assert!(!ptr2.is_aligned());
1310-
/// };
1311-
/// ```
1312-
///
1313-
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
13141226
#[inline]
13151227
#[must_use]
13161228
#[stable(feature = "pointer_is_aligned", since = "1.79.0")]
1317-
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1318-
pub const fn is_aligned(self) -> bool
1229+
pub fn is_aligned(self) -> bool
13191230
where
13201231
T: Sized,
13211232
{
@@ -1352,85 +1263,10 @@ impl<T: ?Sized> NonNull<T> {
13521263
///
13531264
/// assert_ne!(ptr.is_aligned_to(8), ptr.wrapping_add(1).is_aligned_to(8));
13541265
/// ```
1355-
///
1356-
/// # At compiletime
1357-
/// **Note: Alignment at compiletime is experimental and subject to change. See the
1358-
/// [tracking issue] for details.**
1359-
///
1360-
/// At compiletime, the compiler may not know where a value will end up in memory.
1361-
/// Calling this function on a pointer created from a reference at compiletime will only
1362-
/// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1363-
/// cannot be stricter aligned than the reference's underlying allocation.
1364-
///
1365-
/// ```
1366-
/// #![feature(pointer_is_aligned_to)]
1367-
/// #![feature(const_pointer_is_aligned)]
1368-
///
1369-
/// // On some platforms, the alignment of i32 is less than 4.
1370-
/// #[repr(align(4))]
1371-
/// struct AlignedI32(i32);
1372-
///
1373-
/// const _: () = {
1374-
/// let data = AlignedI32(42);
1375-
/// let ptr = &data as *const AlignedI32;
1376-
///
1377-
/// assert!(ptr.is_aligned_to(1));
1378-
/// assert!(ptr.is_aligned_to(2));
1379-
/// assert!(ptr.is_aligned_to(4));
1380-
///
1381-
/// // At compiletime, we know for sure that the pointer isn't aligned to 8.
1382-
/// assert!(!ptr.is_aligned_to(8));
1383-
/// assert!(!ptr.wrapping_add(1).is_aligned_to(8));
1384-
/// };
1385-
/// ```
1386-
///
1387-
/// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1388-
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
1389-
///
1390-
/// ```
1391-
/// #![feature(pointer_is_aligned_to)]
1392-
/// #![feature(const_pointer_is_aligned)]
1393-
///
1394-
/// // On some platforms, the alignment of i32 is less than 4.
1395-
/// #[repr(align(4))]
1396-
/// struct AlignedI32(i32);
1397-
///
1398-
/// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1399-
/// const COMPTIME_PTR: *const AlignedI32 = &AlignedI32(42);
1400-
/// const _: () = assert!(!COMPTIME_PTR.is_aligned_to(8));
1401-
/// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).is_aligned_to(8));
1402-
///
1403-
/// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1404-
/// let runtime_ptr = COMPTIME_PTR;
1405-
/// assert_ne!(
1406-
/// runtime_ptr.is_aligned_to(8),
1407-
/// runtime_ptr.wrapping_add(1).is_aligned_to(8),
1408-
/// );
1409-
/// ```
1410-
///
1411-
/// If a pointer is created from a fixed address, this function behaves the same during
1412-
/// runtime and compiletime.
1413-
///
1414-
/// ```
1415-
/// #![feature(pointer_is_aligned_to)]
1416-
/// #![feature(const_pointer_is_aligned)]
1417-
///
1418-
/// const _: () = {
1419-
/// let ptr = 40 as *const u8;
1420-
/// assert!(ptr.is_aligned_to(1));
1421-
/// assert!(ptr.is_aligned_to(2));
1422-
/// assert!(ptr.is_aligned_to(4));
1423-
/// assert!(ptr.is_aligned_to(8));
1424-
/// assert!(!ptr.is_aligned_to(16));
1425-
/// };
1426-
/// ```
1427-
///
1428-
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
14291266
#[inline]
14301267
#[must_use]
14311268
#[unstable(feature = "pointer_is_aligned_to", issue = "96284")]
1432-
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1433-
pub const fn is_aligned_to(self, align: usize) -> bool {
1269+
pub fn is_aligned_to(self, align: usize) -> bool {
14341270
self.pointer.is_aligned_to(align)
14351271
}
14361272
}

‎core/src/slice/ascii.rs

+79-74
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use core::ascii::EscapeDefault;
44

55
use crate::fmt::{self, Write};
6+
use crate::intrinsics::const_eval_select;
67
use crate::{ascii, iter, mem, ops};
78

89
#[cfg(not(test))]
@@ -346,89 +347,93 @@ pub const fn is_ascii_simple(mut bytes: &[u8]) -> bool {
346347
/// If any of these loads produces something for which `contains_nonascii`
347348
/// (above) returns true, then we know the answer is false.
348349
#[inline]
349-
#[rustc_allow_const_fn_unstable(const_raw_ptr_comparison, const_pointer_is_aligned)] // only in a debug assertion
350-
#[rustc_allow_const_fn_unstable(const_align_offset)] // behavior does not change when `align_offset` fails
350+
#[rustc_allow_const_fn_unstable(const_eval_select)] // fallback impl has same behavior
351351
const fn is_ascii(s: &[u8]) -> bool {
352-
const USIZE_SIZE: usize = mem::size_of::<usize>();
353-
354-
let len = s.len();
355-
let align_offset = s.as_ptr().align_offset(USIZE_SIZE);
356-
357-
// If we wouldn't gain anything from the word-at-a-time implementation, fall
358-
// back to a scalar loop.
359-
//
360-
// We also do this for architectures where `size_of::<usize>()` isn't
361-
// sufficient alignment for `usize`, because it's a weird edge case.
362-
if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::<usize>() {
363-
return is_ascii_simple(s);
352+
// The runtime version behaves the same as the compiletime version, it's
353+
// just more optimized.
354+
return const_eval_select((s,), compiletime, runtime);
355+
356+
const fn compiletime(s: &[u8]) -> bool {
357+
is_ascii_simple(s)
364358
}
365359

366-
// We always read the first word unaligned, which means `align_offset` is
367-
// 0, we'd read the same value again for the aligned read.
368-
let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset };
360+
#[inline]
361+
fn runtime(s: &[u8]) -> bool {
362+
const USIZE_SIZE: usize = mem::size_of::<usize>();
363+
364+
let len = s.len();
365+
let align_offset = s.as_ptr().align_offset(USIZE_SIZE);
366+
367+
// If we wouldn't gain anything from the word-at-a-time implementation, fall
368+
// back to a scalar loop.
369+
//
370+
// We also do this for architectures where `size_of::<usize>()` isn't
371+
// sufficient alignment for `usize`, because it's a weird edge case.
372+
if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::<usize>() {
373+
return is_ascii_simple(s);
374+
}
369375

370-
let start = s.as_ptr();
371-
// SAFETY: We verify `len < USIZE_SIZE` above.
372-
let first_word = unsafe { (start as *const usize).read_unaligned() };
376+
// We always read the first word unaligned, which means `align_offset` is
377+
// 0, we'd read the same value again for the aligned read.
378+
let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset };
373379

374-
if contains_nonascii(first_word) {
375-
return false;
376-
}
377-
// We checked this above, somewhat implicitly. Note that `offset_to_aligned`
378-
// is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked
379-
// above.
380-
debug_assert!(offset_to_aligned <= len);
381-
382-
// SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the
383-
// middle chunk of the slice.
384-
let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize };
385-
386-
// `byte_pos` is the byte index of `word_ptr`, used for loop end checks.
387-
let mut byte_pos = offset_to_aligned;
388-
389-
// Paranoia check about alignment, since we're about to do a bunch of
390-
// unaligned loads. In practice this should be impossible barring a bug in
391-
// `align_offset` though.
392-
// While this method is allowed to spuriously fail in CTFE, if it doesn't
393-
// have alignment information it should have given a `usize::MAX` for
394-
// `align_offset` earlier, sending things through the scalar path instead of
395-
// this one, so this check should pass if it's reachable.
396-
debug_assert!(word_ptr.is_aligned_to(mem::align_of::<usize>()));
397-
398-
// Read subsequent words until the last aligned word, excluding the last
399-
// aligned word by itself to be done in tail check later, to ensure that
400-
// tail is always one `usize` at most to extra branch `byte_pos == len`.
401-
while byte_pos < len - USIZE_SIZE {
402-
// Sanity check that the read is in bounds
403-
debug_assert!(byte_pos + USIZE_SIZE <= len);
404-
// And that our assumptions about `byte_pos` hold.
405-
debug_assert!(matches!(
406-
word_ptr.cast::<u8>().guaranteed_eq(start.wrapping_add(byte_pos)),
407-
// These are from the same allocation, so will hopefully always be
408-
// known to match even in CTFE, but if it refuses to compare them
409-
// that's ok since it's just a debug check anyway.
410-
None | Some(true),
411-
));
412-
413-
// SAFETY: We know `word_ptr` is properly aligned (because of
414-
// `align_offset`), and we know that we have enough bytes between `word_ptr` and the end
415-
let word = unsafe { word_ptr.read() };
416-
if contains_nonascii(word) {
380+
let start = s.as_ptr();
381+
// SAFETY: We verify `len < USIZE_SIZE` above.
382+
let first_word = unsafe { (start as *const usize).read_unaligned() };
383+
384+
if contains_nonascii(first_word) {
417385
return false;
418386
}
387+
// We checked this above, somewhat implicitly. Note that `offset_to_aligned`
388+
// is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked
389+
// above.
390+
debug_assert!(offset_to_aligned <= len);
391+
392+
// SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the
393+
// middle chunk of the slice.
394+
let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize };
395+
396+
// `byte_pos` is the byte index of `word_ptr`, used for loop end checks.
397+
let mut byte_pos = offset_to_aligned;
398+
399+
// Paranoia check about alignment, since we're about to do a bunch of
400+
// unaligned loads. In practice this should be impossible barring a bug in
401+
// `align_offset` though.
402+
// While this method is allowed to spuriously fail in CTFE, if it doesn't
403+
// have alignment information it should have given a `usize::MAX` for
404+
// `align_offset` earlier, sending things through the scalar path instead of
405+
// this one, so this check should pass if it's reachable.
406+
debug_assert!(word_ptr.is_aligned_to(mem::align_of::<usize>()));
407+
408+
// Read subsequent words until the last aligned word, excluding the last
409+
// aligned word by itself to be done in tail check later, to ensure that
410+
// tail is always one `usize` at most to extra branch `byte_pos == len`.
411+
while byte_pos < len - USIZE_SIZE {
412+
// Sanity check that the read is in bounds
413+
debug_assert!(byte_pos + USIZE_SIZE <= len);
414+
// And that our assumptions about `byte_pos` hold.
415+
debug_assert!(word_ptr.cast::<u8>() == start.wrapping_add(byte_pos));
416+
417+
// SAFETY: We know `word_ptr` is properly aligned (because of
418+
// `align_offset`), and we know that we have enough bytes between `word_ptr` and the end
419+
let word = unsafe { word_ptr.read() };
420+
if contains_nonascii(word) {
421+
return false;
422+
}
419423

420-
byte_pos += USIZE_SIZE;
421-
// SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that
422-
// after this `add`, `word_ptr` will be at most one-past-the-end.
423-
word_ptr = unsafe { word_ptr.add(1) };
424-
}
424+
byte_pos += USIZE_SIZE;
425+
// SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that
426+
// after this `add`, `word_ptr` will be at most one-past-the-end.
427+
word_ptr = unsafe { word_ptr.add(1) };
428+
}
425429

426-
// Sanity check to ensure there really is only one `usize` left. This should
427-
// be guaranteed by our loop condition.
428-
debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE);
430+
// Sanity check to ensure there really is only one `usize` left. This should
431+
// be guaranteed by our loop condition.
432+
debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE);
429433

430-
// SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start.
431-
let last_word = unsafe { (start.add(len - USIZE_SIZE) as *const usize).read_unaligned() };
434+
// SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start.
435+
let last_word = unsafe { (start.add(len - USIZE_SIZE) as *const usize).read_unaligned() };
432436

433-
!contains_nonascii(last_word)
437+
!contains_nonascii(last_word)
438+
}
434439
}

‎core/src/slice/memchr.rs

+52-43
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Original implementation taken from rust-memchr.
22
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
33

4+
use crate::intrinsics::const_eval_select;
45
use crate::mem;
56

67
const LO_USIZE: usize = usize::repeat_u8(0x01);
@@ -50,58 +51,66 @@ const fn memchr_naive(x: u8, text: &[u8]) -> Option<usize> {
5051
None
5152
}
5253

53-
#[rustc_allow_const_fn_unstable(const_cmp)]
54-
#[rustc_allow_const_fn_unstable(const_align_offset)]
54+
#[rustc_allow_const_fn_unstable(const_eval_select)] // fallback impl has same behavior
5555
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_memchr", since = "1.65.0"))]
5656
const fn memchr_aligned(x: u8, text: &[u8]) -> Option<usize> {
57-
// Scan for a single byte value by reading two `usize` words at a time.
58-
//
59-
// Split `text` in three parts
60-
// - unaligned initial part, before the first word aligned address in text
61-
// - body, scan by 2 words at a time
62-
// - the last remaining part, < 2 word size
63-
64-
// search up to an aligned boundary
65-
let len = text.len();
66-
let ptr = text.as_ptr();
67-
let mut offset = ptr.align_offset(USIZE_BYTES);
57+
// The runtime version behaves the same as the compiletime version, it's
58+
// just more optimized.
59+
return const_eval_select((x, text), compiletime, runtime);
6860

69-
if offset > 0 {
70-
// FIXME(const-hack, fee1-dead): replace with min
71-
offset = if offset < len { offset } else { len };
72-
// FIXME(const-hack, fee1-dead): replace with range slicing
73-
// SAFETY: offset is within bounds
74-
let slice = unsafe { super::from_raw_parts(text.as_ptr(), offset) };
75-
if let Some(index) = memchr_naive(x, slice) {
76-
return Some(index);
77-
}
61+
const fn compiletime(x: u8, text: &[u8]) -> Option<usize> {
62+
memchr_naive(x, text)
7863
}
7964

80-
// search the body of the text
81-
let repeated_x = usize::repeat_u8(x);
82-
while offset <= len - 2 * USIZE_BYTES {
83-
// SAFETY: the while's predicate guarantees a distance of at least 2 * usize_bytes
84-
// between the offset and the end of the slice.
85-
unsafe {
86-
let u = *(ptr.add(offset) as *const usize);
87-
let v = *(ptr.add(offset + USIZE_BYTES) as *const usize);
65+
#[inline]
66+
fn runtime(x: u8, text: &[u8]) -> Option<usize> {
67+
// Scan for a single byte value by reading two `usize` words at a time.
68+
//
69+
// Split `text` in three parts
70+
// - unaligned initial part, before the first word aligned address in text
71+
// - body, scan by 2 words at a time
72+
// - the last remaining part, < 2 word size
73+
74+
// search up to an aligned boundary
75+
let len = text.len();
76+
let ptr = text.as_ptr();
77+
let mut offset = ptr.align_offset(USIZE_BYTES);
78+
79+
if offset > 0 {
80+
offset = offset.min(len);
81+
let slice = &text[..offset];
82+
if let Some(index) = memchr_naive(x, slice) {
83+
return Some(index);
84+
}
85+
}
8886

89-
// break if there is a matching byte
90-
let zu = contains_zero_byte(u ^ repeated_x);
91-
let zv = contains_zero_byte(v ^ repeated_x);
92-
if zu || zv {
93-
break;
87+
// search the body of the text
88+
let repeated_x = usize::repeat_u8(x);
89+
while offset <= len - 2 * USIZE_BYTES {
90+
// SAFETY: the while's predicate guarantees a distance of at least 2 * usize_bytes
91+
// between the offset and the end of the slice.
92+
unsafe {
93+
let u = *(ptr.add(offset) as *const usize);
94+
let v = *(ptr.add(offset + USIZE_BYTES) as *const usize);
95+
96+
// break if there is a matching byte
97+
let zu = contains_zero_byte(u ^ repeated_x);
98+
let zv = contains_zero_byte(v ^ repeated_x);
99+
if zu || zv {
100+
break;
101+
}
94102
}
103+
offset += USIZE_BYTES * 2;
95104
}
96-
offset += USIZE_BYTES * 2;
97-
}
98105

99-
// Find the byte after the point the body loop stopped.
100-
// FIXME(const-hack): Use `?` instead.
101-
// FIXME(const-hack, fee1-dead): use range slicing
102-
// SAFETY: offset is within bounds
103-
let slice = unsafe { super::from_raw_parts(text.as_ptr().add(offset), text.len() - offset) };
104-
if let Some(i) = memchr_naive(x, slice) { Some(offset + i) } else { None }
106+
// Find the byte after the point the body loop stopped.
107+
// FIXME(const-hack): Use `?` instead.
108+
// FIXME(const-hack, fee1-dead): use range slicing
109+
let slice =
110+
// SAFETY: offset is within bounds
111+
unsafe { super::from_raw_parts(text.as_ptr().add(offset), text.len() - offset) };
112+
if let Some(i) = memchr_naive(x, slice) { Some(offset + i) } else { None }
113+
}
105114
}
106115

107116
/// Returns the last index matching the byte `x` in `text`.

‎core/src/str/converts.rs

-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ use crate::{mem, ptr};
8282
/// ```
8383
#[stable(feature = "rust1", since = "1.0.0")]
8484
#[rustc_const_stable(feature = "const_str_from_utf8_shared", since = "1.63.0")]
85-
#[rustc_allow_const_fn_unstable(str_internals)]
8685
#[rustc_diagnostic_item = "str_from_utf8"]
8786
pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> {
8887
// FIXME(const-hack): This should use `?` again, once it's `const`

‎core/src/str/validations.rs

+20-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Operations related to UTF-8 validation.
22
33
use super::Utf8Error;
4+
use crate::intrinsics::const_eval_select;
45
use crate::mem;
56

67
/// Returns the initial codepoint accumulator for the first byte.
@@ -122,15 +123,28 @@ const fn contains_nonascii(x: usize) -> bool {
122123
/// Walks through `v` checking that it's a valid UTF-8 sequence,
123124
/// returning `Ok(())` in that case, or, if it is invalid, `Err(err)`.
124125
#[inline(always)]
125-
#[rustc_const_unstable(feature = "str_internals", issue = "none")]
126+
#[rustc_allow_const_fn_unstable(const_eval_select)] // fallback impl has same behavior
126127
pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
127128
let mut index = 0;
128129
let len = v.len();
129130

130-
let usize_bytes = mem::size_of::<usize>();
131-
let ascii_block_size = 2 * usize_bytes;
131+
const USIZE_BYTES: usize = mem::size_of::<usize>();
132+
133+
let ascii_block_size = 2 * USIZE_BYTES;
132134
let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 };
133-
let align = v.as_ptr().align_offset(usize_bytes);
135+
let align = {
136+
const fn compiletime(_v: &[u8]) -> usize {
137+
usize::MAX
138+
}
139+
140+
fn runtime(v: &[u8]) -> usize {
141+
v.as_ptr().align_offset(USIZE_BYTES)
142+
}
143+
144+
// Below, we safely fall back to a slower codepath if the offset is `usize::MAX`,
145+
// so the end-to-end behavior is the same at compiletime and runtime.
146+
const_eval_select((v,), compiletime, runtime)
147+
};
134148

135149
while index < len {
136150
let old_offset = index;
@@ -209,11 +223,11 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
209223
// Ascii case, try to skip forward quickly.
210224
// When the pointer is aligned, read 2 words of data per iteration
211225
// until we find a word containing a non-ascii byte.
212-
if align != usize::MAX && align.wrapping_sub(index) % usize_bytes == 0 {
226+
if align != usize::MAX && align.wrapping_sub(index) % USIZE_BYTES == 0 {
213227
let ptr = v.as_ptr();
214228
while index < blocks_end {
215229
// SAFETY: since `align - index` and `ascii_block_size` are
216-
// multiples of `usize_bytes`, `block = ptr.add(index)` is
230+
// multiples of `USIZE_BYTES`, `block = ptr.add(index)` is
217231
// always aligned with a `usize` so it's safe to dereference
218232
// both `block` and `block.add(1)`.
219233
unsafe {

‎core/src/ub_checks.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,19 @@ pub(crate) const fn check_language_ub() -> bool {
120120
#[inline]
121121
#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
122122
pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize, is_zst: bool) -> bool {
123-
ptr.is_aligned_to(align) && (is_zst || !ptr.is_null())
123+
#[inline]
124+
fn runtime(ptr: *const (), align: usize, is_zst: bool) -> bool {
125+
ptr.is_aligned_to(align) && (is_zst || !ptr.is_null())
126+
}
127+
128+
#[inline]
129+
#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
130+
const fn comptime(ptr: *const (), _align: usize, is_zst: bool) -> bool {
131+
is_zst || !ptr.is_null()
132+
}
133+
134+
// This is just for safety checks so we can const_eval_select.
135+
const_eval_select((ptr, align, is_zst), comptime, runtime)
124136
}
125137

126138
#[inline]

‎core/tests/lib.rs

-2
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@
1616
#![feature(cell_update)]
1717
#![feature(clone_to_uninit)]
1818
#![feature(const_align_of_val_raw)]
19-
#![feature(const_align_offset)]
2019
#![feature(const_black_box)]
2120
#![feature(const_eval_select)]
2221
#![feature(const_heap)]
2322
#![feature(const_nonnull_new)]
2423
#![feature(const_option_ext)]
2524
#![feature(const_pin_2)]
26-
#![feature(const_pointer_is_aligned)]
2725
#![feature(const_three_way_compare)]
2826
#![feature(const_trait_impl)]
2927
#![feature(core_intrinsics)]

‎core/tests/ptr.rs

-257
Original file line numberDiff line numberDiff line change
@@ -359,22 +359,6 @@ fn align_offset_zst() {
359359
}
360360
}
361361

362-
#[test]
363-
fn align_offset_zst_const() {
364-
const {
365-
// For pointers of stride = 0, the pointer is already aligned or it cannot be aligned at
366-
// all, because no amount of elements will align the pointer.
367-
let mut p = 1;
368-
while p < 1024 {
369-
assert!(ptr::without_provenance::<()>(p).align_offset(p) == 0);
370-
if p != 1 {
371-
assert!(ptr::without_provenance::<()>(p + 1).align_offset(p) == !0);
372-
}
373-
p = (p + 1).next_power_of_two();
374-
}
375-
}
376-
}
377-
378362
#[test]
379363
fn align_offset_stride_one() {
380364
// For pointers of stride = 1, the pointer can always be aligned. The offset is equal to
@@ -396,25 +380,6 @@ fn align_offset_stride_one() {
396380
}
397381
}
398382

399-
#[test]
400-
fn align_offset_stride_one_const() {
401-
const {
402-
// For pointers of stride = 1, the pointer can always be aligned. The offset is equal to
403-
// number of bytes.
404-
let mut align = 1;
405-
while align < 1024 {
406-
let mut ptr = 1;
407-
while ptr < 2 * align {
408-
let expected = ptr % align;
409-
let offset = if expected == 0 { 0 } else { align - expected };
410-
assert!(ptr::without_provenance::<u8>(ptr).align_offset(align) == offset);
411-
ptr += 1;
412-
}
413-
align = (align + 1).next_power_of_two();
414-
}
415-
}
416-
}
417-
418383
#[test]
419384
fn align_offset_various_strides() {
420385
unsafe fn test_stride<T>(ptr: *const T, align: usize) -> bool {
@@ -495,192 +460,6 @@ fn align_offset_various_strides() {
495460
assert!(!x);
496461
}
497462

498-
#[test]
499-
fn align_offset_various_strides_const() {
500-
const unsafe fn test_stride<T>(ptr: *const T, numptr: usize, align: usize) {
501-
let mut expected = usize::MAX;
502-
// Naive but definitely correct way to find the *first* aligned element of stride::<T>.
503-
let mut el = 0;
504-
while el < align {
505-
if (numptr + el * ::std::mem::size_of::<T>()) % align == 0 {
506-
expected = el;
507-
break;
508-
}
509-
el += 1;
510-
}
511-
let got = ptr.align_offset(align);
512-
assert!(got == expected);
513-
}
514-
515-
const {
516-
// For pointers of stride != 1, we verify the algorithm against the naivest possible
517-
// implementation
518-
let mut align = 1;
519-
let limit = 32;
520-
while align < limit {
521-
let mut ptr = 1;
522-
while ptr < 4 * align {
523-
unsafe {
524-
#[repr(packed)]
525-
struct A3(#[allow(dead_code)] u16, #[allow(dead_code)] u8);
526-
test_stride::<A3>(ptr::without_provenance::<A3>(ptr), ptr, align);
527-
528-
struct A4(#[allow(dead_code)] u32);
529-
test_stride::<A4>(ptr::without_provenance::<A4>(ptr), ptr, align);
530-
531-
#[repr(packed)]
532-
struct A5(#[allow(dead_code)] u32, #[allow(dead_code)] u8);
533-
test_stride::<A5>(ptr::without_provenance::<A5>(ptr), ptr, align);
534-
535-
#[repr(packed)]
536-
struct A6(#[allow(dead_code)] u32, #[allow(dead_code)] u16);
537-
test_stride::<A6>(ptr::without_provenance::<A6>(ptr), ptr, align);
538-
539-
#[repr(packed)]
540-
struct A7(
541-
#[allow(dead_code)] u32,
542-
#[allow(dead_code)] u16,
543-
#[allow(dead_code)] u8,
544-
);
545-
test_stride::<A7>(ptr::without_provenance::<A7>(ptr), ptr, align);
546-
547-
#[repr(packed)]
548-
struct A8(#[allow(dead_code)] u32, #[allow(dead_code)] u32);
549-
test_stride::<A8>(ptr::without_provenance::<A8>(ptr), ptr, align);
550-
551-
#[repr(packed)]
552-
struct A9(
553-
#[allow(dead_code)] u32,
554-
#[allow(dead_code)] u32,
555-
#[allow(dead_code)] u8,
556-
);
557-
test_stride::<A9>(ptr::without_provenance::<A9>(ptr), ptr, align);
558-
559-
#[repr(packed)]
560-
struct A10(
561-
#[allow(dead_code)] u32,
562-
#[allow(dead_code)] u32,
563-
#[allow(dead_code)] u16,
564-
);
565-
test_stride::<A10>(ptr::without_provenance::<A10>(ptr), ptr, align);
566-
567-
test_stride::<u32>(ptr::without_provenance::<u32>(ptr), ptr, align);
568-
test_stride::<u128>(ptr::without_provenance::<u128>(ptr), ptr, align);
569-
}
570-
ptr += 1;
571-
}
572-
align = (align + 1).next_power_of_two();
573-
}
574-
}
575-
}
576-
577-
#[test]
578-
fn align_offset_with_provenance_const() {
579-
const {
580-
// On some platforms (e.g. msp430-none-elf), the alignment of `i32` is less than 4.
581-
#[repr(align(4))]
582-
struct AlignedI32(i32);
583-
584-
let data = AlignedI32(42);
585-
586-
// `stride % align == 0` (usual case)
587-
588-
let ptr: *const i32 = &data.0;
589-
assert!(ptr.align_offset(1) == 0);
590-
assert!(ptr.align_offset(2) == 0);
591-
assert!(ptr.align_offset(4) == 0);
592-
assert!(ptr.align_offset(8) == usize::MAX);
593-
assert!(ptr.wrapping_byte_add(1).align_offset(1) == 0);
594-
assert!(ptr.wrapping_byte_add(1).align_offset(2) == usize::MAX);
595-
assert!(ptr.wrapping_byte_add(2).align_offset(1) == 0);
596-
assert!(ptr.wrapping_byte_add(2).align_offset(2) == 0);
597-
assert!(ptr.wrapping_byte_add(2).align_offset(4) == usize::MAX);
598-
assert!(ptr.wrapping_byte_add(3).align_offset(1) == 0);
599-
assert!(ptr.wrapping_byte_add(3).align_offset(2) == usize::MAX);
600-
601-
assert!(ptr.wrapping_add(42).align_offset(4) == 0);
602-
assert!(ptr.wrapping_add(42).align_offset(8) == usize::MAX);
603-
604-
let ptr1: *const i8 = ptr.cast();
605-
assert!(ptr1.align_offset(1) == 0);
606-
assert!(ptr1.align_offset(2) == 0);
607-
assert!(ptr1.align_offset(4) == 0);
608-
assert!(ptr1.align_offset(8) == usize::MAX);
609-
assert!(ptr1.wrapping_byte_add(1).align_offset(1) == 0);
610-
assert!(ptr1.wrapping_byte_add(1).align_offset(2) == 1);
611-
assert!(ptr1.wrapping_byte_add(1).align_offset(4) == 3);
612-
assert!(ptr1.wrapping_byte_add(1).align_offset(8) == usize::MAX);
613-
assert!(ptr1.wrapping_byte_add(2).align_offset(1) == 0);
614-
assert!(ptr1.wrapping_byte_add(2).align_offset(2) == 0);
615-
assert!(ptr1.wrapping_byte_add(2).align_offset(4) == 2);
616-
assert!(ptr1.wrapping_byte_add(2).align_offset(8) == usize::MAX);
617-
assert!(ptr1.wrapping_byte_add(3).align_offset(1) == 0);
618-
assert!(ptr1.wrapping_byte_add(3).align_offset(2) == 1);
619-
assert!(ptr1.wrapping_byte_add(3).align_offset(4) == 1);
620-
assert!(ptr1.wrapping_byte_add(3).align_offset(8) == usize::MAX);
621-
622-
let ptr2: *const i16 = ptr.cast();
623-
assert!(ptr2.align_offset(1) == 0);
624-
assert!(ptr2.align_offset(2) == 0);
625-
assert!(ptr2.align_offset(4) == 0);
626-
assert!(ptr2.align_offset(8) == usize::MAX);
627-
assert!(ptr2.wrapping_byte_add(1).align_offset(1) == 0);
628-
assert!(ptr2.wrapping_byte_add(1).align_offset(2) == usize::MAX);
629-
assert!(ptr2.wrapping_byte_add(2).align_offset(1) == 0);
630-
assert!(ptr2.wrapping_byte_add(2).align_offset(2) == 0);
631-
assert!(ptr2.wrapping_byte_add(2).align_offset(4) == 1);
632-
assert!(ptr2.wrapping_byte_add(2).align_offset(8) == usize::MAX);
633-
assert!(ptr2.wrapping_byte_add(3).align_offset(1) == 0);
634-
assert!(ptr2.wrapping_byte_add(3).align_offset(2) == usize::MAX);
635-
636-
let ptr3: *const i64 = ptr.cast();
637-
assert!(ptr3.align_offset(1) == 0);
638-
assert!(ptr3.align_offset(2) == 0);
639-
assert!(ptr3.align_offset(4) == 0);
640-
assert!(ptr3.align_offset(8) == usize::MAX);
641-
assert!(ptr3.wrapping_byte_add(1).align_offset(1) == 0);
642-
assert!(ptr3.wrapping_byte_add(1).align_offset(2) == usize::MAX);
643-
644-
// `stride % align != 0` (edge case)
645-
646-
let ptr4: *const [u8; 3] = ptr.cast();
647-
assert!(ptr4.align_offset(1) == 0);
648-
assert!(ptr4.align_offset(2) == 0);
649-
assert!(ptr4.align_offset(4) == 0);
650-
assert!(ptr4.align_offset(8) == usize::MAX);
651-
assert!(ptr4.wrapping_byte_add(1).align_offset(1) == 0);
652-
assert!(ptr4.wrapping_byte_add(1).align_offset(2) == 1);
653-
assert!(ptr4.wrapping_byte_add(1).align_offset(4) == 1);
654-
assert!(ptr4.wrapping_byte_add(1).align_offset(8) == usize::MAX);
655-
assert!(ptr4.wrapping_byte_add(2).align_offset(1) == 0);
656-
assert!(ptr4.wrapping_byte_add(2).align_offset(2) == 0);
657-
assert!(ptr4.wrapping_byte_add(2).align_offset(4) == 2);
658-
assert!(ptr4.wrapping_byte_add(2).align_offset(8) == usize::MAX);
659-
assert!(ptr4.wrapping_byte_add(3).align_offset(1) == 0);
660-
assert!(ptr4.wrapping_byte_add(3).align_offset(2) == 1);
661-
assert!(ptr4.wrapping_byte_add(3).align_offset(4) == 3);
662-
assert!(ptr4.wrapping_byte_add(3).align_offset(8) == usize::MAX);
663-
664-
let ptr5: *const [u8; 5] = ptr.cast();
665-
assert!(ptr5.align_offset(1) == 0);
666-
assert!(ptr5.align_offset(2) == 0);
667-
assert!(ptr5.align_offset(4) == 0);
668-
assert!(ptr5.align_offset(8) == usize::MAX);
669-
assert!(ptr5.wrapping_byte_add(1).align_offset(1) == 0);
670-
assert!(ptr5.wrapping_byte_add(1).align_offset(2) == 1);
671-
assert!(ptr5.wrapping_byte_add(1).align_offset(4) == 3);
672-
assert!(ptr5.wrapping_byte_add(1).align_offset(8) == usize::MAX);
673-
assert!(ptr5.wrapping_byte_add(2).align_offset(1) == 0);
674-
assert!(ptr5.wrapping_byte_add(2).align_offset(2) == 0);
675-
assert!(ptr5.wrapping_byte_add(2).align_offset(4) == 2);
676-
assert!(ptr5.wrapping_byte_add(2).align_offset(8) == usize::MAX);
677-
assert!(ptr5.wrapping_byte_add(3).align_offset(1) == 0);
678-
assert!(ptr5.wrapping_byte_add(3).align_offset(2) == 1);
679-
assert!(ptr5.wrapping_byte_add(3).align_offset(4) == 1);
680-
assert!(ptr5.wrapping_byte_add(3).align_offset(8) == usize::MAX);
681-
}
682-
}
683-
684463
#[test]
685464
fn align_offset_issue_103361() {
686465
#[cfg(target_pointer_width = "64")]
@@ -693,23 +472,6 @@ fn align_offset_issue_103361() {
693472
let _ = ptr::without_provenance::<HugeSize>(SIZE).align_offset(SIZE);
694473
}
695474

696-
#[test]
697-
fn align_offset_issue_103361_const() {
698-
#[cfg(target_pointer_width = "64")]
699-
const SIZE: usize = 1 << 47;
700-
#[cfg(target_pointer_width = "32")]
701-
const SIZE: usize = 1 << 30;
702-
#[cfg(target_pointer_width = "16")]
703-
const SIZE: usize = 1 << 13;
704-
struct HugeSize(#[allow(dead_code)] [u8; SIZE - 1]);
705-
706-
const {
707-
assert!(ptr::without_provenance::<HugeSize>(SIZE - 1).align_offset(SIZE) == SIZE - 1);
708-
assert!(ptr::without_provenance::<HugeSize>(SIZE).align_offset(SIZE) == 0);
709-
assert!(ptr::without_provenance::<HugeSize>(SIZE + 1).align_offset(SIZE) == 1);
710-
}
711-
}
712-
713475
#[test]
714476
fn is_aligned() {
715477
let data = 42;
@@ -726,25 +488,6 @@ fn is_aligned() {
726488
assert_ne!(ptr.is_aligned_to(8), ptr.wrapping_add(1).is_aligned_to(8));
727489
}
728490

729-
#[test]
730-
fn is_aligned_const() {
731-
const {
732-
let data = 42;
733-
let ptr: *const i32 = &data;
734-
assert!(ptr.is_aligned());
735-
assert!(ptr.is_aligned_to(1));
736-
assert!(ptr.is_aligned_to(2));
737-
assert!(ptr.is_aligned_to(4));
738-
assert!(ptr.wrapping_byte_add(2).is_aligned_to(1));
739-
assert!(ptr.wrapping_byte_add(2).is_aligned_to(2));
740-
assert!(!ptr.wrapping_byte_add(2).is_aligned_to(4));
741-
742-
// At comptime neither `ptr` nor `ptr+1` is aligned to 8.
743-
assert!(!ptr.is_aligned_to(8));
744-
assert!(!ptr.wrapping_add(1).is_aligned_to(8));
745-
}
746-
}
747-
748491
#[test]
749492
fn offset_from() {
750493
let mut a = [0; 5];

0 commit comments

Comments
 (0)
Please sign in to comment.