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

Tracking Issue for [*const T|*mut T]::with_metadata_of #75091

Open
5 of 8 tasks
oliver-giersch opened this issue Aug 3, 2020 · 12 comments
Open
5 of 8 tasks

Tracking Issue for [*const T|*mut T]::with_metadata_of #75091

oliver-giersch opened this issue Aug 3, 2020 · 12 comments
Labels
A-raw-pointers Area: raw pointers, MaybeUninit, NonNull B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. Libs-Tracked Libs issues that are tracked on the team's project board. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@oliver-giersch
Copy link
Contributor

oliver-giersch commented Aug 3, 2020

Feature gate: #![feature(set_ptr_value)]

Public API

impl<T: ?Sized> *mut T {
    pub fn with_metadata_of<U: ?Sized>(self, val: *const U) -> *mut U;
}

impl<T: ?Sized> *const T {
    pub fn with_metadata_of<U: ?Sized>(self, val: *const U) -> *const U;
}

Steps / History

Unresolved Questions

@oliver-giersch oliver-giersch added the C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. label Aug 3, 2020
@jonas-schievink jonas-schievink added B-unstable Blocker: Implemented in the nightly compiler and unstable. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Aug 3, 2020
@KodrAus KodrAus added Libs-Tracked Libs issues that are tracked on the team's project board. A-raw-pointers Area: raw pointers, MaybeUninit, NonNull labels Aug 6, 2020
tmandry added a commit to tmandry/rust that referenced this issue Aug 11, 2020
…fJung

Requested changes to [*mut T|*const T]::set_ptr_value

This is a follow-up to PR rust-lang#74774 (tracking issue rust-lang#75091), acting on some change requests made after approval:

- adds `#[must_use]` attribute
- changes type of `val` pointer argument from `()` to `u8`
- adjusts documentation mentioning pointer provenance
@SimonSapin
Copy link
Contributor

As discussed on Zulip this could be replaced with ptr::from_raw_parts(val, ptr::metadata(self)), tracked in #81513

bors added a commit to rust-lang-ci/rust that referenced this issue Mar 28, 2022
Refactor set_ptr_value as with_metadata_of

