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

Arbitrary self types v2: stabilize #135881

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

adetaylor
Copy link
Contributor

@adetaylor adetaylor commented Jan 22, 2025

This PR stabilizes the arbitrary self types v2 feature, tracked in #44874.

r? @wesleywiser

Stabilization report

I'd like to stabilize Arbitrary Self Types in some upcoming Rust version. A stabilization PR is here. What follows is the list of questions from Niko's new template plus various sections I've seen in other stabilization reports.

Summary

This feature allows custom smart pointer types to be used as method receivers:

#![feature(arbitrary_self_types)]

struct MySmartPtr<T>(T);

impl<T> core::ops::Receiver for MySmartPtr<T> {
  type Target = T;
}

struct Content;

impl Content {
  fn method(self: MySmartPtr<Self>) { // note self type
  }
}

fn main() {
  let p = MySmartPtr(Content);
  p.method();
}

What is the RFC for this feature and what changes have occurred to the user-facing design since the RFC was finalized?

Changes since the RFC:

  • The RFC specified additional diagnostics where people forgot to add a T: ?Sized bound - this is in progress here - @cramertj has kindly taken this on, and has determined that this is a pre-existing more general problem which she'll work on. We do not need to block stabilization to wait for this. (The other diagnostics noted in the RFC have been added).

In two cases the RFC wasn't very specific and the implementation has required some choices:

  • The RFC specified that we should ban "generic" arbitrary self types, but we failed to define what that actually meant. Subsequent discussion yielded a fairly narrow definition of "generic".
  • The RFC specifies that we should attempt to detect methods on smart pointers which might "shadow" pre-existing methods on the pointee. We are going ahead and doing this, but some simplifying assumptions were made, as noted in this comment. The likelihood of shadowing occurring in these cases seems low, relative to the likelihood of false positives in these cases.

What behavior are we committing to that has been controversial? Summarize the major arguments pro/con.

In general, Arbitrary Self Types is the opposite of controversial. It has always been anomalous that some specific stdlib smart pointer types have been hard-coded; this work removes that hard-coding.

The specific points which have involved discussion during the process:

  • A prior version of Arbitrary Self Types relied on the Deref trait. This version introduces a new Receiver trait, such that types can be used as self types even if they can't safely devolve to a reference (e.g. because they're a wrapper for a pointer that can't safely comply with Rust reference semantics). This has been largely uncontroversial, but I'd note there's a blanket implementation of Receiver for T: Deref. This ensures all existing smart pointer types continue to work, and reduces complexity in users' brains. I'm 100% sure that this is the right thing to do, but highlighting it here because blanket impls are fairly unusual in the Rust standard library.
  • Most of the discussion and complexity has been around the deshadowing algorithm. We can't add new methods to Rust smart pointer types - e.g. Rc, Box - because we may generate errors if they match the name of methods in pointees. It's already good practice to avoid these because of the risk of shadowing pointee methods, so these types tend to have associated functions instead. Now, any such shadowing will generate an error instead of silently shadowing.
  • For a while we were considering a much more complex version which would have allowed us to add new methods to Rc, Box etc. and allow smart pointer types such as NonNull and Weak. We decided not to do that.
  • There is a theoretical possibility of breaking existing code noted in this comment - which we don't think can happen in practice because any such code would be pointless. (Update: we currently think it's impossible to have written such code, so this concern may not exist at all.)

Are there extensions to this feature that remain unstable? How do we know that we are not accidentally committing to those.

There is an arbitrary_self_types_pointers feature gate which allows raw pointers to be used as self types. Arguments against using this are summarized here.

This was a pre-existing aspect of the former arbitrary_self_types feature gate which has been split out into its own new feature gate because we don't want to stabilize it at this time, but we don't feel a strong need to remove this option from nightly Rust users.

Summarize the major parts of the implementation and provide links into the code (or to PRs)

The three main PRs are:

Summarize existing test coverage of this feature

