Skip to content

Commit e75660d

Browse files
committed
Arbitrary self types v2: use Receiver trait
In this new version of Arbitrary Self Types, we no longer use the Deref trait exclusively when working out which self types are valid. Instead, we follow a chain of Receiver traits. This enables methods to be called on smart pointer types which fundamentally cannot support Deref (for instance because they are wrappers for pointers that don't follow Rust's aliasing rules). This includes: * Changes to tests appropriately * New tests for: * The basics of the feature * Ensuring lifetime elision works properly * Generic Receivers * A copy of the method subst test enhanced with Receiver This is really the heart of the 'arbitrary self types v2' feature, and is the most critical commit in the current PR. Subsequent commits are focused on: * Detecting "shadowing" problems, where a smart pointer type can hide methods in the pointee. * Diagnostics and cleanup. Naming: in this commit, the "Autoderef" type is modified so that it no longer solely focuses on the "Deref" trait, but can now consider the "Receiver" trait instead. Should it be renamed, to something like "TraitFollower"? This was considered, but rejected, because * even in the Receiver case, it still considers built-in derefs * the name Autoderef is short and snappy.
1 parent 5a6036a commit e75660d

File tree

39 files changed

+737
-97
lines changed

39 files changed

+737
-97
lines changed

Diff for: compiler/rustc_error_codes/src/error_codes/E0307.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ impl Trait for Foo {
6565
```
6666

6767
The nightly feature [Arbitrary self types][AST] extends the accepted
68-
set of receiver types to also include any type that can dereference to
69-
`Self`:
68+
set of receiver types to also include any type that implements the
69+
`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.
7072

7173
```
7274
#![feature(arbitrary_self_types)]

Diff for: compiler/rustc_hir_analysis/messages.ftl

+2-2
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,10 @@ hir_analysis_invalid_generic_receiver_ty_help =
241241
use a concrete type such as `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`)
242242
243243
hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty}`
244-
.note = type of `self` must be `Self` or a type that dereferences to it
244+
.note = type of `self` must be `Self` or some type implementing `Receiver`
245245
246246
hir_analysis_invalid_receiver_ty_help =
247-
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`)
247+
consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>`
248248
249249
hir_analysis_invalid_union_field =
250250
field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union

Diff for: compiler/rustc_hir_analysis/src/autoderef.rs

+28-9
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ pub enum AutoderefKind {
1818
/// A type which must dispatch to a `Deref` implementation.
1919
Overloaded,
2020
}
21-
2221
struct AutoderefSnapshot<'tcx> {
2322
at_start: bool,
2423
reached_recursion_limit: bool,
@@ -27,6 +26,10 @@ struct AutoderefSnapshot<'tcx> {
2726
obligations: PredicateObligations<'tcx>,
2827
}
2928

29+
/// Recursively dereference a type, considering both built-in
30+
/// dereferences (`*`) and the `Deref` trait.
31+
/// Although called `Autoderef` it can be configured to use the
32+
/// `Receiver` trait instead of the `Deref` trait.
3033
pub struct Autoderef<'a, 'tcx> {
3134
// Meta infos:
3235
infcx: &'a InferCtxt<'tcx>,
@@ -39,6 +42,7 @@ pub struct Autoderef<'a, 'tcx> {
3942

4043
// Configurations:
4144
include_raw_pointers: bool,
45+
use_receiver_trait: bool,
4246
silence_errors: bool,
4347
}
4448

@@ -69,6 +73,10 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
6973
}
7074

7175
// Otherwise, deref if type is derefable:
76+
// NOTE: in the case of self.use_receiver_trait = true, you might think it would
77+
// be better to skip this clause and use the Overloaded case only, since &T
78+
// and &mut T implement Receiver. But built-in derefs apply equally to Receiver
79+
// and Deref, and this has benefits for const and the emitted MIR.
7280
let (kind, new_ty) =
7381
if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
7482
debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty));
@@ -111,7 +119,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
111119
body_def_id: LocalDefId,
112120
span: Span,
113121
base_ty: Ty<'tcx>,
114-
) -> Autoderef<'a, 'tcx> {
122+
) -> Self {
115123
Autoderef {
116124
infcx,
117125
span,
@@ -125,6 +133,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
125133
reached_recursion_limit: false,
126134
},
127135
include_raw_pointers: false,
136+
use_receiver_trait: false,
128137
silence_errors: false,
129138
}
130139
}
@@ -137,8 +146,13 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
137146
return None;
138147
}
139148

