Skip to content

Commit 2553a45

Browse files
arbitrary_self_types: split the Autoderef chain
Receiver chain has been an extension of Deref chain, but the consequence has been questioned as this would admit method signatures that are deemed dubious. It is also debatable that Receiver trait which determines applicable `Self` type should have anything to do with dereference operation in general. This patch separate the two traits and isolate their use in the language. This means that in method probing, we will chase down a Deref chain for an applicable type for the variable `self`, from which we additionally chase down a Receiver chain for an applicable `Self` type from a list of methods that accepts one of these possible types of `self`. This patch includes rewording of diagnostics.
1 parent d5419f1 commit 2553a45

File tree

66 files changed

+724
-406
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+724
-406
lines changed

compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![feature(arbitrary_self_types, unsize, coerce_unsized, dispatch_from_dyn)]
44

55
use std::marker::Unsize;
6-
use std::ops::{CoerceUnsized, Deref, DispatchFromDyn};
6+
use std::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
77

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

@@ -14,6 +14,9 @@ impl<T: ?Sized> Deref for Ptr<T> {
1414
&*self.0
1515
}
1616
}
17+
impl<T: ?Sized> Receiver for Ptr<T> {
18+
type Target = T;
19+
}
1720

1821
impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
1922
impl<T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<Ptr<U>> for Ptr<T> {}
@@ -27,6 +30,9 @@ impl<T: ?Sized> Deref for Wrapper<T> {
2730
&self.0
2831
}
2932
}
33+
impl<T: ?Sized> Receiver for Wrapper<T> {
34+
type Target = T;
35+
}
3036

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

compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
#![feature(rustc_attrs)]
55
#![allow(internal_features)]
66

7-
use std::{
8-
ops::{Deref, CoerceUnsized, DispatchFromDyn},
9-
marker::Unsize,
10-
};
7+
use std::marker::Unsize;
8+
use std::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
119

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

@@ -18,6 +16,9 @@ impl<T: ?Sized> Deref for Ptr<T> {
1816
&*self.0
1917
}
2018
}
19+
impl<T: ?Sized> Receiver for Ptr<T> {
20+
type Target = T;
21+
}
2122

2223
impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
2324
impl<T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<Ptr<U>> for Ptr<T> {}
@@ -31,11 +32,13 @@ impl<T: ?Sized> Deref for Wrapper<T> {
3132
&self.0
3233
}
3334
}
35+
impl<T: ?Sized> Receiver for Wrapper<T> {
36+
type Target = T;
37+
}
3438

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

38-
3942
trait Trait {
4043
fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32;
4144
fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32;

compiler/rustc_error_codes/src/error_codes/E0307.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,22 +67,16 @@ impl Trait for Foo {
6767
The nightly feature [Arbitrary self types][AST] extends the accepted
6868
set of receiver types to also include any type that implements the
6969
`Receiver` trait and can follow its chain of `Target` types to `Self`.
70-
There's a blanket implementation of `Receiver` for `T: Deref`, so any
71-
type which dereferences to `Self` can be used.
7270

7371
```
7472
#![feature(arbitrary_self_types)]
7573
7674
struct Foo;
7775
struct Bar;
7876
79-
// Because you can dereference `Bar` into `Foo`...
80-
impl std::ops::Deref for Bar {
77+
// Because you can set `Bar` as method receiver for `Foo`...
78+
impl std::ops::Receiver for Bar {
8179
type Target = Foo;
82-
83-
fn deref(&self) -> &Foo {
84-
&Foo
85-
}
8680
}
8781
8882
impl Foo {

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ hir_analysis_invalid_receiver_ty_help =
263263
consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>`
264264
265265
hir_analysis_invalid_receiver_ty_help_no_arbitrary_self_types =
266-
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`)
266+
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`);
267+
alternatively, consider implement `Receiver` trait on the type of `self`, where applicable
267268
268269
hir_analysis_invalid_receiver_ty_help_nonnull_note =
269270
`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`

compiler/rustc_hir_analysis/src/autoderef.rs

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use std::cell::RefCell;
2+
3+
use rustc_data_structures::unord::UnordMap;
14
use rustc_hir::limit::Limit;
25
use rustc_infer::infer::InferCtxt;
36
use rustc_infer::traits::PredicateObligations;
@@ -26,6 +29,39 @@ struct AutoderefSnapshot<'tcx> {
2629
obligations: PredicateObligations<'tcx>,
2730
}
2831

