Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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> {
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
3 changes: 2 additions & 1 deletion compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ hir_analysis_invalid_receiver_ty_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>`

hir_analysis_invalid_receiver_ty_help_no_arbitrary_self_types =
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

hir_analysis_invalid_receiver_ty_help_nonnull_note =
`NonNull` does not implement `Receiver` because it has methods that may shadow the referent; consider wrapping your `NonNull` in a newtype wrapper for which you implement `Receiver`
Expand Down
20 changes: 11 additions & 9 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,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 @@ -145,8 +148,8 @@ 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() {
Expand All @@ -172,13 +175,13 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
// 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);
self.state.obligations.extend(obligations);

Some(self.infcx.resolve_vars_if_possible(normalized_ty))
Expand Down Expand Up @@ -255,11 +258,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
16 changes: 10 additions & 6 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1722,7 +1722,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 @@ -1736,7 +1738,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 @@ -1758,7 +1760,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 @@ -1813,10 +1815,12 @@ fn receiver_is_valid<'tcx>(
let mut autoderef = Autoderef::new(infcx, 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();
}

// The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`.
Expand Down Expand Up @@ -1870,7 +1874,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
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,3 +1,4 @@
use std::marker::PhantomData;
use std::ops::Deref;

use rustc_hir as hir;
Expand Down Expand Up @@ -350,18 +351,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, PhantomData<fn() -> C>),
}
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)
} else {
EitherIter::B([(ty, derefs)].into_iter(), PhantomData)
}
})
.find_map(|(ty, _)| match ty.kind() {
ty::Dynamic(data, ..) => Some(closure(
self,
Expand Down
Loading
Loading