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

stabilize Rc, Arc and Pin as method receivers #55880

Closed
Closed
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
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::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 @@ -582,6 +584,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 @@ -254,7 +254,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 @@ -810,6 +810,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 @@ -764,6 +764,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
15 changes: 15 additions & 0 deletions src/libcore/ops/deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,18 @@ pub trait DerefMut: Deref {
impl<T: ?Sized> DerefMut for &mut T {
fn deref_mut(&mut self) -> &mut T { *self }
}

/// A subtrait of `Deref` that 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")]
pub trait Receiver: Deref {
// 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 @@ -91,7 +91,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 @@ -292,6 +292,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 @@ -302,6 +302,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
31 changes: 30 additions & 1 deletion src/librustc_typeck/check/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
obligations: Vec<traits::PredicateObligation<'tcx>>,
at_start: bool,
include_raw_pointers: bool,
require_receiver_trait: bool,
span: Span,
}

Expand Down Expand Up @@ -136,12 +137,31 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
trait_ref,
Ident::from_str("Target"),
),
cause,
cause.clone(),
0,
&mut self.obligations);

debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty);

if self.require_receiver_trait {
// cur_ty: Receiver
let trait_ref = TraitRef {
def_id: tcx.lang_items().receiver_trait()?,
substs: tcx.mk_substs_trait(self.cur_ty, &[]),
};

let obligation = traits::Obligation::new(
cause.clone(),
self.fcx.param_env,
trait_ref.to_predicate()
);

if !self.fcx.predicate_may_hold(&obligation) {
debug!("overloaded_deref_ty: `Receiver` trait not implemented");
return None;
}
}

Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty))
}

Expand Down Expand Up @@ -211,6 +231,14 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
self
}

/// require the `Receiver` trait, a subtrait of `Deref`
/// this is used to make sure only stdlib receiver types like `&T` and `Rc<T>`
/// are allowed without #![feature(arbitrary_self_types)]
pub fn require_receiver_trait(mut self) -> Self {
self.require_receiver_trait = true;
self
}

pub fn finalize(self) {
let fcx = self.fcx;
fcx.register_predicates(self.into_obligations());
Expand All @@ -230,6 +258,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
obligations: vec![],
at_start: true,
include_raw_pointers: false,
require_receiver_trait: false,
span,
}
}
Expand Down
100 changes: 65 additions & 35 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
// except according to those terms.

use check::{Inherited, FnCtxt};
use check::autoderef::Autoderef;
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::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 @@ -751,61 +751,91 @@ fn check_method_receiver<'fcx, 'gcx, 'tcx>(fcx: &FnCtxt<'fcx, 'gcx, 'tcx>,
&ty::Binder::bind(self_arg_ty)
);

let mut autoderef = fcx.autoderef(span, self_arg_ty).include_raw_pointers();
if fcx.tcx.features().arbitrary_self_types {
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 let Some(potential_self_ty) = receiver_derefs_to_self(fcx, &mut autoderef, self_ty) {
autoderef.finalize();

if fcx.infcx.can_eq(fcx.param_env, self_ty, potential_self_ty).is_ok() {
autoderef.finalize();
if let Some(mut err) = fcx.demand_eqtype_with_origin(
&cause, self_ty, potential_self_ty) {
err.emit();
}
break
if let Some(mut err) = fcx.demand_eqtype_with_origin(
&cause, self_ty, potential_self_ty) {
err.emit();
}
} else {
// 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: {:?}", self_arg_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);
} else {
let mut autoderef = fcx.autoderef(span, self_arg_ty)
.require_receiver_trait();

if let Some(potential_self_ty) = receiver_derefs_to_self(fcx, &mut autoderef, self_ty) {
// receiver works fine
autoderef.finalize();
if let Some(mut err) = fcx.demand_eqtype_with_origin(
&cause, self_ty, potential_self_ty) {
err.emit();
}
} else {
// try again using `arbitrary_self_types` rules, to get a better error message
// don't require receiver trait, and allow raw pointer receivers

if !fcx.tcx.features().arbitrary_self_types {
match self_kind {
ExplicitSelf::ByValue |
ExplicitSelf::ByReference(_, _) |
ExplicitSelf::ByBox => (),
let mut autoderef = fcx.autoderef(span, self_arg_ty)
.include_raw_pointers();

ExplicitSelf::ByRawPointer(_) => {
if let Some(_) = receiver_derefs_to_self(fcx, &mut autoderef, self_ty) {
// 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",
self_arg_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: {:?}", self_arg_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();
/// Returns true if the Autoderef's final type can equal self_ty
fn receiver_derefs_to_self<'fcx, 'tcx, 'gcx>(
fcx: &FnCtxt<'fcx, 'gcx, 'tcx>,
autoderef: &mut Autoderef<'fcx, 'gcx, 'tcx>,
self_ty: Ty<'tcx>
) -> Option<Ty<'tcx>> {
loop {
if let Some((potential_self_ty, _)) = autoderef.next() {
debug!("receiver_derefs_to_self: potential self type `{:?}` to match `{:?}`",
potential_self_ty, self_ty);

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

if can_eq_self {
return Some(potential_self_ty)
}
} else {
return None
}
}
}
Expand Down
19 changes: 18 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,17 @@ 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
}
}

struct Foo;

impl Foo {
fn by_box_rc(self: Box<Rc<Self>>) -> i32 {
11
}
}

fn main() {
Expand All @@ -53,4 +64,10 @@ 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());

assert_eq!(11, Box::new(Rc::new(Foo)).by_box_rc());
}
Loading