about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJacob Pratt <jacob@jhpratt.dev>2025-01-15 04:08:12 -0500
committerGitHub <noreply@github.com>2025-01-15 04:08:12 -0500
commit56eb7bd9a98498d06dce35734c439704c0fdca03 (patch)
treea35b23906675e3209317d661102a8047ff20010e
parentbf4aeeb45c7d3dee7930a78d759445b8b8615caa (diff)
parent58d6301cad7ac20407e708cd9d1ec499d1804015 (diff)
downloadrust-56eb7bd9a98498d06dce35734c439704c0fdca03.tar.gz
rust-56eb7bd9a98498d06dce35734c439704c0fdca03.zip
Rollup merge of #134678 - zachs18:offset-ptr-update, r=tgross35
Update `ReadDir::next` in `std::sys::pal::unix::fs` to use `&raw const (*p).field` instead of `p.byte_offset().cast()`

Since https://github.com/rust-lang/reference/pull/1387 and https://github.com/rust-lang/rust/pull/117572, `&raw mut (*p).field`/`addr_of!((*p).field)` is defined to have the same inbounds preconditions as `ptr::offset`/`ptr::byte_offset`. I.e. `&raw const (*p).field` does not require that `p: *const T` point to a full `size_of::<T>()` bytes of memory, only that `p.byte_add(offset_of!(T, field))` is defined.

The old comment "[...] we don't even get to use `&raw const (*entry_ptr).d_name` because that operation requires the full extent of *entry_ptr to be in bounds of the same allocation, which is not necessarily the case here [...]" is now outdated, and the code can be simplified to use `&raw const (*entry_ptr).field`.

-------

There should be no behavior differences from this PR.

The `: *const dirent64` on line 716 and the `const _: usize = mem::offset_of!(dirent64, $field);` and comment on lines 749-751 are just sanity checks and should not affect semantics.

Since the `offset_ptr!` macro is only called three times, and all with the same local variable entry_ptr, I just used the local variable directly in the macro instead of taking it as an input, and renamed the macro to `entry_field_ptr!`.

The whole macro could also be removed and replaced with just using `&raw const (*entry_ptr).field`  in the three places, but the comments on the macro seemed worthwhile to keep.
-rw-r--r--library/std/src/sys/pal/unix/fs.rs36
1 files changed, 13 insertions, 23 deletions
diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs
index 54fa31620ac..fdf011c1948 100644
--- a/library/std/src/sys/pal/unix/fs.rs
+++ b/library/std/src/sys/pal/unix/fs.rs
@@ -709,7 +709,7 @@ impl Iterator for ReadDir {
                 // thread safety for readdir() as long an individual DIR* is not accessed
                 // concurrently, which is sufficient for Rust.
                 super::os::set_errno(0);
-                let entry_ptr = readdir64(self.inner.dirp.0);
+                let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0);
                 if entry_ptr.is_null() {
                     // We either encountered an error, or reached the end. Either way,
                     // the next call to next() should return None.
@@ -735,29 +735,19 @@ impl Iterator for ReadDir {
                 // contents were "simply" partially initialized data.
                 //
                 // Like for uninitialized contents, converting entry_ptr to `&dirent64`
-                // would not be legal. However, unique to dirent64 is that we don't even
-                // get to use `&raw const (*entry_ptr).d_name` because that operation
-                // requires the full extent of *entry_ptr to be in bounds of the same
-                // allocation, which is not necessarily the case here.
-                //
-                // Instead we must access fields individually through their offsets.
-                macro_rules! offset_ptr {
-                    ($entry_ptr:expr, $field:ident) => {{
-                        const OFFSET: isize = mem::offset_of!(dirent64, $field) as isize;
-                        if true {
-                            // Cast to the same type determined by the else branch.
-                            $entry_ptr.byte_offset(OFFSET).cast::<_>()
-                        } else {
-                            #[allow(deref_nullptr)]
-                            {
-                                &raw const (*ptr::null::<dirent64>()).$field
-                            }
-                        }
-                    }};
+                // would not be legal. However, we can use `&raw const (*entry_ptr).d_name`
+                // to refer the fields individually, because that operation is equivalent
+                // to `byte_offset` and thus does not require the full extent of `*entry_ptr`
+                // to be in bounds of the same allocation, only the offset of the field
+                // being referenced.
+                macro_rules! entry_field_ptr {
+                    ($field:ident) => {
+                        &raw const (*entry_ptr).$field
+                    };
                 }
 
                 // d_name is guaranteed to be null-terminated.
-                let name = CStr::from_ptr(offset_ptr!(entry_ptr, d_name).cast());
+                let name = CStr::from_ptr(entry_field_ptr!(d_name).cast());
                 let name_bytes = name.to_bytes();
                 if name_bytes == b"." || name_bytes == b".." {
                     continue;
@@ -765,14 +755,14 @@ impl Iterator for ReadDir {
 
                 #[cfg(not(target_os = "vita"))]
                 let entry = dirent64_min {
-                    d_ino: *offset_ptr!(entry_ptr, d_ino) as u64,
+                    d_ino: *entry_field_ptr!(d_ino) as u64,
                     #[cfg(not(any(
                         target_os = "solaris",
                         target_os = "illumos",
                         target_os = "aix",
                         target_os = "nto",
                     )))]
-                    d_type: *offset_ptr!(entry_ptr, d_type) as u8,
+                    d_type: *entry_field_ptr!(d_type) as u8,
                 };
 
                 #[cfg(target_os = "vita")]