Skip to content

Commit a161062

Browse files
committed
Add clarifying context to the most confusing pointer APIs
1 parent fbdb10f commit a161062

File tree

3 files changed

+114
-4
lines changed

3 files changed

+114
-4
lines changed

library/core/src/ptr/const_ptr.rs

+35-2
Original file line numberDiff line numberDiff line change
@@ -520,9 +520,16 @@ impl<T: ?Sized> *const T {
520520
/// Calculates the distance between two pointers. The returned value is in
521521
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
522522
///
523-
/// This function is the inverse of [`offset`].
523+
/// This is equivalent to `(self as isize - other as isize) / (mem::size_of::<T>() as isize)`,
524+
/// except that it has a lot more opportunities for UB, in exchange for the compiler
525+
/// understanding what you're doing better.
524526
///
525-
/// [`offset`]: #method.offset
527+
/// The primary motivation of this method is for computing the `len` of an array/slice
528+
/// of `T` that you are currently representing as a "start" and "end" pointer
529+
/// (and "end" is "one past the end" of the array).
530+
/// In that case, `end.offset_from(start)` gets you the length of the array.
531+
///
532+
/// All of the following safety requirements are trivially satisfied for this usecase.
526533
///
527534
/// # Safety
528535
///
@@ -560,6 +567,7 @@ impl<T: ?Sized> *const T {
560567
/// such large allocations either.)
561568
///
562569
/// [`add`]: #method.add
570+
/// [`offset`]: #method.offset
563571
/// [allocated object]: crate::ptr#allocated-object
564572
///
565573
/// # Panics
@@ -1021,6 +1029,31 @@ impl<T: ?Sized> *const T {
10211029
/// Computes the offset that needs to be applied to the pointer in order to make it aligned to
10221030
/// `align`.
10231031
///
1032+
/// Before getting into the precise semantics, it helps to have some context for why this
1033+
/// operation seems so weird on the surface. This is for two reasons:
1034+
///
1035+
/// * It's for SIMD, specifically [`slice::align_to`]
1036+
/// * It wants code that uses SIMD operations to work in a `const fn`
1037+
///
1038+
/// In particular, this operation is intended for breaking up a loop into its component
1039+
/// unaligned and aligned parts, so that the aligned parts can be processed using SIMD.
1040+
/// For that kind of pattern it's always ok to "fail" to align the pointer, because the
1041+
/// unaligned path can always handle all the data.
1042+
///
1043+
/// Lots of really basic operations want to use SIMD under the hood, so it would be very
1044+
/// frustrating if using this pattern made it impossible for an operation to work in
1045+
/// const contexts. Unfortunately, observing any properties of a pointer's address
1046+
/// is a very dangerous and problematic operation in const contexts, because it's a huge
1047+
/// reproducibility issue (which has actual soundness implications with compilation units).
1048+
///
1049+
/// Thankfully, because the precise SIMD pattern we're interested in *already* has a fallback
1050+
/// mode where the alignment completely fails, the compiler can just make this operation always
1051+
/// fail to align the pointer when doing const evaluation, and then everything is
1052+
/// always deterministic and reproducible (and the const evaluator is an interpreter anyway,
1053+
/// so you weren't actually going to get amazing SIMD speedups).
1054+
///
1055+
/// Alright, now on to the actual semantics:
1056+
///
10241057
/// If it is not possible to align the pointer, the implementation returns
10251058
/// `usize::MAX`. It is permissible for the implementation to *always*
10261059
/// return `usize::MAX`. Only your algorithm's performance can depend

library/core/src/ptr/mut_ptr.rs

+35-2
Original file line numberDiff line numberDiff line change
@@ -698,9 +698,16 @@ impl<T: ?Sized> *mut T {
698698
/// Calculates the distance between two pointers. The returned value is in
699699
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
700700
///
701-
/// This function is the inverse of [`offset`].
701+
/// This is equivalent to `(self as isize - other as isize) / (mem::size_of::<T>() as isize)`,
702+
/// except that it has a lot more opportunities for UB, in exchange for the compiler
703+
/// understanding what you're doing better.
702704
///
703-
/// [`offset`]: #method.offset-1
705+
/// The primary motivation of this method is for computing the `len` of an array/slice
706+
/// of `T` that you are currently representing as a "start" and "end" pointer
707+
/// (and "end" is "one past the end" of the array).
708+
/// In that case, `end.offset_from(start)` gets you the length of the array.
709+
///
710+
/// All of the following safety requirements are trivially satisfied for this usecase.
704711
///
705712
/// # Safety
706713
///
@@ -738,6 +745,7 @@ impl<T: ?Sized> *mut T {
738745
/// such large allocations either.)
739746
///
740747
/// [`add`]: #method.add
748+
/// [`offset`]: #method.offset
741749
/// [allocated object]: crate::ptr#allocated-object
742750
///
743751
/// # Panics
@@ -1292,6 +1300,31 @@ impl<T: ?Sized> *mut T {
12921300
/// Computes the offset that needs to be applied to the pointer in order to make it aligned to
12931301
/// `align`.
12941302
///
1303+
/// Before getting into the precise semantics, it helps to have some context for why this
1304+
/// operation seems so weird on the surface. This is for two reasons:
1305+
///
1306+
/// * It's for SIMD, specifically [`slice::align_to`]
1307+
/// * It wants code that uses SIMD operations to work in a `const fn`
1308+
///
1309+
/// In particular, this operation is intended for breaking up a loop into its component
1310+
/// unaligned and aligned parts, so that the aligned parts can be processed using SIMD.
1311+
/// For that kind of pattern it's always ok to "fail" to align the pointer, because the
1312+
/// unaligned path can always handle all the data.
1313+
///
1314+
/// Lots of really basic operations want to use SIMD under the hood, so it would be very
1315+
/// frustrating if using this pattern made it impossible for an operation to work in
1316+
/// const contexts. Unfortunately, observing any properties of a pointer's address
1317+
/// is a very dangerous and problematic operation in const contexts, because it's a huge
1318+
/// reproducibility issue (which has actual soundness implications with compilation units).
1319+
///
1320+
/// Thankfully, because the precise SIMD pattern we're interested in *already* has a fallback
1321+
/// mode where the alignment completely fails, the compiler can just make this operation always
1322+
/// fail to align the pointer when doing const evaluation, and then everything is
1323+
/// always deterministic and reproducible (and the const evaluator is an interpreter anyway,
1324+
/// so you weren't actually going to get amazing SIMD speedups).
1325+
///
1326+
/// Alright, now on to the actual semantics:
1327+
///
12951328
/// If it is not possible to align the pointer, the implementation returns
12961329
/// `usize::MAX`. It is permissible for the implementation to *always*
12971330
/// return `usize::MAX`. Only your algorithm's performance can depend

library/core/src/slice/mod.rs

+44
Original file line numberDiff line numberDiff line change
@@ -3406,6 +3406,28 @@ impl<T> [T] {
34063406
/// This method has no purpose when either input element `T` or output element `U` are
34073407
/// zero-sized and will return the original slice without splitting anything.
34083408
///
3409+
/// The reason this operation is slightly vague on its guarantees is because:
3410+
///
3411+
/// * It's for SIMD, specifically [`slice::as_simd`]
3412+
/// * It wants code that uses SIMD operations to work in a `const fn`
3413+
///
3414+
/// In particular, this operation is intended for breaking up a loop into its component
3415+
/// unaligned and aligned parts, so that the aligned parts can be processed using SIMD.
3416+
/// For that kind of pattern it's always ok to "fail" to align the slice, because the
3417+
/// unaligned path can always handle all the data.
3418+
///
3419+
/// Lots of really basic operations want to use SIMD under the hood, so it would be very
3420+
/// frustrating if using this pattern made it impossible for an operation to work in
3421+
/// const contexts. Unfortunately, observing any properties of a pointer's address
3422+
/// is a very dangerous and problematic operation in const contexts, because it's a huge
3423+
/// reproducibility issue (which has actual soundness implications with compilation units).
3424+
///
3425+
/// Thankfully, because the precise SIMD pattern we're interested in *already* has a fallback
3426+
/// mode where the alignment completely fails, the compiler can just make this operation always
3427+
/// fail to align the slice when doing const evaluation, and then everything is
3428+
/// always deterministic and reproducible (and the const evaluator is an interpreter anyway,
3429+
/// so you weren't actually going to get amazing SIMD speedups).
3430+
///
34093431
/// # Safety
34103432
///
34113433
/// This method is essentially a `transmute` with respect to the elements in the returned
@@ -3467,6 +3489,28 @@ impl<T> [T] {
34673489
/// This method has no purpose when either input element `T` or output element `U` are
34683490
/// zero-sized and will return the original slice without splitting anything.
34693491
///
3492+
/// The reason this operation is slightly vague on its guarantees is because:
3493+
///
3494+
/// * It's for SIMD, specifically [`slice::as_simd_mut`]
3495+
/// * It wants code that uses SIMD operations to work in a `const fn`
3496+
///
3497+
/// In particular, this operation is intended for breaking up a loop into its component
3498+
/// unaligned and aligned parts, so that the aligned parts can be processed using SIMD.
3499+
/// For that kind of pattern it's always ok to "fail" to align the slice, because the
3500+
/// unaligned path can always handle all the data.
3501+
///
3502+
/// Lots of really basic operations want to use SIMD under the hood, so it would be very
3503+
/// frustrating if using this pattern made it impossible for an operation to work in
3504+
/// const contexts. Unfortunately, observing any properties of a pointer's address
3505+
/// is a very dangerous and problematic operation in const contexts, because it's a huge
3506+
/// reproducibility issue (which has actual soundness implications with compilation units).
3507+
///
3508+
/// Thankfully, because the precise SIMD pattern we're interested in *already* has a fallback
3509+
/// mode where the alignment completely fails, the compiler can just make this operation always
3510+
/// fail to align the slice when doing const evaluation, and then everything is
3511+
/// always deterministic and reproducible (and the const evaluator is an interpreter anyway,
3512+
/// so you weren't actually going to get amazing SIMD speedups).
3513+
///
34703514
/// # Safety
34713515
///
34723516
/// This method is essentially a `transmute` with respect to the elements in the returned

0 commit comments

Comments
 (0)