Has a call-for-testing period been conducted? If so, what feedback was received?

No; though an earlier version of arbitrary self types has been in nightly for years. This has been experimented with by multiple communities - Rust/C++ interop, Rust/Python interop, and Rust for Linux. As the RFC notes, v2 was proposed based on experiences with v1.

What outstanding bugs in the issue tracker involve this feature? Are they stabilization-blocking?

  • We need to land this fix for it to work well with the unstable DispatchFromDyn and the being-stabilized CoercePointee. This should block stabilization of either this feature, or CoercePointee.

Summarize contributors to the feature by name for recognition and assuredness that people involved in the feature agree with stabilization

Very sorry to those who I've missed; it's been a long road :)

What FIXMEs are still in the code for that feature and why is it ok to leave them there?

None.

What static checks are done that are needed to prevent undefined behavior?

None.

In what way does this feature interact with the reference/specification, and are those edits prepared

Does this feature introduce new expressions and can they produce temporaries? What are the lifetimes of those temporaries?

No.

What other unstable features may be exposed by this feature?

None, though the separate derive(CoercePointee) becomes more useful when this is also stabilized.

What is tooling support like for this feature, rustdoc/clippy/rust-analzyer/rustfmt/etc

I don't anticipate any work here being needed other than for rust-analyzer, tracked here.

@rustbot rustbot added A-rustdoc-json Area: Rustdoc JSON backend PG-exploit-mitigations Project group: Exploit mitigations S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jan 22, 2025
@rust-log-analyzer

This comment has been minimized.

@traviscross traviscross added T-lang Relevant to the language team, which will review and decide on the PR/issue. and removed T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. A-rustdoc-json Area: Rustdoc JSON backend PG-exploit-mitigations Project group: Exploit mitigations labels Jan 22, 2025
@adetaylor
Copy link
Contributor Author

@rustbot label -S-waiting-on-review

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 23, 2025
@adetaylor adetaylor force-pushed the stabilize-arbitrary-self-types branch from 78dbe11 to 1ce21e5 Compare January 23, 2025 08:11
@rustbot rustbot added A-rustdoc-json Area: Rustdoc JSON backend PG-exploit-mitigations Project group: Exploit mitigations labels Jan 23, 2025
@rust-log-analyzer

This comment has been minimized.

@adetaylor adetaylor force-pushed the stabilize-arbitrary-self-types branch from 1ce21e5 to 0d8e767 Compare January 23, 2025 14:37
@rust-log-analyzer

This comment has been minimized.

@nikomatsakis
Copy link
Contributor

Thanks for this! I already see an edit to the template that could be useful, clarifying what static checks are required and linking to tests that demonstrate them, but it kind of duplicates the reference/spec work

@PoignardAzur
Copy link
Contributor

So the report mentions that testing already happened in a few forms.

How interested would you be in other libraries using this feature? We could implement it in Masonry, but since we don't want our crate to be nightly-only, we'd have to either use a feature flag or keep it to a separate branch.

If we do end up writing an implementation, is there any kind of feedback you'd be specifically interested in?

@adetaylor
Copy link
Contributor Author

How interested would you be in other libraries using this feature? We could implement it in Masonry, but since we don't want our crate to be nightly-only, we'd have to either use a feature flag or keep it to a separate branch.

Good question and thanks for the offer! I personally have run out of runway to work on this feature - so I'm keeping my fingers crossed that no major semantic changes are needed. Or if they are, it will need someone else to pick up the work. So, the most interesting and useful testing to me would be looking for corner cases or oddities in the current implementation which cause ICEs or other oddities. (An example is the interaction with CoercePointee which caused a crash). It's hard for me to guess how likely it is that you'd find any such problem. Obviously I hope you wouldn't, but real world testing always has a habit of finding fun surprises.

Overall: up to you! If you think it would be useful for Masonry long-term then perhaps it's worth having a play in the hopes and expectations that this will be stabilized soon and you can therefore merge it into your main branch before too long.