32+
#[derive(Debug, Default)]
33+
pub struct AutoderefCache<'tcx> {
34+
next_deref: RefCell<UnordMap<Ty<'tcx>, (Ty<'tcx>, PredicateObligations<'tcx>)>>,
35+
next_receiver: RefCell<UnordMap<Ty<'tcx>, (Ty<'tcx>, PredicateObligations<'tcx>)>>,
36+
}
37+
38+
impl<'tcx> AutoderefCache<'tcx> {
39+
pub fn get(
40+
&self,
41+
use_receiver: bool,
42+
ty: Ty<'tcx>,
43+
) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> {
44+
if use_receiver {
45+
self.next_receiver.borrow().get(&ty).cloned()
46+
} else {
47+
self.next_deref.borrow().get(&ty).cloned()
48+
}
49+
}
50+
pub fn cache(
51+
&self,
52+
use_receiver: bool,
53+
ty: Ty<'tcx>,
54+
next: Ty<'tcx>,
55+
predicates: PredicateObligations<'tcx>,
56+
) {
57+
if use_receiver {
58+
self.next_receiver.borrow_mut().insert(ty, (next, predicates));
59+
} else {
60+
self.next_deref.borrow_mut().insert(ty, (next, predicates));
61+
}
62+
}
63+
}
64+
2965
/// Recursively dereference a type, considering both built-in
3066
/// dereferences (`*`) and the `Deref` trait.
3167
/// Although called `Autoderef` it can be configured to use the
@@ -36,6 +72,7 @@ pub struct Autoderef<'a, 'tcx> {
3672
span: Span,
3773
body_id: LocalDefId,
3874
param_env: ty::ParamEnv<'tcx>,
75+
cache: Option<&'a AutoderefCache<'tcx>>,
3976

4077
// Current state:
4178
state: AutoderefSnapshot<'tcx>,
@@ -80,16 +117,19 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
80117
}
81118

