Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#![feature(arbitrary_self_types, unsize, coerce_unsized, dispatch_from_dyn)]

use std::marker::Unsize;
use std::ops::{CoerceUnsized, Deref, DispatchFromDyn};
use std::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};

struct Ptr<T: ?Sized>(Box<T>);

Expand All @@ -14,6 +14,9 @@ impl<T: ?Sized> Deref for Ptr<T> {
&*self.0
}
}
impl<T: ?Sized> Receiver for Ptr<T> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are these impls required? I imagine this might be a consequence of my comment in wfcheck?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is still an open question.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If these impls are required, is it just because of arbitrary_self_types?

type Target = T;
}

impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
impl<T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<Ptr<U>> for Ptr<T> {}
Expand All @@ -27,6 +30,9 @@ impl<T: ?Sized> Deref for Wrapper<T> {
&self.0
}
}
impl<T: ?Sized> Receiver for Wrapper<T> {
type Target = T;
}

impl<T: CoerceUnsized<U>, U> CoerceUnsized<Wrapper<U>> for Wrapper<T> {}
impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
#![feature(rustc_attrs)]
#![allow(internal_features)]

use std::{
ops::{Deref, CoerceUnsized, DispatchFromDyn},
marker::Unsize,
};
use std::marker::Unsize;
use std::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};

struct Ptr<T: ?Sized>(Box<T>);

Expand All @@ -18,6 +16,9 @@ impl<T: ?Sized> Deref for Ptr<T> {
&*self.0
}
}
impl<T: ?Sized> Receiver for Ptr<T> {
type Target = T;
}

impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
impl<T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<Ptr<U>> for Ptr<T> {}
Expand All @@ -31,11 +32,13 @@ impl<T: ?Sized> Deref for Wrapper<T> {
&self.0
}
}
impl<T: ?Sized> Receiver for Wrapper<T> {
type Target = T;
}

impl<T: CoerceUnsized<U>, U> CoerceUnsized<Wrapper<U>> for Wrapper<T> {}
impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {}


trait Trait {
fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32;
fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32;
Expand Down
10 changes: 2 additions & 8 deletions compiler/rustc_error_codes/src/error_codes/E0307.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,16 @@ impl Trait for Foo {
The nightly feature [Arbitrary self types][AST] extends the accepted
set of receiver types to also include any type that implements the
`Receiver` trait and can follow its chain of `Target` types to `Self`.
There's a blanket implementation of `Receiver` for `T: Deref`, so any
type which dereferences to `Self` can be used.

```
#![feature(arbitrary_self_types)]

struct Foo;
struct Bar;

// Because you can dereference `Bar` into `Foo`...
impl std::ops::Deref for Bar {
// Because you can set `Bar` as method receiver for `Foo`...
impl std::ops::Receiver for Bar {
type Target = Foo;

fn deref(&self) -> &Foo {
&Foo
}
}

impl Foo {
Expand Down
72 changes: 62 additions & 10 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::cell::RefCell;

use rustc_data_structures::unord::UnordMap;
use rustc_hir::limit::Limit;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::PredicateObligations;
Expand Down Expand Up @@ -26,6 +29,39 @@ struct AutoderefSnapshot<'tcx> {
obligations: PredicateObligations<'tcx>,
}

#[derive(Debug, Default)]
pub struct AutoderefCache<'tcx> {
next_deref: RefCell<UnordMap<Ty<'tcx>, (Ty<'tcx>, PredicateObligations<'tcx>)>>,
next_receiver: RefCell<UnordMap<Ty<'tcx>, (Ty<'tcx>, PredicateObligations<'tcx>)>>,
}
Comment on lines +33 to +36
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This definitely needs a comment describing what this cache is, how it works, and why it's necessary.


impl<'tcx> AutoderefCache<'tcx> {
pub fn get(
&self,
use_receiver: bool,
ty: Ty<'tcx>,
) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> {
if use_receiver {
self.next_receiver.borrow().get(&ty).cloned()
} else {
self.next_deref.borrow().get(&ty).cloned()
}
}
pub fn cache(
&self,
use_receiver: bool,
ty: Ty<'tcx>,
next: Ty<'tcx>,
predicates: PredicateObligations<'tcx>,
) {
if use_receiver {
self.next_receiver.borrow_mut().insert(ty, (next, predicates));
} else {
self.next_deref.borrow_mut().insert(ty, (next, predicates));
}
}
}

/// Recursively dereference a type, considering both built-in
/// dereferences (`*`) and the `Deref` trait.
/// Although called `Autoderef` it can be configured to use the
Expand All @@ -36,6 +72,7 @@ pub struct Autoderef<'a, 'tcx> {
span: Span,
body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
cache: Option<&'a AutoderefCache<'tcx>>,

// Current state:
state: AutoderefSnapshot<'tcx>,
Expand Down Expand Up @@ -80,16 +117,19 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
}

// Otherwise, deref if type is derefable:
// NOTE: in the case of self.use_receiver_trait = true, you might think it would
// be better to skip this clause and use the Overloaded case only, since &T
// and &mut T implement Receiver. But built-in derefs apply equally to Receiver
// and Deref, and this has benefits for const and the emitted MIR.
// NOTE: in the case of self.use_receiver_trait = true,
// Autoderef works only with Receiver trait.
// Caller is expecting us to expand the Receiver chain only.
let (kind, new_ty) =
if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty));
// NOTE: we may still need to normalize the built-in deref in case
// we have some type like `&<Ty as Trait>::Assoc`, since users of
// autoderef expect this type to have been structurally normalized.
// NOTE: even when we follow Receiver chain we still unwrap
// references and pointers here, but this is only symbolic and
// we are not going to really dereferences any references or pointers.
// That happens when autoderef is chasing the Deref chain.
if self.infcx.next_trait_solver()
&& let ty::Alias(..) = ty.kind()
{
Expand Down Expand Up @@ -122,13 +162,15 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
impl<'a, 'tcx> Autoderef<'a, 'tcx> {
pub fn new(
infcx: &'a InferCtxt<'tcx>,
cache: Option<&'a AutoderefCache<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
body_def_id: LocalDefId,
span: Span,
base_ty: Ty<'tcx>,
) -> Self {
Autoderef {
infcx,
cache,
span,
body_id: body_def_id,
param_env,
Expand All @@ -145,13 +187,19 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
}
}

#[instrument(level = "debug", skip(self), ret)]
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
debug!("overloaded_deref_ty({:?})", ty);
let tcx = self.infcx.tcx;

if ty.references_error() {
return None;
}
if let Some(cache) = &self.cache
&& let Some((ty, obligations)) = cache.get(self.use_receiver_trait, self.state.cur_ty)
{
self.state.obligations.extend(obligations);
return Some(ty);
}

// <ty as Deref>, or whatever the equivalent trait is that we've been asked to walk.
let (trait_def_id, trait_target_def_id) = if self.use_receiver_trait {
Expand All @@ -167,18 +215,23 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
self.param_env,
ty::Binder::dummy(trait_ref),
);
// We detect whether the self type implements `Deref` before trying to
// We detect whether the self type implements `Deref`/`Receiver` before trying to
// structurally normalize. We use `predicate_may_hold_opaque_types_jank`
// to support not-yet-defined opaque types. It will succeed for `impl Deref`
// but fail for `impl OtherTrait`.
if !self.infcx.predicate_may_hold_opaque_types_jank(&obligation) {
debug!("overloaded_deref_ty: cannot match obligation");
debug!("cannot match obligation");
return None;
}

let (normalized_ty, obligations) =
self.structurally_normalize_ty(Ty::new_projection(tcx, trait_target_def_id, [ty]))?;
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
debug!(?ty, ?normalized_ty, ?obligations);
if matches!(ty.kind(), ty::Adt(..))
&& let Some(cache) = &self.cache
{
cache.cache(self.use_receiver_trait, ty, normalized_ty, obligations.clone());
}
self.state.obligations.extend(obligations);

Some(self.infcx.resolve_vars_if_possible(normalized_ty))
Expand Down Expand Up @@ -255,11 +308,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
/// Use `core::ops::Receiver` and `core::ops::Receiver::Target` as
/// the trait and associated type to iterate, instead of
/// `core::ops::Deref` and `core::ops::Deref::Target`
pub fn use_receiver_trait(mut self) -> Self {
pub fn follow_receiver_chain(mut self) -> Self {
self.use_receiver_trait = true;
self
}

pub fn silence_errors(mut self) -> Self {
self.silence_errors = true;
self
Expand Down
22 changes: 14 additions & 8 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1800,7 +1800,9 @@ fn check_method_receiver<'tcx>(
// Report error; would not have worked with `arbitrary_self_types[_pointers]`.
{
match receiver_validity_err {
ReceiverValidityError::DoesNotDeref if arbitrary_self_types_level.is_some() => {
ReceiverValidityError::DoesNotReceive
if arbitrary_self_types_level.is_some() =>
{
let hint = match receiver_ty
.builtin_deref(false)
.unwrap_or(receiver_ty)
Expand All @@ -1814,7 +1816,7 @@ fn check_method_receiver<'tcx>(

tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty, hint })
}
ReceiverValidityError::DoesNotDeref => {
ReceiverValidityError::DoesNotReceive => {
tcx.dcx().emit_err(errors::InvalidReceiverTyNoArbitrarySelfTypes {
span,
receiver_ty,
Expand All @@ -1836,7 +1838,7 @@ fn check_method_receiver<'tcx>(
enum ReceiverValidityError {
/// The self type does not get to the receiver type by following the
/// autoderef chain.
DoesNotDeref,
DoesNotReceive,
/// A type was found which is a method type parameter, and that's not allowed.
MethodGenericParamUsed,
}
Expand Down Expand Up @@ -1892,17 +1894,21 @@ fn receiver_is_valid<'tcx>(

confirm_type_is_not_a_method_generic_param(receiver_ty, method_generics)?;

let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
let cache = Default::default();
let mut autoderef =
Autoderef::new(infcx, Some(&cache), wfcx.param_env, wfcx.body_def_id, span, receiver_ty);

// The `arbitrary_self_types` feature allows custom smart pointer
// types to be method receivers, as identified by following the Receiver<Target=T>
// types to be method receivers, as identified by following the Receiver<Target = T>
// chain.
if arbitrary_self_types_enabled.is_some() {
autoderef = autoderef.use_receiver_trait();
// We are in the wf check, so we would like to deref the references in the type head.
// However, we do not want to walk `Deref` chain.
autoderef = autoderef.follow_receiver_chain();
Comment on lines +1905 to +1907
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm curious why that is here? But in confirm we do?

}

// The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`.
if arbitrary_self_types_enabled == Some(ArbitrarySelfTypesLevel::WithPointers) {
if matches!(arbitrary_self_types_enabled, Some(ArbitrarySelfTypesLevel::WithPointers)) {
autoderef = autoderef.include_raw_pointers();
}

Expand Down Expand Up @@ -1956,7 +1962,7 @@ fn receiver_is_valid<'tcx>(
}

debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
Err(ReceiverValidityError::DoesNotDeref)
Err(ReceiverValidityError::DoesNotReceive)
}

fn legacy_receiver_is_implemented<'tcx>(
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1727,7 +1727,8 @@ pub(crate) enum InvalidReceiverTyHint {
#[diag("invalid `self` parameter type: `{$receiver_ty}`", code = E0307)]
#[note("type of `self` must be `Self` or a type that dereferences to it")]
#[help(
"consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)"
"consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` where P is one of the previous types except `Self`; \
alternatively, consider implement `Receiver` trait on the type of `self`, where applicable"
)]
pub(crate) struct InvalidReceiverTyNoArbitrarySelfTypes<'tcx> {
#[primary_span]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use super::{FnCtxt, PlaceOp};

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> {
Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
Autoderef::new(self, None, self.param_env, self.body_id, span, base_ty)
}