@obi1kenobi
Copy link
Member

I'd like to ask that the cargo SemVer reference be updated to describe any new SemVer hazards that are introduced here. This doesn't have to be a stabilization blocker, but I'd appreciate it if it can be done at least shortly thereafter so I can make sure cargo-semver-checks quickly offers the best possible support for linting this excellent new functionality.

@adetaylor
Copy link
Contributor Author

Rebasing this breaks the miri build, for reasons that will be tracked in #138065.

rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Mar 5, 2025
Rollup merge of rust-lang#136764 - traviscross:TC/make-ptr_cast_add_auto_to_object-hard-error, r=oli-obk

Make `ptr_cast_add_auto_to_object` lint into hard error

In Rust 1.81, we added a FCW lint (including linting in dependencies) against pointer casts that add an auto trait to dyn bounds.  This was part of work making casts of pointers involving trait objects stricter, and was part of the work needed to restabilize trait upcasting.

We considered just making this a hard error, but opted against it at that time due to breakage found by crater.  This breakage was mostly due to the `anymap` crate which has been a persistent problem for us.

It's now a year later, and the fact that this is not yet a hard error is giving us pause about stabilizing arbitrary self types and `derive(CoercePointee)`.  So let's see about making a hard error of this.

r? ghost

cc ```@adetaylor``` ```@Darksonn``` ```@BoxyUwU``` ```@RalfJung``` ```@compiler-errors``` ```@oli-obk``` ```@WaffleLapkin```

Related:

- rust-lang#135881
- rust-lang#136702
- rust-lang#136776

Tracking:

- rust-lang#127323
- rust-lang#44874
- rust-lang#123430
@bors
Copy link
Contributor

bors commented Mar 5, 2025

☔ The latest upstream changes (presumably #138058) made this pull request unmergeable. Please resolve the merge conflicts.

@adetaylor adetaylor force-pushed the stabilize-arbitrary-self-types branch from 632fd28 to 4faaac4 Compare March 5, 2025 21:32
@rust-log-analyzer

This comment has been minimized.

github-actions bot pushed a commit to rust-lang/rustc-dev-guide that referenced this pull request Mar 6, 2025
…ject-hard-error, r=oli-obk

Make `ptr_cast_add_auto_to_object` lint into hard error

In Rust 1.81, we added a FCW lint (including linting in dependencies) against pointer casts that add an auto trait to dyn bounds.  This was part of work making casts of pointers involving trait objects stricter, and was part of the work needed to restabilize trait upcasting.

We considered just making this a hard error, but opted against it at that time due to breakage found by crater.  This breakage was mostly due to the `anymap` crate which has been a persistent problem for us.

It's now a year later, and the fact that this is not yet a hard error is giving us pause about stabilizing arbitrary self types and `derive(CoercePointee)`.  So let's see about making a hard error of this.

r? ghost

cc ```@adetaylor``` ```@Darksonn``` ```@BoxyUwU``` ```@RalfJung``` ```@compiler-errors``` ```@oli-obk``` ```@WaffleLapkin```

Related:

- rust-lang/rust#135881
- rust-lang/rust#136702
- rust-lang/rust#136776

Tracking:

- rust-lang/rust#127323
- rust-lang/rust#44874
- rust-lang/rust#123430
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Mar 6, 2025
…ject-hard-error, r=oli-obk

Make `ptr_cast_add_auto_to_object` lint into hard error

In Rust 1.81, we added a FCW lint (including linting in dependencies) against pointer casts that add an auto trait to dyn bounds.  This was part of work making casts of pointers involving trait objects stricter, and was part of the work needed to restabilize trait upcasting.

We considered just making this a hard error, but opted against it at that time due to breakage found by crater.  This breakage was mostly due to the `anymap` crate which has been a persistent problem for us.

It's now a year later, and the fact that this is not yet a hard error is giving us pause about stabilizing arbitrary self types and `derive(CoercePointee)`.  So let's see about making a hard error of this.

r? ghost

cc ```@adetaylor``` ```@Darksonn``` ```@BoxyUwU``` ```@RalfJung``` ```@compiler-errors``` ```@oli-obk``` ```@WaffleLapkin```

Related:

- rust-lang/rust#135881
- rust-lang/rust#136702
- rust-lang/rust#136776

Tracking:

- rust-lang/rust#127323
- rust-lang/rust#44874
- rust-lang/rust#123430
@@ -365,16 +365,18 @@ unsafe impl<T: ?Sized> DerefPure for &mut T {}
/// }
/// ```
#[lang = "receiver"]
#[unstable(feature = "arbitrary_self_types", issue = "44874")]
#[cfg(not(bootstrap))]
Copy link
Member

Choose a reason for hiding this comment

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

What is the point of totally cfg'ing out the receiver trait here when bootstrap is enabled?

Copy link
Contributor Author

@adetaylor adetaylor Mar 7, 2025

Choose a reason for hiding this comment

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

I think this was necessary in the past because the bootstrap compiler didn't understand the lang_item (though my memory might be wrong). I'm assuming that may not be the case any longer. I'll try removing it. Maybe it'll help with the CI problem.

Copy link
Member

@compiler-errors compiler-errors Mar 7, 2025

Choose a reason for hiding this comment

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

It almost certainly will fix the CI problem. The receiver trait exists today so this PR is essentially just removing a trait from the standard library in cfg(bootstrap) mode, which is going to cause failures since we use the Receiver trait in the compiler.

Update the unstable book to indicate that this feature is now
stabilized.
All the test changes necessary for stabilization here.
@adetaylor adetaylor force-pushed the stabilize-arbitrary-self-types branch from 4faaac4 to a415b41 Compare March 7, 2025 17:21
@rust-log-analyzer

This comment has been minimized.

@compiler-errors
Copy link
Member

You probably want to add a #[cfg_attr(bootstrap, doc = "#[feature(arbitrary_self_types)]")] or however you spell it, so that you can conditionally gate whatever doc test under the feature gate when bootstrapping. Or just copy the item and remove the doctest from the bootstrap version.

@adetaylor
Copy link
Contributor Author

For now I'm going to mark the doctest compile_fail just to see if we get past the miri problems and to see what else crops up.

@adetaylor adetaylor force-pushed the stabilize-arbitrary-self-types branch from a415b41 to e6e8529 Compare March 7, 2025 18:05
@rust-log-analyzer

This comment has been minimized.

@adetaylor adetaylor force-pushed the stabilize-arbitrary-self-types branch from e6e8529 to 3825ac6 Compare March 7, 2025 21:56
For now, this disables a doctest, which otherwise fails. We'll want to
re-enable that later.
@adetaylor adetaylor force-pushed the stabilize-arbitrary-self-types branch from 3825ac6 to f0a0065 Compare March 7, 2025 23:09
@bors
Copy link
Contributor

bors commented Mar 8, 2025

☔ The latest upstream changes (presumably #138208) made this pull request unmergeable. Please resolve the merge conflicts.

lnicola pushed a commit to lnicola/rust-analyzer that referenced this pull request Mar 10, 2025
…ject-hard-error, r=oli-obk

Make `ptr_cast_add_auto_to_object` lint into hard error

In Rust 1.81, we added a FCW lint (including linting in dependencies) against pointer casts that add an auto trait to dyn bounds.  This was part of work making casts of pointers involving trait objects stricter, and was part of the work needed to restabilize trait upcasting.

We considered just making this a hard error, but opted against it at that time due to breakage found by crater.  This breakage was mostly due to the `anymap` crate which has been a persistent problem for us.

It's now a year later, and the fact that this is not yet a hard error is giving us pause about stabilizing arbitrary self types and `derive(CoercePointee)`.  So let's see about making a hard error of this.

r? ghost

cc ```@adetaylor``` ```@Darksonn``` ```@BoxyUwU``` ```@RalfJung``` ```@compiler-errors``` ```@oli-obk``` ```@WaffleLapkin```

Related:

- rust-lang/rust#135881
- rust-lang/rust#136702
- rust-lang/rust#136776

Tracking:

- rust-lang/rust#127323
- rust-lang/rust#44874
- rust-lang/rust#123430
@obi1kenobi
Copy link
Member

Thrilled to see the amazing work being done here and in related PRs. My most sincere thanks and admiration for everyone helping push this work forward 🙇

I was playing with the arbitrary_self_types feature today as part of some cargo-semver-checks work, and I noticed a behavior I found surprising: Pin<P> as a receiver but does not directly allow setting P to a custom impl Receiver type. For example: playground

#![feature(arbitrary_self_types)]

pub struct Example(i64);

pub struct CustomReceiver<T>(T);

impl<T> std::ops::Receiver for CustomReceiver<T> {
    type Target = T;
}

impl Example {
    pub fn by_pinned_custom_receiver(self: std::pin::Pin<CustomReceiver<Self>>) {}
}

produces the following error:

error[E0307]: invalid `self` parameter type: `Pin<CustomReceiver<Example>>`
  --> src/lib.rs:12:44
   |
12 |     pub fn by_pinned_custom_receiver(self: std::pin::Pin<CustomReceiver<Self>>) {}
   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: type of `self` must be `Self` or some type implementing `Receiver`
   = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>`

For more information about this error, try `rustc --explain E0307`.

Changing self: std::pin::Pin<CustomReceiver<Self>> to self: std::pin::Pin<&mut CustomReceiver<Self>> (i.e. changing Pin<P> from P = CustomReceiver<Self> to P = &mut CustomReceiver<Self>) avoids the error.

Perhaps this is entirely expected, and it is merely my naïveté causing me to be surprised. I thought I'd flag it on the off chance it either is genuinely unexpected, or deserving of closer documentation than the present trait Receiver and Pin-as-receiver-type documentation currently offer.

@madsmtm
Copy link
Contributor

madsmtm commented Mar 10, 2025

Yeah, we can't impl<T: Receiver> Receiver for Pin<T> because of the blanket Deref impl (I think it's the same underlying issue as discussed further up, see in particular this response).

@obi1kenobi
Copy link
Member

obi1kenobi commented Mar 10, 2025

Neat, so it sounds like this is a known issue — thank you. This implication of the blanket Deref impl wasn't obvious to me. Is this worth specifically calling out in docs? I'll leave that question in the capable hands of everyone here 🙏

Thanks again for driving this amazing work forward!

@traviscross
Copy link
Contributor

traviscross commented Mar 10, 2025

That is a particularly unfortunate and motivating example of it though. Thanks for mentioning that @obi1kenobi.

Here's that spelled out a bit:

Playground link

That is motivating enough we should discuss it -- I don't recall us ever considering this one, and it is bad -- to see how we might eventually be able to address it.

@rustbot labels +I-lang-nominated

@rustbot rustbot added the I-lang-nominated Nominated for discussion during a lang team meeting. label Mar 10, 2025
@traviscross
Copy link
Contributor

@rfcbot concern discuss-plan-for-pin

@programmerjake
Copy link
Member

programmerjake commented Mar 11, 2025

I started a conversation on maybe allowing overlapping impls only for a impl<T: ?Sized + Receiver> Receiver for Pin<T> in core:
#t-types > impl Receiver for Pin and overlapping impls

alternative idea: #t-lang > impl Receiver for Pin and impl Receiver for impl Deref

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-rustdoc-json Area: Rustdoc JSON backend A-rustdoc-search Area: Rustdoc's search feature disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. I-lang-nominated Nominated for discussion during a lang team meeting. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. PG-exploit-mitigations Project group: Exploit mitigations proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-lang Relevant to the language team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.