Replaces `set_ptr_value` (rust-lang#75091) with methods of reversed argument order:

```rust
impl<T: ?Sized> *mut T {
    pub fn with_metadata_of<U: ?Sized>(self, val: *mut U) -> *mut U;
}

impl<T: ?Sized> *const T {
    pub fn with_metadata_of<U: ?Sized>(self, val: *const U) -> *const U;
}
```

By reversing the arguments we achieve several clarifications:

- The function closely resembles `cast` with an argument to
  initialize the metadata. This is easier to teach and answers a long
  outstanding question that had restricted cast to `Sized` pointee
  targets. See multiples reviews of
  <rust-lang#47631>
- The 'object identity', in the form of provenance, is now preserved
  from the receiver argument to the result. This helps explain the method as
  a builder-style, instead of some kind of setter that would modify
  something in-place. Ensuring that the result has the identity of the
  `self` argument is also beneficial for an intuition of effects.
- An outstanding concern, 'Correct argument type', is avoided by not
  committing to any specific argument type. This is consistent with cast
  which does not require its receiver to be a 'raw address'.

Hopefully the usage examples in `sync/rc.rs` serve as sufficient examples of the style to convince the reader of the readability improvements of this style, when compared to the previous order of arguments.

I want to take the opportunity to motivate inclusion of this method _separate_ from metadata API, separate from `feature(ptr_metadata)`. It does _not_ involve the `Pointee` trait in any form. This may be regarded as a very, very light form that does not commit to any details of the pointee trait, or its associated metadata. There are several use cases for which this is already sufficient and no further inspection of metadata is necessary.

- Storing the coercion of `*mut T` into `*mut dyn Trait` as a way to dynamically cast some an arbitrary instance of the same type to a dyn trait instance. In particular, one can have a field of type `Option<*mut dyn io::Seek>` to memorize if a particular writer is seekable. Then a method `fn(self: &T) -> Option<&dyn Seek>` can be provided, which does _not_ involve the static trait bound `T: Seek`. This makes it possible to create an API that is capable of utilizing seekable streams and non-seekable streams (instead of a possible less efficient manner such as more buffering) through the same entry-point.

- Enabling more generic forms of unsizing for no-`std` smart pointers. Using the stable APIs only few concrete cases are available. One can unsize arrays to `[T]` by `ptr::slice_from_raw_parts` but unsizing a custom smart pointer to, e.g., `dyn Iterator`, `dyn Future`, `dyn Debug`, can't easily be done generically. Exposing `with_metadata_of` would allow smart pointers to offer their own `unsafe` escape hatch with similar parameters where the caller provides the unsized metadata. This is particularly interesting for embedded where `dyn`-trait usage can drastically reduce code size.
@ChayimFriedman2
Copy link
Contributor

Even if we want to keep these methods I think it's better to write them in terms of the metadata API as it's easy to reason about and less error prone than the current implementation.

HeroicKatora pushed a commit to HeroicKatora/rust that referenced this issue Oct 21, 2022
The method takes two pointer arguments: one `self` supplying the pointer
value, and a second pointer supplying the metadata.

The new parameter type more clearly reflects the actual requirements.
The provenance of the metadata parameter is disregarded completely.
Using a mutable pointer in the call site can be coerced to a const
pointer while the reverse is not true.

An example of the current use:

```rust
// Manually taking an unsized object from a `ManuallyDrop` into another allocation.
let val: &core::mem::ManuallyDrop<T> = …;

let ptr = val as *const _ as *mut T;
let ptr = uninit.as_ptr().with_metadata_of(ptr);
```

This could then instead be simplified to:

```rust
// Manually taking an unsized object from a `ManuallyDrop` into another allocation.
let val: &core::mem::ManuallyDrop<T> = …;

let ptr = uninit.as_ptr().with_metadata_of(&**val);
```
@dtolnay dtolnay changed the title Tracking Issue for [*const T|*mut T]::set_ptr_value Tracking Issue for [*const T|*mut T]::with_metadata_of Oct 21, 2022
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Oct 22, 2022
…nter_argument, r=dtolnay

Adjust argument type for mutable with_metadata_of (rust-lang#75091)

The method takes two pointer arguments: one `self` supplying the pointer value, and a second pointer supplying the metadata.

The new parameter type more clearly reflects the actual requirements. The provenance of the metadata parameter is disregarded completely. Using a mutable pointer in the call site can be coerced to a const pointer while the reverse is not true.

In some cases, the current parameter type can thus lead to a very slightly confusing additional cast. [Example](HeroicKatora/static-alloc@cad9377).

```rust
// Manually taking an unsized object from a `ManuallyDrop` into another allocation.
let val: &core::mem::ManuallyDrop<T> = …;

let ptr = val as *const _ as *mut T;
let ptr = uninit.as_ptr().with_metadata_of(ptr);
```

This could then instead be simplified to:

```rust
// Manually taking an unsized object from a `ManuallyDrop` into another allocation.
let val: &core::mem::ManuallyDrop<T> = …;

let ptr = uninit.as_ptr().with_metadata_of(&**val);
```

Tracking issue: rust-lang#75091

`@dtolnay` you're reviewed rust-lang#95249, would you mind chiming in?
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Oct 22, 2022
…nter_argument, r=dtolnay

Adjust argument type for mutable with_metadata_of (rust-lang#75091)

The method takes two pointer arguments: one `self` supplying the pointer value, and a second pointer supplying the metadata.

The new parameter type more clearly reflects the actual requirements. The provenance of the metadata parameter is disregarded completely. Using a mutable pointer in the call site can be coerced to a const pointer while the reverse is not true.

In some cases, the current parameter type can thus lead to a very slightly confusing additional cast. [Example](HeroicKatora/static-alloc@cad9377).

```rust
// Manually taking an unsized object from a `ManuallyDrop` into another allocation.
let val: &core::mem::ManuallyDrop<T> = …;

let ptr = val as *const _ as *mut T;
let ptr = uninit.as_ptr().with_metadata_of(ptr);
```

This could then instead be simplified to:

```rust
// Manually taking an unsized object from a `ManuallyDrop` into another allocation.
let val: &core::mem::ManuallyDrop<T> = …;

let ptr = uninit.as_ptr().with_metadata_of(&**val);
```

Tracking issue: rust-lang#75091

``@dtolnay`` you're reviewed rust-lang#95249, would you mind chiming in?
bors added a commit to rust-lang-ci/rust that referenced this issue Oct 22, 2022
Rollup of 7 pull requests

Successful merges:

 - rust-lang#102602 (Slightly tweak comments wrt `lint_overflowing_range_endpoint`)
 - rust-lang#103190 (rustdoc: render bounds of cross-crate GAT params)
 - rust-lang#103224 (Allow semicolon after closure within parentheses in macros)
 - rust-lang#103280 ((rust-lang#102929) Implement `String::leak` (attempt 2))
 - rust-lang#103329 (Add a forgotten check for NonNull::new_unchecked's precondition)
 - rust-lang#103346 (Adjust argument type for mutable with_metadata_of (rust-lang#75091))
 - rust-lang#103360 (Reduce false positives in msys2 detection)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Manishearth added a commit to Manishearth/rust that referenced this issue Nov 18, 2022
…ation__--this-can-be-simplified, r=scottmcm

Simplify some pointer method implementations

- Make `pointer::with_metadata_of` const (+simplify implementation) (cc rust-lang#75091)
- Simplify implementation of various pointer methods

r? `@scottmcm`

----

`from_raw_parts::<T>(this, metadata(self))` was annoying me for a while and I've finally figured out how it should _actually_ be done.
Manishearth added a commit to Manishearth/rust that referenced this issue Nov 18, 2022
…ation__--this-can-be-simplified, r=scottmcm

Simplify some pointer method implementations

- Make `pointer::with_metadata_of` const (+simplify implementation) (cc rust-lang#75091)
- Simplify implementation of various pointer methods

r? ``@scottmcm``

----

`from_raw_parts::<T>(this, metadata(self))` was annoying me for a while and I've finally figured out how it should _actually_ be done.
Manishearth added a commit to Manishearth/rust that referenced this issue Nov 18, 2022
…ation__--this-can-be-simplified, r=scottmcm

Simplify some pointer method implementations

- Make `pointer::with_metadata_of` const (+simplify implementation) (cc rust-lang#75091)
- Simplify implementation of various pointer methods

r? ```@scottmcm```

----

`from_raw_parts::<T>(this, metadata(self))` was annoying me for a while and I've finally figured out how it should _actually_ be done.
thomcc pushed a commit to tcdi/postgrestd that referenced this issue Feb 10, 2023
…ment, r=dtolnay

Adjust argument type for mutable with_metadata_of (#75091)

The method takes two pointer arguments: one `self` supplying the pointer value, and a second pointer supplying the metadata.

The new parameter type more clearly reflects the actual requirements. The provenance of the metadata parameter is disregarded completely. Using a mutable pointer in the call site can be coerced to a const pointer while the reverse is not true.

In some cases, the current parameter type can thus lead to a very slightly confusing additional cast. [Example](HeroicKatora/static-alloc@cad9377).

```rust
// Manually taking an unsized object from a `ManuallyDrop` into another allocation.
let val: &core::mem::ManuallyDrop<T> = …;

let ptr = val as *const _ as *mut T;
let ptr = uninit.as_ptr().with_metadata_of(ptr);
```

This could then instead be simplified to:

```rust
// Manually taking an unsized object from a `ManuallyDrop` into another allocation.
let val: &core::mem::ManuallyDrop<T> = …;

let ptr = uninit.as_ptr().with_metadata_of(&**val);
```

Tracking issue: rust-lang/rust#75091

``@dtolnay`` you're reviewed #95249, would you mind chiming in?
@WaffleLapkin
Copy link
Member

This needs a better motivational example. From current docs:

This function is primarily useful for allowing byte-wise pointer
arithmetic on potentially fat pointers:

#![feature(set_ptr_value)]
use core::fmt::Debug;
let mut arr: [i32; 3] = [1, 2, 3];
let mut ptr = arr.as_mut_ptr() as *mut dyn Debug;
let thin = ptr as *mut u8;
unsafe {
    ptr = thin.add(8).with_metadata_of(ptr);
    assert_eq!(*(ptr as *mut i32), 3);
    println!("{:?}", &*ptr); // will print "3"
}

This is superceeded by byte_add and co (#96283)

@dtolnay
Copy link
Member

dtolnay commented Nov 7, 2023

dyn-clone perhaps? dtolnay/dyn-clone#8

@HeroicKatora
Copy link
Contributor

HeroicKatora commented Nov 16, 2023

flexible-io is the implementation of the solution devised a while ago to the problem and another instance which originally got me invested in this PR. It relies on with_metadata_of. The full metadata API might be an optimization for later, but is way more complex than necessary here.

@kotakotik22

This comment was marked as off-topic.

@bjorn3
Copy link
Member

bjorn3 commented Apr 29, 2024

That will use the provenance of from rather than the provenance of onto, which will cause UB if you dereference the resulting pointer, but the provenance of from doesn't allow accessing this address. Miri is entirely correct in rejecting this. With rustc it may work by accident or you may get a miscompilation.

@GnomedDev
Copy link
Contributor

For the "Is this even needed/useful given that there now also is #81513?", I feel like this is super useful even with or without the metadata APIs.

  • Without (as stable is now) it allows for working with metadata in a limited fashion without assuming layout (which people are, and will do if not given the correct tools).
  • With, this is still useful as a higher level helper function, and doesn't block any of the metadata API stuff as a with_metadata function could be added to attach a Pointee::Metadata to an existing pointer in future.

I'd love to see this stabilized, if that is a good resolution to the last question needed answering.

@Noratrieb
Copy link
Member

@rust-lang/libs-api see above

@scottmcm
Copy link
Member

scottmcm commented Oct 2, 2024

Hmm, I think the primary alternative to this would be something like rust-lang/libs-team#246 -- instead of copying the metadata from a full other pointer, just remove the blocker to letting you pass around the metadata directly.

It's not obvious to me that people really ever want this version where you need to pass a full pointer, just to ignore its address. Or is there some situation I'm not thinking of where that's really better than doing a let (p, m) = pointer.parts(); then p.whatever().with_metadata(m)?

@GnomedDev
Copy link
Contributor

GnomedDev commented Oct 3, 2024

As I said above @scottmcm:

  • Without pointer metadata: This is a simpler API that can be stable now to allow people to do what they are already doing but soundly (without assuming fat pointer layout).
  • With pointer metadata: This is a helper function for the situation where someone does have a full pointer and can avoid splitting it into parts just to add the metadata on.

My motivating example is https://github.com/andylokandy/smallbox which has been assuming fat pointer layout for years and I recently added a nightly feature which uses this API and strict provenance. Since strict provenance is in the process of being stabilized, this being stabilized as well would be amazing for smallbox.

@HeroicKatora
Copy link
Contributor

HeroicKatora commented Oct 3, 2024

The ACP is not about stabilizing <T as Pointee>::Metadata but rather only a wrapper. @scottmcm Would it be possible to reflect that in the Unresolved questions of the tracking issue #75091 (comment) that only point to the Pointee::Metadata trait.

That is, a way to represent *const T without an address, for in cases where that address is redundant as here. The value for library authors in having the method proposed here is for storing / dealing with a the unsized pointer whose address is ignored anyways since the provenance is also lost when passed as argument. The proposed struct thus reflects the mechanism quite well. If we had an adjusted type such as that then the signature here might as well be:

  • <*const T>::with_metadata_of<U>(PointeeMetadata<U>)

I still think that the with_metadata_of method is more intuitive in usage than any from_raw_parts design, as explained in the PR that changed it originally. However, rust-lang/libs-team#246 's API sketch could be reworked into a similar design. There's no need to do as much with free methods as in the sketch. For instance such a compromise could look like:

pub struct Metadata<T: ?Sized>(<T as Pointee>::Metadata);

impl<T: ?Sized> Metadata<T> {
    /// Not taking up space of `new() where T: Sized`.
    pub fn for_ptr(ptr: *const T) -> Self;
}

// impl for Metadata<T>: Copy, Eq, Ord, Hash

impl<T: ?Sized> *const T {
    pub fn metadata(self) -> Metadata<T>;
    pub fn with_metadata_of<U: ?Sized>(self, other: Metadata<U>) -> *const U;
}

impl<T: ?Sized> *mut T {
    pub fn metadata(self) -> Metadata<T>;
    pub fn with_metadata_of<U: ?Sized>(self, other: Metadata<U>) -> *mut U;
}

I've got a bunch of lines of reasoning I pondered going in the other direction but ultimately decided they wouldn't be persuasive:

In terms of simple vs. complex there is prior art: mem::size_of::<T>() vs Layout::new::<T>().size(). It also happens that the semantics of the composite are simpler than the semantics of its parts. This also reduce the number of feature interactions that might complicate any design phases. (Note: size was const-stable before Layout was stabilized and then it took 24 versions to transfer that). Weak because: I don't think time should be too influential in the design of such a smallish feature. And also the alternative is almost as simple.

Answering the question of whether usability, we can look at rustc's usage. I don't think it matters greatly to library authors and usage whether metadata would be stored on its own or in a fat-ptr with null address.

  • rc/arc instantly use the metadata pointer after an unsizing cast. That's also addressed by the alternative: "It's not practical to provide unsizing support directly for ::Metadata, but it can be added relatively straightforwardly for a proper Metadata struct". Slightly more verbose but not really more complex:
    • mem.with_metadata_of((ptr as *const ArcInner<T>).metadata()) (in case no unsizing cast and no method is provided)
    • mem.with_metadata_of(ptr.metadata() as Metadata<ArcInner<T>>)
    • mem.with_metadata_of(ptr.metadata().cast<ArcInner<T>>())
  • The use in pointer types such as byte_offset is a little odd. They do __modify_address(self).with_metadata_of(self). It shouldn't be problematic to split this into two lines or adding the metadata constructor but it would be a little noisy. Alas it's not entirely clear if they should then be written with from_raw_parts or not. (In particular mask which calls an intrinsic already operating on ()-pointers).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-raw-pointers Area: raw pointers, MaybeUninit, NonNull B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. Libs-Tracked Libs issues that are tracked on the team's project board. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests