Skip to content

Commit

Permalink
Stabilize Rc, Arc and Pin as method receivers
Browse files Browse the repository at this point in the history
This lets you write methods using `self: Rc<Self>`, `self: Arc<Self>`, `self: Pin<&mut Self>`, `self: Pin<Box<Self>`, and other combinations involving `Pin` and another stdlib receiver type, without needing the `arbitrary_self_types`. Other user-created receiver types can be used, but they still require the feature flag to use.

This is implemented by introducing a new trait, `Receiver`, which the method receiver's type must implement if the `arbitrary_self_types` feature is not enabled. To keep composed receiver types such as `&Arc<Self>` unstable, the receiver type is also required to implement `Deref<Target=Self>` when the feature flag is not enabled.

This lets you use `self: Rc<Self>` and `self: Arc<Self>` in stable Rust, which was not allowed previously. It was agreed that they would be stabilized in rust-lang#55786. `self: Pin<&Self>` and other pinned receiver types do not require the `arbitrary_self_types` feature, but they cannot be used on stable because `Pin` still requires the `pin` feature.
  • Loading branch information
mikeyhew committed Dec 20, 2018
1 parent daa8792 commit 153f5a7
Show file tree
Hide file tree
Showing 16 changed files with 250 additions and 91 deletions.
7 changes: 6 additions & 1 deletion src/liballoc/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ use core::iter::{Iterator, FromIterator, FusedIterator};
use core::marker::{Unpin, Unsize};
use core::mem;
use core::pin::Pin;
use core::ops::{CoerceUnsized, DispatchFromDyn, Deref, DerefMut, Generator, GeneratorState};
use core::ops::{
CoerceUnsized, DispatchFromDyn, Deref, DerefMut, Receiver, Generator, GeneratorState
};
use core::ptr::{self, NonNull, Unique};
use core::task::{LocalWaker, Poll};

Expand Down Expand Up @@ -583,6 +585,9 @@ impl<T: ?Sized> DerefMut for Box<T> {
}
}

#[unstable(feature = "receiver_trait", issue = "0")]
impl<T: ?Sized> Receiver for Box<T> {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<I: Iterator + ?Sized> Iterator for Box<I> {
type Item = I::Item;
Expand Down
1 change: 1 addition & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
#![feature(ptr_internals)]
#![feature(ptr_offset_from)]
#![feature(rustc_attrs)]
#![feature(receiver_trait)]
#![feature(specialization)]
#![feature(split_ascii_whitespace)]
#![feature(staged_api)]
Expand Down
5 changes: 4 additions & 1 deletion src/liballoc/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ use core::intrinsics::abort;
use core::marker;
use core::marker::{Unpin, Unsize, PhantomData};
use core::mem::{self, align_of_val, forget, size_of_val};
use core::ops::Deref;
use core::ops::{Deref, Receiver};
use core::ops::{CoerceUnsized, DispatchFromDyn};
use core::pin::Pin;
use core::ptr::{self, NonNull};
Expand Down Expand Up @@ -813,6 +813,9 @@ impl<T: ?Sized> Deref for Rc<T> {
}
}

#[unstable(feature = "receiver_trait", issue = "0")]
impl<T: ?Sized> Receiver for Rc<T> {}

#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc<T> {
/// Drops the `Rc`.
Expand Down
5 changes: 4 additions & 1 deletion src/liballoc/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use core::fmt;
use core::cmp::Ordering;
use core::intrinsics::abort;
use core::mem::{self, align_of_val, size_of_val};
use core::ops::Deref;
use core::ops::{Deref, Receiver};
use core::ops::{CoerceUnsized, DispatchFromDyn};
use core::pin::Pin;
use core::ptr::{self, NonNull};
Expand Down Expand Up @@ -767,6 +767,9 @@ impl<T: ?Sized> Deref for Arc<T> {
}
}

#[unstable(feature = "receiver_trait", issue = "0")]
impl<T: ?Sized> Receiver for Arc<T> {}

impl<T: Clone> Arc<T> {
/// Makes a mutable reference into the given `Arc`.
///
Expand Down
16 changes: 16 additions & 0 deletions src/libcore/ops/deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,19 @@ pub trait DerefMut: Deref {
impl<T: ?Sized> DerefMut for &mut T {
fn deref_mut(&mut self) -> &mut T { *self }
}

/// Indicates that a struct can be used as a method receiver, without the
/// `arbitrary_self_types` feature. This is implemented by stdlib pointer types like `Box<T>`,
/// `Rc<T>`, `&T`, and `Pin<P>`.
#[cfg_attr(not(stage0), lang = "receiver")]
#[unstable(feature = "receiver_trait", issue = "0")]
#[doc(hidden)]
pub trait Receiver {
// Empty.
}

#[unstable(feature = "receiver_trait", issue = "0")]
impl<T: ?Sized> Receiver for &T {}

#[unstable(feature = "receiver_trait", issue = "0")]
impl<T: ?Sized> Receiver for &mut T {}
3 changes: 3 additions & 0 deletions src/libcore/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ pub use self::bit::{BitAndAssign, BitOrAssign, BitXorAssign, ShlAssign, ShrAssig
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::deref::{Deref, DerefMut};

#[unstable(feature = "receiver_trait", issue = "0")]
pub use self::deref::Receiver;

#[stable(feature = "rust1", since = "1.0.0")]
pub use self::drop::Drop;

Expand Down
5 changes: 4 additions & 1 deletion src/libcore/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@

use fmt;
use marker::Sized;
use ops::{Deref, DerefMut, CoerceUnsized, DispatchFromDyn};
use ops::{Deref, DerefMut, Receiver, CoerceUnsized, DispatchFromDyn};

#[doc(inline)]
pub use marker::Unpin;
Expand Down Expand Up @@ -302,6 +302,9 @@ where
}
}

#[unstable(feature = "receiver_trait", issue = "0")]
impl<P: Receiver> Receiver for Pin<P> {}

#[unstable(feature = "pin", issue = "49150")]
impl<P: fmt::Debug> fmt::Debug for Pin<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ language_item_table! {

DerefTraitLangItem, "deref", deref_trait, Target::Trait;
DerefMutTraitLangItem, "deref_mut", deref_mut_trait, Target::Trait;
ReceiverTraitLangItem, "receiver", receiver_trait, Target::Trait;

FnTraitLangItem, "fn", fn_trait, Target::Trait;
FnMutTraitLangItem, "fn_mut", fn_mut_trait, Target::Trait;
Expand Down
185 changes: 138 additions & 47 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ use constrained_type_params::{identify_constrained_type_params, Parameter};

use hir::def_id::DefId;
use rustc::traits::{self, ObligationCauseCode};
use rustc::ty::{self, Lift, Ty, TyCtxt, TyKind, GenericParamDefKind, TypeFoldable};
use rustc::ty::{self, Lift, Ty, TyCtxt, TyKind, GenericParamDefKind, TypeFoldable, ToPredicate};
use rustc::ty::subst::{Subst, Substs};
use rustc::ty::util::ExplicitSelf;
use rustc::util::nodemap::{FxHashSet, FxHashMap};
use rustc::middle::lang_items;
use rustc::infer::opaque_types::may_define_existential_type;
Expand Down Expand Up @@ -749,72 +748,164 @@ fn check_method_receiver<'fcx, 'gcx, 'tcx>(fcx: &FnCtxt<'fcx, 'gcx, 'tcx>,
&ty::Binder::bind(self_ty)
);

let self_arg_ty = sig.inputs()[0];
let receiver_ty = sig.inputs()[0];

let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver);
let self_arg_ty = fcx.normalize_associated_types_in(span, &self_arg_ty);
let self_arg_ty = fcx.tcx.liberate_late_bound_regions(
let receiver_ty = fcx.normalize_associated_types_in(span, &receiver_ty);
let receiver_ty = fcx.tcx.liberate_late_bound_regions(
method.def_id,
&ty::Binder::bind(self_arg_ty)
&ty::Binder::bind(receiver_ty)
);

let mut autoderef = fcx.autoderef(span, self_arg_ty).include_raw_pointers();

loop {
if let Some((potential_self_ty, _)) = autoderef.next() {
debug!("check_method_receiver: potential self type `{:?}` to match `{:?}`",
potential_self_ty, self_ty);

if fcx.infcx.can_eq(fcx.param_env, self_ty, potential_self_ty).is_ok() {
autoderef.finalize(fcx);
if let Some(mut err) = fcx.demand_eqtype_with_origin(
&cause, self_ty, potential_self_ty) {
err.emit();
}
break
}
} else {
if fcx.tcx.features().arbitrary_self_types {
if !receiver_is_valid(fcx, span, receiver_ty, self_ty, true) {
// report error, arbitrary_self_types was enabled
fcx.tcx.sess.diagnostic().mut_span_err(
span, &format!("invalid `self` type: {:?}", self_arg_ty))
.note(&format!("type must be `{:?}` or a type that dereferences to it", self_ty))
span, &format!("invalid `self` type: {:?}", receiver_ty)
).note(&format!("type must be `{:?}` or a type that dereferences to it", self_ty))
.help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
.code(DiagnosticId::Error("E0307".into()))
.emit();
return
}
}

let is_self_ty = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok();
let self_kind = ExplicitSelf::determine(self_arg_ty, is_self_ty);

if !fcx.tcx.features().arbitrary_self_types {
match self_kind {
ExplicitSelf::ByValue |
ExplicitSelf::ByReference(_, _) |
ExplicitSelf::ByBox => (),

ExplicitSelf::ByRawPointer(_) => {
} else {
if !receiver_is_valid(fcx, span, receiver_ty, self_ty, false) {
if receiver_is_valid(fcx, span, receiver_ty, self_ty, true) {
// report error, would have worked with arbitrary_self_types
feature_gate::feature_err(
&fcx.tcx.sess.parse_sess,
"arbitrary_self_types",
span,
GateIssue::Language,
"raw pointer `self` is unstable")
&format!(
"`{}` cannot be used as the type of `self` without \
the `arbitrary_self_types` feature",
receiver_ty,
),
).help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
.emit();
} else {
// report error, would not have worked with arbitrary_self_types
fcx.tcx.sess.diagnostic().mut_span_err(
span, &format!("invalid `self` type: {:?}", receiver_ty)
).note(&format!("type must be `{:?}` or a type that dereferences to it", self_ty))
.help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
.code(DiagnosticId::Error("E0307".into()))
.emit();
}
}
}
}

ExplicitSelf::Other => {
feature_gate::feature_err(
&fcx.tcx.sess.parse_sess,
"arbitrary_self_types",
span,
GateIssue::Language,"arbitrary `self` types are unstable")
.help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
.emit();
fn receiver_is_valid<'fcx, 'tcx, 'gcx>(
fcx: &FnCtxt<'fcx, 'gcx, 'tcx>,
span: Span,
receiver_ty: Ty<'tcx>,
self_ty: Ty<'tcx>,
arbitrary_self_types_enabled: bool,
) -> bool {
let cause = fcx.cause(span, traits::ObligationCauseCode::MethodReceiver);

let can_eq_self = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok();

if can_eq_self(receiver_ty) {
if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, self_ty, receiver_ty) {
err.emit();
}
return true
}

let mut autoderef = fcx.autoderef(span, receiver_ty);

if arbitrary_self_types_enabled {
autoderef = autoderef.include_raw_pointers();
}

// skip the first type, we know its not equal to `self_ty`
autoderef.next();

let potential_self_ty = loop {
if let Some((potential_self_ty, _)) = autoderef.next() {
debug!("receiver_is_valid: potential self type `{:?}` to match `{:?}`",
potential_self_ty, self_ty);

if can_eq_self(potential_self_ty) {
break potential_self_ty
}
} else {
debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`",
receiver_ty, self_ty);
return false
}
};

if !arbitrary_self_types_enabled {
// check that receiver_ty: Receiver<Target=self_ty>

let receiver_trait_def_id = match fcx.tcx.lang_items().receiver_trait() {
Some(did) => did,
None => {
debug!("receiver_is_valid: missing Receiver trait");
return false
}
};

let receiver_trait_ref = ty::TraitRef{
def_id: receiver_trait_def_id,
substs: fcx.tcx.mk_substs_trait(receiver_ty, &[]),
};

let receiver_obligation = traits::Obligation::new(
cause.clone(),
fcx.param_env,
receiver_trait_ref.to_predicate()
);

if !fcx.predicate_must_hold(&receiver_obligation) {
debug!("receiver_is_valid: type `{:?}` does not implement `Receiver` trait",
receiver_ty);
return false
}

let deref_trait_def_id = match fcx.tcx.lang_items().deref_trait() {
Some(did) => did,
None => {
debug!("receiver_is_valid: missing Deref trait");
return false
}
};

let deref_trait_ref = ty::TraitRef {
def_id: deref_trait_def_id,
substs: fcx.tcx.mk_substs_trait(receiver_ty, &[]),
};

let projection_ty = ty::ProjectionTy::from_ref_and_name(
fcx.tcx, deref_trait_ref, ast::Ident::from_str("Target")
);

let projection_predicate = ty::Binder::dummy(ty::ProjectionPredicate {
projection_ty, ty: self_ty
}).to_predicate();

let deref_obligation = traits::Obligation::new(
cause.clone(),
fcx.param_env,
projection_predicate,
);

if !fcx.predicate_must_hold(&deref_obligation) {
debug!("receiver_is_valid: type `{:?}` does not directly deref to `{:?}`",
receiver_ty, self_ty);
return false
}
}

if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, self_ty, potential_self_ty) {
err.emit();
}

autoderef.finalize(fcx);

true
}

fn check_variances_for_type_defn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
Expand Down
9 changes: 8 additions & 1 deletion src/test/run-pass/arbitrary_self_types_stdlib_pointers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(arbitrary_self_types)]
#![feature(pin)]
#![feature(rustc_attrs)]

Expand All @@ -23,6 +22,7 @@ trait Trait {
fn by_arc(self: Arc<Self>) -> i64;
fn by_pin_mut(self: Pin<&mut Self>) -> i64;
fn by_pin_box(self: Pin<Box<Self>>) -> i64;
fn by_pin_pin_pin_ref(self: Pin<Pin<Pin<&Self>>>) -> i64;
}

impl Trait for i64 {
Expand All @@ -38,6 +38,9 @@ impl Trait for i64 {
fn by_pin_box(self: Pin<Box<Self>>) -> i64 {
*self
}
fn by_pin_pin_pin_ref(self: Pin<Pin<Pin<&Self>>>) -> i64 {
*self
}
}

fn main() {
Expand All @@ -53,4 +56,8 @@ fn main() {

let pin_box = Into::<Pin<Box<i64>>>::into(Box::new(4i64)) as Pin<Box<dyn Trait>>;
assert_eq!(4, pin_box.by_pin_box());

let value = 5i64;
let pin_pin_pin_ref = Pin::new(Pin::new(Pin::new(&value))) as Pin<Pin<Pin<&dyn Trait>>>;
assert_eq!(5, pin_pin_pin_ref.by_pin_pin_pin_ref());
}
Loading

0 comments on commit 153f5a7

Please sign in to comment.