82119
// Otherwise, deref if type is derefable:
83-
// NOTE: in the case of self.use_receiver_trait = true, you might think it would
84-
// be better to skip this clause and use the Overloaded case only, since &T
85-
// and &mut T implement Receiver. But built-in derefs apply equally to Receiver
86-
// and Deref, and this has benefits for const and the emitted MIR.
120+
// NOTE: in the case of self.use_receiver_trait = true,
121+
// Autoderef works only with Receiver trait.
122+
// Caller is expecting us to expand the Receiver chain only.
87123
let (kind, new_ty) =
88124
if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
89125
debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty));
90126
// NOTE: we may still need to normalize the built-in deref in case
91127
// we have some type like `&<Ty as Trait>::Assoc`, since users of
92128
// autoderef expect this type to have been structurally normalized.
129+
// NOTE: even when we follow Receiver chain we still unwrap
130+
// references and pointers here, but this is only symbolic and
131+
// we are not going to really dereferences any references or pointers.
132+
// That happens when autoderef is chasing the Deref chain.
93133
if self.infcx.next_trait_solver()
94134
&& let ty::Alias(..) = ty.kind()
95135
{
@@ -122,13 +162,15 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
122162
impl<'a, 'tcx> Autoderef<'a, 'tcx> {
123163
pub fn new(
124164
infcx: &'a InferCtxt<'tcx>,
165+
cache: Option<&'a AutoderefCache<'tcx>>,
125166
param_env: ty::ParamEnv<'tcx>,
126167
body_def_id: LocalDefId,
127168
span: Span,
128169
base_ty: Ty<'tcx>,
129170
) -> Self {
130171
Autoderef {
131172
infcx,
173+
cache,
132174
span,
133175
body_id: body_def_id,
134176
param_env,
@@ -145,13 +187,19 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
145187
}
146188
}
147189

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

152194
if ty.references_error() {
153195
return None;
154196
}
197+
if let Some(cache) = &self.cache
198+
&& let Some((ty, obligations)) = cache.get(self.use_receiver_trait, self.state.cur_ty)
199+
{
200+
self.state.obligations.extend(obligations);
201+
return Some(ty);
202+
}
155203

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

179227
let (normalized_ty, obligations) =
180228
self.structurally_normalize_ty(Ty::new_projection(tcx, trait_target_def_id, [ty]))?;
181-
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
229+
debug!(?ty, ?normalized_ty, ?obligations);
230+
if matches!(ty.kind(), ty::Adt(..))
231+
&& let Some(cache) = &self.cache
232+
{
233+
cache.cache(self.use_receiver_trait, ty, normalized_ty, obligations.clone());
234+
}
182235
self.state.obligations.extend(obligations);
183236

184237
Some(self.infcx.resolve_vars_if_possible(normalized_ty))
@@ -255,11 +308,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
255308
/// Use `core::ops::Receiver` and `core::ops::Receiver::Target` as
256309
/// the trait and associated type to iterate, instead of
257310
/// `core::ops::Deref` and `core::ops::Deref::Target`
258-
pub fn use_receiver_trait(mut self) -> Self {
311+
pub fn follow_receiver_chain(mut self) -> Self {
259312
self.use_receiver_trait = true;
260313
self
261314
}
262-
263315
pub fn silence_errors(mut self) -> Self {
264316
self.silence_errors = true;
265317
self

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,7 +1720,9 @@ fn check_method_receiver<'tcx>(
17201720
// Report error; would not have worked with `arbitrary_self_types[_pointers]`.
17211721
{
17221722
match receiver_validity_err {
1723-
ReceiverValidityError::DoesNotDeref if arbitrary_self_types_level.is_some() => {
1723+
ReceiverValidityError::DoesNotReceive
1724+
if arbitrary_self_types_level.is_some() =>
1725+
{
17241726
let hint = match receiver_ty
17251727
.builtin_deref(false)
17261728
.unwrap_or(receiver_ty)
@@ -1734,7 +1736,7 @@ fn check_method_receiver<'tcx>(
17341736

17351737
tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty, hint })
17361738
}
1737-
ReceiverValidityError::DoesNotDeref => {
1739+
ReceiverValidityError::DoesNotReceive => {
17381740
tcx.dcx().emit_err(errors::InvalidReceiverTyNoArbitrarySelfTypes {
17391741
span,
17401742
receiver_ty,
@@ -1756,7 +1758,7 @@ fn check_method_receiver<'tcx>(
17561758
enum ReceiverValidityError {
17571759
/// The self type does not get to the receiver type by following the
17581760
/// autoderef chain.
1759-
DoesNotDeref,
1761+
DoesNotReceive,
17601762
/// A type was found which is a method type parameter, and that's not allowed.
17611763
MethodGenericParamUsed,
17621764
}
@@ -1812,17 +1814,21 @@ fn receiver_is_valid<'tcx>(
18121814

18131815
confirm_type_is_not_a_method_generic_param(receiver_ty, method_generics)?;
18141816

1815-
let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
1817+
let cache = Default::default();
1818+
let mut autoderef =
1819+
Autoderef::new(infcx, Some(&cache), wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
18161820

18171821
// The `arbitrary_self_types` feature allows custom smart pointer
1818-
// types to be method receivers, as identified by following the Receiver<Target=T>
1822+
// types to be method receivers, as identified by following the Receiver<Target = T>
18191823
// chain.
18201824
if arbitrary_self_types_enabled.is_some() {
1821-
autoderef = autoderef.use_receiver_trait();
1825+
// We are in the wf check, so we would like to deref the references in the type head.
1826+
// However, we do not want to walk `Deref` chain.
1827+
autoderef = autoderef.follow_receiver_chain();
18221828
}
18231829

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

@@ -1876,7 +1882,7 @@ fn receiver_is_valid<'tcx>(
18761882
}
18771883

18781884
debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
1879-
Err(ReceiverValidityError::DoesNotDeref)
1885+
Err(ReceiverValidityError::DoesNotReceive)
18801886
}
18811887

18821888
fn legacy_receiver_is_implemented<'tcx>(

compiler/rustc_hir_typeck/src/autoderef.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use super::{FnCtxt, PlaceOp};
1515

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

2121
pub(crate) fn try_overloaded_deref(

compiler/rustc_hir_typeck/src/method/confirm.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::marker::PhantomData;
12
use std::ops::Deref;
23

34
use rustc_hir as hir;
@@ -350,18 +351,37 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
350351
// yield an object-type (e.g., `&Object` or `Box<Object>`
351352
// etc).
352353

353-
let mut autoderef = self.fcx.autoderef(self.span, self_ty);
354+
let fcx = self.fcx;
355+
let span = self.span;
356+
let autoderef = fcx.autoderef(span, self_ty);
354357

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

363363
autoderef
364364
.include_raw_pointers()
365+
.flat_map(|(ty, derefs)| {
366+
enum EitherIter<A, B, C> {
367+
A(A, PhantomData<fn() -> C>),
368+
B(B),
369+
}
370+
impl<A: Iterator<Item = C>, B: Iterator<Item = C>, C> Iterator for EitherIter<A, B, C> {
371+
type Item = C;
372+
fn next(&mut self) -> Option<Self::Item> {
373+
match self {
374+
EitherIter::A(a, _) => a.next(),
375+
EitherIter::B(b) => b.next(),
376+
}
377+
}
378+
}
379+
if follow_receiver_chain {
380+
EitherIter::A(fcx.autoderef(span, ty).follow_receiver_chain(), PhantomData)
381+
} else {
382+
EitherIter::B([(ty, derefs)].into_iter())
383+
}
384+
})
365385
.find_map(|(ty, _)| match ty.kind() {
366386
ty::Dynamic(data, ..) => Some(closure(
367387
self,

0 commit comments

Comments
 (0)