Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cast to a DST pointer? #288

Open
joshlf opened this issue Jun 14, 2021 · 8 comments
Open

Cast to a DST pointer? #288

joshlf opened this issue Jun 14, 2021 · 8 comments

Comments

@joshlf
Copy link

joshlf commented Jun 14, 2021

This question is inspired by the slice_dst crate (cc @CAD97)

Is it valid, given a #[repr(C)] struct with a trailing slice field, to cast a slice fat pointer to a fat pointer to that struct? If so, is it guaranteed that the slice length will be retained as expected? Concretely:

#[repr(C)]
struct Foo {
    bar: u8,
    baz: [u16],
}

fn create_foo_ptr(base: *const u8, elems: usize) -> *const Foo {
    let slice_ptr = std::ptr::slice_from_raw_parts(base as *const u16, elems);
    slice_ptr as *const Foo // <-- Will this produce a `*const Foo` with length `elems` for the `baz` field?
}
@RalfJung
Copy link
Member

Oh interesting, I did not realize that we even allow DST raw pointer casts that change the type.

I would strongly assume that this affects only the type, and leaves the value unchanged. But I don't think we fully document what the wide pointer metadata of types like Foo even is?

@joshlf
Copy link
Author

joshlf commented Jun 19, 2021

Yeah, it'd be great if there were some specified way to construct a DST raw pointer. Speaking for myself, it would allow us to support DSTs in zerocopy.

@RalfJung
Copy link
Member

I think the plan is for that to happen via rust-lang/rust#81513

@CAD97
Copy link

CAD97 commented Jun 20, 2021

But I don't think we fully document what the wide pointer metadata of types like Foo even is?

I agree that I don't think it's explicitly documented, but I recall it at least being mentioned in one place that compound DSTs have the same metadata as their tail DST member. I hope it's not just the stdlib source that I recall this from, though...

This is also implicitly reinforced by the error: when you mismatch fat pointer types, you get the error

error[E0606]: casting `*mut (dyn Trait + 'static)` as `*mut [()]` is invalid
  --> src/lib.rs:14:5
   |
14 |     p as *mut _
   |     ^^^^^^^^^^^
   |
   = note: vtable kinds may not match

which (for better or for worse) implies that the fat pointers match when the error is not emitted (i.e. when the DSTs have the same terminal tail DST kind).

The fact that this works is used extensively by the ecosystem at this point, at least by rowan (in rust-analyzer), triomphe, and bitvec.

nicholasbishop added a commit to nicholasbishop/uefi-rs that referenced this issue Feb 21, 2022
The `FileInfo`, `FileSystemInfo`, and `FileSystemVolumeLabel` structs
are all DSTs because they end with a `Char16` slice containing a name. A
wide pointer (or reference) to such a struct contains two parts: the
base memory address, and the number of elements in the slice.

Previously there was no officially-documented way to create such a wide
pointer, so uefi-rs used a trick that is used [elsewhere in the Rust
ecosystem][1]: create a slice matching the type and length of the slice
at the end of the struct, but passing in the desired base pointer
instead. Then the slice type is cast to match the desired struct type.

There is now a standard (albeit currently unstable) API for performing
this construction added by [RFC 2580][2]. This requires enabling the
`ptr_metadata` feature. The runtime result should be the same, but
without relying on undocumented Rust internals.

[1]: rust-lang/unsafe-code-guidelines#288 (comment)
[2]: https://rust-lang.github.io/rfcs/2580-ptr-meta.html
nicholasbishop added a commit to nicholasbishop/uefi-rs that referenced this issue Feb 22, 2022
The `FileInfo`, `FileSystemInfo`, and `FileSystemVolumeLabel` structs
are all DSTs because they end with a `Char16` slice containing a name. A
wide pointer (or reference) to such a struct contains two parts: the
base memory address, and the number of elements in the slice.

Previously there was no officially-documented way to create such a wide
pointer, so uefi-rs used a trick that is used [elsewhere in the Rust
ecosystem][1]: create a slice matching the type and length of the slice
at the end of the struct, but passing in the desired base pointer
instead. Then the slice type is cast to match the desired struct type.

There is now a standard (albeit currently unstable) API for performing
this construction added by [RFC 2580][2]. This requires enabling the
`ptr_metadata` feature. The runtime result should be the same, but
without relying on undocumented Rust internals.

[1]: rust-lang/unsafe-code-guidelines#288 (comment)
[2]: https://rust-lang.github.io/rfcs/2580-ptr-meta.html
joshlf added a commit to joshlf/reference that referenced this issue Oct 11, 2023
@joshlf
Copy link
Author

joshlf commented Oct 11, 2023

I've submitted rust-lang/reference#1417 to document this behavior.

@joshlf
Copy link
Author

joshlf commented Oct 18, 2023

Just ran across another instance of this being relied upon in the ecosystem.

@RalfJung
Copy link
Member

Those are two slice types, that's IMO much less subtle than when structs with slice tail are involved.

@joshlf
Copy link
Author

joshlf commented Oct 18, 2023

Agreed; my point is just that it's an example of relying on behavior which is technically not guaranteed, but would be with rust-lang/reference#1417.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants