Skip to content

Refactor unsizing coercion documentation #1731

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

WaffleLapkin
Copy link
Member

The old one was quite confusing and also incorrect in a few places. Follow-up to #1622.

@rustbot rustbot added the S-waiting-on-review Status: The marked PR is awaiting review from a maintainer label Feb 9, 2025
@WaffleLapkin WaffleLapkin force-pushed the unsize_coercion_rewrite branch 3 times, most recently from 6f8650a to 08bec64 Compare February 9, 2025 16:56
@rustbot

This comment has been minimized.

@ehuss
Copy link
Contributor

ehuss commented Mar 13, 2025

@rust-lang/types would you be able to review this?

@nikomatsakis
Copy link
Contributor

To add a bit more color, it'd be good to get a double check that the content is factually accurate before spec team takes it on for editorial review.

Comment on lines 270 to 273
<!-- FIXME: are there more requirements for `CoerceUnsized`? -->

r[coerce.unsize.coerce-unsized-impls]
Types which currently implement `CoerceUnsized<_>` (assuming `T: Unsize<U>`, `'a: 'b`, `A: CoerceUnsized<B>`, `Al: Allocator`):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally I would prefer to not duplicate the list of impls from the standard library here. I assume the user can click the link to CoerceUnsized to see which impls it has?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, mh. Yes, users can find them in the std docs (that's where I got the impls from!).

However, if we want the reference to be prescriptive, I think we should include this. While this is library code, it controls how a language feature behaves.

But I don't have a strong opinion here, if you think that this is not useful I can remove the list.

@WaffleLapkin WaffleLapkin force-pushed the unsize_coercion_rewrite branch from 08bec64 to 9271fdd Compare March 13, 2025 17:12
@ehuss
Copy link
Contributor

ehuss commented Apr 17, 2025

@WaffleLapkin To move this along, would it be possible to split this PR up a little so it doesn't get hung up? In particular, I think it would move things faster if we remove the name change (unsized -> unsizing) to avoid getting stuck on bikeshedding (that doesn't mean we can't make that change separately, it just adds friction). And remove the list of CoerceUnsized impls since I think we don't want to go down that road of trying to duplicate the standard library docs (I understand that there is a close relationship of language features with certain parts of the standard library, but other than recognizing a trait or type is special, we usually don't go into details on the library side, and instead defer to those docs).

You can either just make this PR smaller, or open a new smaller PR, whichever is more convenient.

Assigning TC since he asked.

@WaffleLapkin WaffleLapkin force-pushed the unsize_coercion_rewrite branch 2 times, most recently from b68840f to 56ee0bb Compare April 18, 2025 14:41
@jackh726
Copy link
Member

I can review this over the weekend

@WaffleLapkin
Copy link
Member Author

@ehuss I forgot to comment, but I removed the impls and split off the rename.

Copy link
Member

@jackh726 jackh726 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good to me in terms of correctness.

When performing unsized coercion, the pointer metadata type changes. For example, when unsized `&u32` to `&dyn Debug` metadate type changes from `()` to `DynMetadata<dyn Debug>` (note that exact metadata types are not yet stable). This can also lead to a change in the pointer size -- `&u32` is half the size of `&dyn Debug`.

r[coerce.unsize.traits]
Three traits, [`Unsize`], [`CoerceUnsized`], and [`PinCoerceUnsized`] are used to assist in this process and expose it for library use.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be worth clarifying these traits are unstable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a note about this down on line 273.

This is actually an interesting case. Generally in the Reference we don't document the unstable, just the exposed stable behaviors. But here, it's probably indeed easiest to describe those stable behaviors in terms of the unstable traits, and we'd already made this exception.

Additionally, a type `Foo<T>` can implement `CoerceUnsized<Foo<U>>` when `T`
implements `Unsize<U>` or `CoerceUnsized<Foo<U>>`. This allows it to provide an
unsized coercion to `Foo<U>`.
<!-- FIXME: are there more requirements for `CoerceUnsized`? -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I think another constraint here is that only one field can be coerced.

The old one was quite confusing and also incorrect in a few places.
@WaffleLapkin WaffleLapkin force-pushed the unsize_coercion_rewrite branch from 7780344 to cddb6e7 Compare May 11, 2025 01:26
@WaffleLapkin
Copy link
Member Author

Is there anything I can do to move this forward?

let _: Cell<Box<[u8]>> = Cell::new(Box::<[u8; 0]>::new([]));

// The result of the coercion doesn't *have* to be the same pointer-like type,
// alhtough this is only allowed for certain pairs of pointer-like types.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// alhtough this is only allowed for certain pairs of pointer-like types.
// although this is only allowed for certain pairs of pointer-like types.

When performing unsized coercion, the pointer metadata type changes. For example, when unsized `&u32` to `&dyn Debug` metadate type changes from `()` to `DynMetadata<dyn Debug>` (note that exact metadata types are not yet stable). This can also lead to a change in the pointer size -- `&u32` is half the size of `&dyn Debug`.

r[coerce.unsize.traits]
Three traits, [`Unsize`], [`CoerceUnsized`], and [`PinCoerceUnsized`] are used to assist in this process and expose it for library use.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Three traits, [`Unsize`], [`CoerceUnsized`], and [`PinCoerceUnsized`] are used to assist in this process and expose it for library use.
Three internal traits, [`Unsize`], [`CoerceUnsized`], and [`PinCoerceUnsized`] are
used to assist in this process and expose it for library use.

* The last field is the *only* field which type involves `T`.
* The type of the last field implements `Unsize<F>` where `F` is the same type with `T` replaced by `U`. i.e. if the field has type `Bar<T>`, then `Bar<T>` implements `Unsize<Bar<U>>`.

r[coerce.unsize.pointer]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
r[coerce.unsize.pointer]
r[coerce.unsize.struct]

🤔

Comment on lines +255 to 257
* `dyn T: Unsize<dyn U>`, when `U` is one of `T`'s [supertraits].
* This allows dropping auto traits, i.e. `dyn T + Auto` to `dyn U` is allowed.
* This allows adding auto traits if the principal trait has the auto trait as a super trait, i.e. given `trait T: U + Send {}`, `dyn T` to `dyn T + Send` or to `dyn U + Send` coercions are allowed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

existing, but pls change this to use TrA and TrBorFooandBarorTraitandSuper`

I personally tend to get very confused by single letter trait names, esp if we've just used T to mean "any type" 2 lines above

* The type of the last field implements `Unsize<F>` where `F` is the same type with `T` replaced by `U`. i.e. if the field has type `Bar<T>`, then `Bar<T>` implements `Unsize<Bar<U>>`.

r[coerce.unsize.pointer]
A type `S<..., T, ...>` can implement `CoerceUnsized<S<..., U, ...>>` if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see https://github.com/rust-lang/rust/blob/2cd37831b0706c9f2d6e7e20eb556dc7548bf732/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs#L1048

unsizing can change an arbitrary number of struct type/const args as long all of them are only mentioned in the last field and that last field impls CoerceUnsized

The last field of S has a type involving T. i.e. it's either of T or C<..., T, ...> where C is a type constructor and T is only present in it once (C<T, T> is disallowed).

Where do you get the "T is only present once" requirement from?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait, confused by the difference between coerce.unsize.composite and coerce.unsize.pointer 🤔 using

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an example which disagrees with the current docs

trait Trait<T, U> {
    type Assoc: ?Sized;
}

impl<T> Trait<T, u32> for T {
    type Assoc = [u8; 4];
}

impl<T> Trait<T, u16> for T {
    type Assoc = [u8];
}

struct Foo<T: Trait<T, U>, U>(<T as Trait<T, U>>::Assoc);

fn main() {
    let x: &Foo<(), u32> = &Foo([0; 4]);
    let y: &Foo<String, u16> = x;
}

Copy link
Contributor

@lcnr lcnr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits, then r=me from the "accuracy end". Unsure whether I've got the right to merge reference PRs myself

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: The marked PR is awaiting review from a maintainer
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants