@@ -1320,6 +1320,8 @@ impl<T: ?Sized> *const T {
13201320 /// }
13211321 /// # }
13221322 /// ```
1323+ #[ must_use]
1324+ #[ inline]
13231325 #[ stable( feature = "align_offset" , since = "1.36.0" ) ]
13241326 #[ rustc_const_unstable( feature = "const_align_offset" , issue = "90962" ) ]
13251327 pub const fn align_offset ( self , align : usize ) -> usize
@@ -1330,32 +1332,149 @@ impl<T: ?Sized> *const T {
13301332 panic ! ( "align_offset: align is not a power-of-two" ) ;
13311333 }
13321334
1333- fn rt_impl < T > ( p : * const T , align : usize ) -> usize {
1334- // SAFETY: `align` has been checked to be a power of 2 above
1335- unsafe { align_offset ( p, align) }
1336- }
1335+ #[ cfg( bootstrap) ]
1336+ {
1337+ fn rt_impl < T > ( p : * const T , align : usize ) -> usize {
1338+ // SAFETY: `align` has been checked to be a power of 2 above
1339+ unsafe { align_offset ( p, align) }
1340+ }
1341+
1342+ const fn ctfe_impl < T > ( _: * const T , _: usize ) -> usize {
1343+ usize:: MAX
1344+ }
13371345
1338- const fn ctfe_impl < T > ( _: * const T , _: usize ) -> usize {
1339- usize:: MAX
1346+ // SAFETY:
1347+ // It is permissible for `align_offset` to always return `usize::MAX`,
1348+ // algorithm correctness can not depend on `align_offset` returning non-max values.
1349+ //
1350+ // As such the behaviour can't change after replacing `align_offset` with `usize::MAX`, only performance can.
1351+ unsafe { intrinsics:: const_eval_select ( ( self , align) , ctfe_impl, rt_impl) }
13401352 }
13411353
1342- // SAFETY:
1343- // It is permissible for `align_offset` to always return `usize::MAX`,
1344- // algorithm correctness can not depend on `align_offset` returning non-max values.
1345- //
1346- // As such the behaviour can't change after replacing `align_offset` with `usize::MAX`, only performance can.
1347- unsafe { intrinsics:: const_eval_select ( ( self , align) , ctfe_impl, rt_impl) }
1354+ #[ cfg( not( bootstrap) ) ]
1355+ {
1356+ // SAFETY: `align` has been checked to be a power of 2 above
1357+ unsafe { align_offset ( self , align) }
1358+ }
13481359 }
13491360
13501361 /// Returns whether the pointer is properly aligned for `T`.
1362+ ///
1363+ /// # Examples
1364+ ///
1365+ /// Basic usage:
1366+ /// ```
1367+ /// #![feature(pointer_is_aligned)]
1368+ /// #![feature(pointer_byte_offsets)]
1369+ ///
1370+ /// // On some platforms, the alignment of i32 is less than 4.
1371+ /// #[repr(align(4))]
1372+ /// struct AlignedI32(i32);
1373+ ///
1374+ /// let data = AlignedI32(42);
1375+ /// let ptr = &data as *const AlignedI32;
1376+ ///
1377+ /// assert!(ptr.is_aligned());
1378+ /// assert!(!ptr.wrapping_byte_add(1).is_aligned());
1379+ /// ```
1380+ ///
1381+ /// # At compiletime
1382+ /// **Note: Alignment at compiletime is experimental and subject to change. See the
1383+ /// [tracking issue] for details.**
1384+ ///
1385+ /// At compiletime, the compiler may not know where a value will end up in memory.
1386+ /// Calling this function on a pointer created from a reference at compiletime will only
1387+ /// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1388+ /// is never aligned if cast to a type with a stricter alignment than the reference's
1389+ /// underlying allocation.
1390+ ///
1391+ #[ cfg_attr( bootstrap, doc = "```ignore" ) ]
1392+ #[ cfg_attr( not( bootstrap) , doc = "```" ) ]
1393+ /// #![feature(pointer_is_aligned)]
1394+ /// #![feature(const_pointer_is_aligned)]
1395+ ///
1396+ /// // On some platforms, the alignment of primitives is less than their size.
1397+ /// #[repr(align(4))]
1398+ /// struct AlignedI32(i32);
1399+ /// #[repr(align(8))]
1400+ /// struct AlignedI64(i64);
1401+ ///
1402+ /// const _: () = {
1403+ /// let data = AlignedI32(42);
1404+ /// let ptr = &data as *const AlignedI32;
1405+ /// assert!(ptr.is_aligned());
1406+ ///
1407+ /// // At runtime either `ptr1` or `ptr2` would be aligned, but at compiletime neither is aligned.
1408+ /// let ptr1 = ptr.cast::<AlignedI64>();
1409+ /// let ptr2 = ptr.wrapping_add(1).cast::<AlignedI64>();
1410+ /// assert!(!ptr1.is_aligned());
1411+ /// assert!(!ptr2.is_aligned());
1412+ /// };
1413+ /// ```
1414+ ///
1415+ /// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1416+ /// pointer is aligned, even if the compiletime pointer wasn't aligned.
1417+ ///
1418+ #[ cfg_attr( bootstrap, doc = "```ignore" ) ]
1419+ #[ cfg_attr( not( bootstrap) , doc = "```" ) ]
1420+ /// #![feature(pointer_is_aligned)]
1421+ /// #![feature(const_pointer_is_aligned)]
1422+ ///
1423+ /// // On some platforms, the alignment of primitives is less than their size.
1424+ /// #[repr(align(4))]
1425+ /// struct AlignedI32(i32);
1426+ /// #[repr(align(8))]
1427+ /// struct AlignedI64(i64);
1428+ ///
1429+ /// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1430+ /// const COMPTIME_PTR: *const AlignedI32 = &AlignedI32(42);
1431+ /// const _: () = assert!(!COMPTIME_PTR.cast::<AlignedI64>().is_aligned());
1432+ /// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).cast::<AlignedI64>().is_aligned());
1433+ ///
1434+ /// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1435+ /// let runtime_ptr = COMPTIME_PTR;
1436+ /// assert_ne!(
1437+ /// runtime_ptr.cast::<AlignedI64>().is_aligned(),
1438+ /// runtime_ptr.wrapping_add(1).cast::<AlignedI64>().is_aligned(),
1439+ /// );
1440+ /// ```
1441+ ///
1442+ /// If a pointer is created from a fixed address, this function behaves the same during
1443+ /// runtime and compiletime.
1444+ ///
1445+ #[ cfg_attr( bootstrap, doc = "```ignore" ) ]
1446+ #[ cfg_attr( not( bootstrap) , doc = "```" ) ]
1447+ /// #![feature(pointer_is_aligned)]
1448+ /// #![feature(const_pointer_is_aligned)]
1449+ ///
1450+ /// // On some platforms, the alignment of primitives is less than their size.
1451+ /// #[repr(align(4))]
1452+ /// struct AlignedI32(i32);
1453+ /// #[repr(align(8))]
1454+ /// struct AlignedI64(i64);
1455+ ///
1456+ /// const _: () = {
1457+ /// let ptr = 40 as *const AlignedI32;
1458+ /// assert!(ptr.is_aligned());
1459+ ///
1460+ /// // For pointers with a known address, runtime and compiletime behavior are identical.
1461+ /// let ptr1 = ptr.cast::<AlignedI64>();
1462+ /// let ptr2 = ptr.wrapping_add(1).cast::<AlignedI64>();
1463+ /// assert!(ptr1.is_aligned());
1464+ /// assert!(!ptr2.is_aligned());
1465+ /// };
1466+ /// ```
1467+ ///
1468+ /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
13511469 #[ must_use]
13521470 #[ inline]
13531471 #[ unstable( feature = "pointer_is_aligned" , issue = "96284" ) ]
1354- pub fn is_aligned ( self ) -> bool
1472+ #[ rustc_const_unstable( feature = "const_pointer_is_aligned" , issue = "104203" ) ]
1473+ pub const fn is_aligned ( self ) -> bool
13551474 where
13561475 T : Sized ,
13571476 {
1358- self . is_aligned_to ( core :: mem:: align_of :: < T > ( ) )
1477+ self . is_aligned_to ( mem:: align_of :: < T > ( ) )
13591478 }
13601479
13611480 /// Returns whether the pointer is aligned to `align`.
@@ -1366,16 +1485,121 @@ impl<T: ?Sized> *const T {
13661485 /// # Panics
13671486 ///
13681487 /// The function panics if `align` is not a power-of-two (this includes 0).
1488+ ///
1489+ /// # Examples
1490+ ///
1491+ /// Basic usage:
1492+ /// ```
1493+ /// #![feature(pointer_is_aligned)]
1494+ /// #![feature(pointer_byte_offsets)]
1495+ ///
1496+ /// // On some platforms, the alignment of i32 is less than 4.
1497+ /// #[repr(align(4))]
1498+ /// struct AlignedI32(i32);
1499+ ///
1500+ /// let data = AlignedI32(42);
1501+ /// let ptr = &data as *const AlignedI32;
1502+ ///
1503+ /// assert!(ptr.is_aligned_to(1));
1504+ /// assert!(ptr.is_aligned_to(2));
1505+ /// assert!(ptr.is_aligned_to(4));
1506+ ///
1507+ /// assert!(ptr.wrapping_byte_add(2).is_aligned_to(2));
1508+ /// assert!(!ptr.wrapping_byte_add(2).is_aligned_to(4));
1509+ ///
1510+ /// assert_ne!(ptr.is_aligned_to(8), ptr.wrapping_add(1).is_aligned_to(8));
1511+ /// ```
1512+ ///
1513+ /// # At compiletime
1514+ /// **Note: Alignment at compiletime is experimental and subject to change. See the
1515+ /// [tracking issue] for details.**
1516+ ///
1517+ /// At compiletime, the compiler may not know where a value will end up in memory.
1518+ /// Calling this function on a pointer created from a reference at compiletime will only
1519+ /// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1520+ /// cannot be stricter aligned than the reference's underlying allocation.
1521+ ///
1522+ #[ cfg_attr( bootstrap, doc = "```ignore" ) ]
1523+ #[ cfg_attr( not( bootstrap) , doc = "```" ) ]
1524+ /// #![feature(pointer_is_aligned)]
1525+ /// #![feature(const_pointer_is_aligned)]
1526+ ///
1527+ /// // On some platforms, the alignment of i32 is less than 4.
1528+ /// #[repr(align(4))]
1529+ /// struct AlignedI32(i32);
1530+ ///
1531+ /// const _: () = {
1532+ /// let data = AlignedI32(42);
1533+ /// let ptr = &data as *const AlignedI32;
1534+ ///
1535+ /// assert!(ptr.is_aligned_to(1));
1536+ /// assert!(ptr.is_aligned_to(2));
1537+ /// assert!(ptr.is_aligned_to(4));
1538+ ///
1539+ /// // At compiletime, we know for sure that the pointer isn't aligned to 8.
1540+ /// assert!(!ptr.is_aligned_to(8));
1541+ /// assert!(!ptr.wrapping_add(1).is_aligned_to(8));
1542+ /// };
1543+ /// ```
1544+ ///
1545+ /// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1546+ /// pointer is aligned, even if the compiletime pointer wasn't aligned.
1547+ ///
1548+ #[ cfg_attr( bootstrap, doc = "```ignore" ) ]
1549+ #[ cfg_attr( not( bootstrap) , doc = "```" ) ]
1550+ /// #![feature(pointer_is_aligned)]
1551+ /// #![feature(const_pointer_is_aligned)]
1552+ ///
1553+ /// // On some platforms, the alignment of i32 is less than 4.
1554+ /// #[repr(align(4))]
1555+ /// struct AlignedI32(i32);
1556+ ///
1557+ /// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1558+ /// const COMPTIME_PTR: *const AlignedI32 = &AlignedI32(42);
1559+ /// const _: () = assert!(!COMPTIME_PTR.is_aligned_to(8));
1560+ /// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).is_aligned_to(8));
1561+ ///
1562+ /// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1563+ /// let runtime_ptr = COMPTIME_PTR;
1564+ /// assert_ne!(
1565+ /// runtime_ptr.is_aligned_to(8),
1566+ /// runtime_ptr.wrapping_add(1).is_aligned_to(8),
1567+ /// );
1568+ /// ```
1569+ ///
1570+ /// If a pointer is created from a fixed address, this function behaves the same during
1571+ /// runtime and compiletime.
1572+ ///
1573+ #[ cfg_attr( bootstrap, doc = "```ignore" ) ]
1574+ #[ cfg_attr( not( bootstrap) , doc = "```" ) ]
1575+ /// #![feature(pointer_is_aligned)]
1576+ /// #![feature(const_pointer_is_aligned)]
1577+ ///
1578+ /// const _: () = {
1579+ /// let ptr = 40 as *const u8;
1580+ /// assert!(ptr.is_aligned_to(1));
1581+ /// assert!(ptr.is_aligned_to(2));
1582+ /// assert!(ptr.is_aligned_to(4));
1583+ /// assert!(ptr.is_aligned_to(8));
1584+ /// assert!(!ptr.is_aligned_to(16));
1585+ /// };
1586+ /// ```
1587+ ///
1588+ /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
13691589 #[ must_use]
13701590 #[ inline]
13711591 #[ unstable( feature = "pointer_is_aligned" , issue = "96284" ) ]
1372- pub fn is_aligned_to ( self , align : usize ) -> bool {
1592+ #[ rustc_const_unstable( feature = "const_pointer_is_aligned" , issue = "104203" ) ]
1593+ pub const fn is_aligned_to ( self , align : usize ) -> bool {
13731594 if !align. is_power_of_two ( ) {
13741595 panic ! ( "is_aligned_to: align is not a power-of-two" ) ;
13751596 }
13761597
1377- // Cast is needed for `T: !Sized`
1378- self . cast :: < u8 > ( ) . addr ( ) & align - 1 == 0
1598+ // We can't use the address of `self` in a `const fn`, so we use `align_offset` instead.
1599+ // The cast to `()` is used to
1600+ // 1. deal with fat pointers; and
1601+ // 2. ensure that `align_offset` doesn't actually try to compute an offset.
1602+ self . cast :: < ( ) > ( ) . align_offset ( align) == 0
13791603 }
13801604}
13811605
0 commit comments