pub(crate) fn try_overloaded_deref(
Expand Down
32 changes: 26 additions & 6 deletions compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Deref;

use rustc_hir as hir;
Expand Down Expand Up @@ -356,18 +357,37 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// yield an object-type (e.g., `&Object` or `Box<Object>`
// etc).

let mut autoderef = self.fcx.autoderef(self.span, self_ty);
let fcx = self.fcx;
let span = self.span;
let autoderef = fcx.autoderef(span, self_ty);

// We don't need to gate this behind arbitrary self types
// per se, but it does make things a bit more gated.
if self.tcx.features().arbitrary_self_types()
|| self.tcx.features().arbitrary_self_types_pointers()
{
autoderef = autoderef.use_receiver_trait();
}
let follow_receiver_chain = self.tcx.features().arbitrary_self_types()
|| self.tcx.features().arbitrary_self_types_pointers();

autoderef
.include_raw_pointers()
.flat_map(|(ty, derefs)| {
enum EitherIter<A, B, C> {
A(A, PhantomData<fn() -> C>),
B(B),
}
impl<A: Iterator<Item = C>, B: Iterator<Item = C>, C> Iterator for EitherIter<A, B, C> {
type Item = C;
fn next(&mut self) -> Option<Self::Item> {
match self {
EitherIter::A(a, _) => a.next(),
EitherIter::B(b) => b.next(),
}
}
}
if follow_receiver_chain {
EitherIter::A(fcx.autoderef(span, ty).follow_receiver_chain(), PhantomData)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Okay, so if I'm understanding this correctly, for each step in Deref chain, we then try a Receiver chain? That's pretty crazy, and is very likely going to have an effect on perf.

I would really like to evaluate perf before and after this PR (when enabling arbitrary self types).

Copy link
Copy Markdown
Contributor Author

@dingxiangfei2009 dingxiangfei2009 Nov 18, 2025

Choose a reason for hiding this comment

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

I think we are already performing quadratic time probing with how Receiver is set up on main branch.

  • FnCtxt::probe_op walks the Deref chain followed by Receiver extension chain for candidate Self type. They are collected into steps.
  • ProbeContext::assemble_* collects all methods with applicable Self type, for each Self from steps.
  • ProbeContext::pick_all_method does the following. For each type from steps, only when it is a member of the Deref chain which corresponds to a possible self value, this function calls out to ProbeContext::pick_method among all candidate method items. It will try to unify the type of self with that of self argument in the method signature. That is how we find a method match with one self and one Self in a right impl.

I am thinking of sorting the candidates into bins for each candidate Self type. With this structure we save some search effort. Let me work on a patch on this idea.

} else {
EitherIter::B([(ty, derefs)].into_iter())
}
})
.find_map(|(ty, _)| match ty.kind() {
ty::Dynamic(data, ..) => Some(closure(
self,
Expand Down
Loading