140-
// <ty as Deref>
141-
let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]);
149+
// <ty as Deref>, or whatever the equivalent trait is that we've been asked to walk.
150+
let (trait_def_id, trait_target_def_id) = if self.use_receiver_trait {
151+
(tcx.lang_items().receiver_trait()?, tcx.lang_items().receiver_target()?)
152+
} else {
153+
(tcx.lang_items().deref_trait()?, tcx.lang_items().deref_target()?)
154+
};
155+
let trait_ref = ty::TraitRef::new(tcx, trait_def_id, [ty]);
142156
let cause = traits::ObligationCause::misc(self.span, self.body_id);
143157
let obligation = traits::Obligation::new(
144158
tcx,
@@ -151,11 +165,8 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
151165
return None;
152166
}
153167

154-
let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection(
155-
tcx,
156-
tcx.lang_items().deref_target()?,
157-
[ty],
158-
))?;
168+
let (normalized_ty, obligations) =
169+
self.structurally_normalize(Ty::new_projection(tcx, trait_target_def_id, [ty]))?;
159170
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
160171
self.state.obligations.extend(obligations);
161172

@@ -234,6 +245,14 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
234245
self
235246
}
236247

248+
/// Use `core::ops::Receiver` and `core::ops::Receiver::Target` as
249+
/// the trait and associated type to iterate, instead of
250+
/// `core::ops::Deref` and `core::ops::Deref::Target`
251+
pub fn use_receiver_trait(mut self) -> Self {
252+
self.use_receiver_trait = true;
253+
self
254+
}
255+
237256
pub fn silence_errors(mut self) -> Self {
238257
self.silence_errors = true;
239258
self

Diff for: compiler/rustc_hir_analysis/src/check/wfcheck.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -1821,13 +1821,18 @@ fn receiver_is_valid<'tcx>(
18211821

18221822
let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
18231823

1824+
// The `arbitrary_self_types` feature allows custom smart pointer
1825+
// types to be method receivers, as identified by following the Receiver<Target=T>
1826+
// chain.
1827+
if arbitrary_self_types_enabled.is_some() {
1828+
autoderef = autoderef.use_receiver_trait();
1829+
}
1830+
18241831
// The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`.
18251832
if arbitrary_self_types_enabled == Some(ArbitrarySelfTypesLevel::WithPointers) {
18261833
autoderef = autoderef.include_raw_pointers();
18271834
}
18281835

1829-
let receiver_trait_def_id = tcx.require_lang_item(LangItem::LegacyReceiver, Some(span));
1830-
18311836
// Keep dereferencing `receiver_ty` until we get to `self_ty`.
18321837
while let Some((potential_self_ty, _)) = autoderef.next() {
18331838
debug!(
@@ -1849,11 +1854,13 @@ fn receiver_is_valid<'tcx>(
18491854
}
18501855

18511856
// Without `feature(arbitrary_self_types)`, we require that each step in the
1852-
// deref chain implement `receiver`.
1857+
// deref chain implement `LegacyReceiver`.
18531858
if arbitrary_self_types_enabled.is_none() {
1854-
if !receiver_is_implemented(
1859+
let legacy_receiver_trait_def_id =
1860+
tcx.require_lang_item(LangItem::LegacyReceiver, Some(span));
1861+
if !legacy_receiver_is_implemented(
18551862
wfcx,
1856-
receiver_trait_def_id,
1863+
legacy_receiver_trait_def_id,
18571864
cause.clone(),
18581865
potential_self_ty,
18591866
) {
@@ -1866,7 +1873,7 @@ fn receiver_is_valid<'tcx>(
18661873
cause.clone(),
18671874
wfcx.param_env,
18681875
potential_self_ty,
1869-
receiver_trait_def_id,
1876+
legacy_receiver_trait_def_id,
18701877
);
18711878
}
18721879
}
@@ -1875,22 +1882,22 @@ fn receiver_is_valid<'tcx>(
18751882
Err(ReceiverValidityError::DoesNotDeref)
18761883
}
18771884

1878-
fn receiver_is_implemented<'tcx>(
1885+
fn legacy_receiver_is_implemented<'tcx>(
18791886
wfcx: &WfCheckingCtxt<'_, 'tcx>,
1880-
receiver_trait_def_id: DefId,
1887+
legacy_receiver_trait_def_id: DefId,
18811888
cause: ObligationCause<'tcx>,
18821889
receiver_ty: Ty<'tcx>,
18831890
) -> bool {
18841891
let tcx = wfcx.tcx();
1885-
let trait_ref = ty::TraitRef::new(tcx, receiver_trait_def_id, [receiver_ty]);
1892+
let trait_ref = ty::TraitRef::new(tcx, legacy_receiver_trait_def_id, [receiver_ty]);
18861893

18871894
let obligation = Obligation::new(tcx, cause, wfcx.param_env, trait_ref);
18881895

18891896
if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) {
18901897
true
18911898
} else {
18921899
debug!(
1893-
"receiver_is_implemented: type `{:?}` does not implement `Receiver` trait",
1900+
"receiver_is_implemented: type `{:?}` does not implement `LegacyReceiver` trait",
18941901
receiver_ty
18951902
);
18961903
false

Diff for: compiler/rustc_hir_typeck/src/method/probe.rs

+74-23
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
366366
autoderefs: 0,
367367
from_unsafe_deref: false,
368368
unsize: false,
369+
reachable_via_deref: true,
369370
}]),
370371
opt_bad_ty: None,
371372
reached_recursion_limit: false,
@@ -516,47 +517,93 @@ fn method_autoderef_steps<'tcx>(
516517
let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
517518
let ParamEnvAnd { param_env, value: self_ty } = goal;
518519

519-
let mut autoderef =
520+
// If arbitrary self types is not enabled, we follow the chain of
521+
// `Deref<Target=T>`. If arbitrary self types is enabled, we instead
522+
// follow the chain of `Receiver<Target=T>`, but we also record whether
523+
// such types are reachable by following the (potentially shorter)
524+
// chain of `Deref<Target=T>`. We will use the first list when finding
525+
// potentially relevant function implementations (e.g. relevant impl blocks)
526+
// but the second list when determining types that the receiver may be
527+
// converted to, in order to find out which of those methods might actually
528+
// be callable.
529+
let mut autoderef_via_deref =
520530
Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty)
521531
.include_raw_pointers()
522532
.silence_errors();
523-
let mut reached_raw_pointer = false;
524-
let mut steps: Vec<_> = autoderef
525-
.by_ref()
526-
.map(|(ty, d)| {
527-
let step = CandidateStep {
528-
self_ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, ty),
529-
autoderefs: d,
530-
from_unsafe_deref: reached_raw_pointer,
531-
unsize: false,
532-
};
533-
if let ty::RawPtr(_, _) = ty.kind() {
534-
// all the subsequent steps will be from_unsafe_deref
535-
reached_raw_pointer = true;
536-
}
537-
step
538-
})
539-
.collect();
540533

541-
let final_ty = autoderef.final_ty(true);
534+
let mut reached_raw_pointer = false;
535+
let arbitrary_self_types_enabled =
536+
tcx.features().arbitrary_self_types() || tcx.features().arbitrary_self_types_pointers();
537+
let (mut steps, reached_recursion_limit): (Vec<_>, bool) = if arbitrary_self_types_enabled {
538+
let reachable_via_deref =
539+
autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false));
540+
541+
let mut autoderef_via_receiver =
542+
Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty)
543+
.include_raw_pointers()
544+
.use_receiver_trait()
545+
.silence_errors();
546+
let steps = autoderef_via_receiver
547+
.by_ref()
548+
.zip(reachable_via_deref)
549+
.map(|((ty, d), reachable_via_deref)| {
550+
let step = CandidateStep {
551+
self_ty: infcx
552+
.make_query_response_ignoring_pending_obligations(inference_vars, ty),
553+
autoderefs: d,
554+
from_unsafe_deref: reached_raw_pointer,
555+
unsize: false,
556+
reachable_via_deref,
557+
};
558+
if ty.is_unsafe_ptr() {
559+
// all the subsequent steps will be from_unsafe_deref
560+
reached_raw_pointer = true;
561+
}
562+
step
563+
})
564+
.collect();
565+
(steps, autoderef_via_receiver.reached_recursion_limit())
566+
} else {
567+
let steps = autoderef_via_deref
568+
.by_ref()
569+
.map(|(ty, d)| {
570+
let step = CandidateStep {
571+
self_ty: infcx
572+
.make_query_response_ignoring_pending_obligations(inference_vars, ty),
573+
autoderefs: d,
574+
from_unsafe_deref: reached_raw_pointer,
575+
unsize: false,
576+
reachable_via_deref: true,
577+
};
578+
if ty.is_unsafe_ptr() {
579+
// all the subsequent steps will be from_unsafe_deref
580+
reached_raw_pointer = true;
581+
}
582+
step
583+
})
584+
.collect();
585+
(steps, autoderef_via_deref.reached_recursion_limit())
586+
};
587+
let final_ty = autoderef_via_deref.final_ty(true);
542588
let opt_bad_ty = match final_ty.kind() {
543589
ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
544590
reached_raw_pointer,
545591
ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
546592
}),
547593
ty::Array(elem_ty, _) => {
548-
let dereferences = steps.len() - 1;
549-
594+
let autoderefs = steps.iter().filter(|s| s.reachable_via_deref).count() - 1;
550595
steps.push(CandidateStep {
551596
self_ty: infcx.make_query_response_ignoring_pending_obligations(
552597
inference_vars,
553598
Ty::new_slice(infcx.tcx, *elem_ty),
554599
),
555-
autoderefs: dereferences,
600+
autoderefs,
556601
// this could be from an unsafe deref if we had
557602
// a *mut/const [T; N]
558603
from_unsafe_deref: reached_raw_pointer,
559604
unsize: true,
605+
reachable_via_deref: true, // this is always the final type from
606+
// autoderef_via_deref
560607
});
561608

562609
None
@@ -569,7 +616,7 @@ fn method_autoderef_steps<'tcx>(
569616
MethodAutoderefStepsResult {
570617
steps: tcx.arena.alloc_from_iter(steps),
571618
opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)),
572-
reached_recursion_limit: autoderef.reached_recursion_limit(),
619+
reached_recursion_limit,
573620
}
574621
}
575622

@@ -1065,6 +1112,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
10651112
) -> Option<PickResult<'tcx>> {
10661113
self.steps
10671114
.iter()
1115+
// At this point we're considering the types to which the receiver can be converted,
1116+
// so we want to follow the `Deref` chain not the `Receiver` chain. Filter out
1117+
// steps which can only be reached by following the (longer) `Receiver` chain.
1118+
.filter(|step| step.reachable_via_deref)
10681119
.filter(|step| {
10691120
debug!("pick_all_method: step={:?}", step);
10701121
// skip types that are from a type error or that would require dereferencing

Diff for: compiler/rustc_middle/src/traits/query.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,21 @@ pub struct CandidateStep<'tcx> {
149149
/// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't.
150150
pub from_unsafe_deref: bool,
151151
pub unsize: bool,
152+
/// We will generate CandidateSteps which are reachable via a chain
153+
/// of following `Receiver`. The first 'n' of those will be reachable
154+
/// by following a chain of 'Deref' instead (since there's a blanket
155+
/// implementation of Receiver for Deref).
156+
/// We use the entire set of steps when identifying method candidates
157+
/// (e.g. identifying relevant `impl` blocks) but only those that are
158+
/// reachable via Deref when examining what the receiver type can
159+
/// be converted into by autodereffing.
160+
pub reachable_via_deref: bool,
152161
}
153162

154163
#[derive(Copy, Clone, Debug, HashStable)]
155164
pub struct MethodAutoderefStepsResult<'tcx> {
156-
/// The valid autoderef steps that could be found.
165+
/// The valid autoderef steps that could be found by following a chain
166+
/// of `Receiver<Target=T>` or `Deref<Target=T>` trait implementations.
157167
pub steps: &'tcx [CandidateStep<'tcx>],
158168
/// If Some(T), a type autoderef reported an error on.
159169
pub opt_bad_ty: Option<&'tcx MethodAutoderefBadTy<'tcx>>,

Diff for: tests/ui/async-await/inference_var_self_argument.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ error[E0307]: invalid `self` parameter type: `&dyn Foo`
44
LL | async fn foo(self: &dyn Foo) {
55
| ^^^^^^^^
66
|
7-
= note: type of `self` must be `Self` or a type that dereferences to it
8-
= 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`)
7+
= note: type of `self` must be `Self` or some type implementing `Receiver`
8+
= 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>`
99

1010
error[E0038]: the trait `Foo` cannot be made into an object
1111
--> $DIR/inference_var_self_argument.rs:5:5

Diff for: tests/ui/async-await/issue-66312.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ error[E0307]: invalid `self` parameter type: `T`
44
LL | fn is_some(self: T);
55
| ^
66
|
7-
= note: type of `self` must be `Self` or a type that dereferences to it
8-
= 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`)
7+
= note: type of `self` must be `Self` or some type implementing `Receiver`
8+
= 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>`
99

1010
error[E0308]: mismatched types
1111
--> $DIR/issue-66312.rs:9:8

0 commit comments

Comments
 (0)