Skip to content

Commit a8cd964

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 9a9dadd commit a8cd964

36 files changed

+645
-89
lines changed

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 can 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)]

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

compiler/rustc_hir_analysis/src/autoderef.rs

+32-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,14 @@ 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. (They don't implement Deref). We can't do that
79+
// for a couple of reasons - first, there is magic in the builtin * to allow
80+
// calls to fn a(&mut self) if there's a: &mut T even if 'a' itself is not mutable.
81+
// Secondly the builtin deref can work in constant contexts.
82+
// So even for Receiver-based autodereferencing, we need to retain
83+
// use of the built-in dereference *.
7284
let (kind, new_ty) =
7385
if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
7486
debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty));
@@ -111,7 +123,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
111123
body_def_id: LocalDefId,
112124
span: Span,
113125
base_ty: Ty<'tcx>,
114-
) -> Autoderef<'a, 'tcx> {
126+
) -> Self {
115127
Autoderef {
116128
infcx,
117129
span,
@@ -125,6 +137,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
125137
reached_recursion_limit: false,
126138
},
127139
include_raw_pointers: false,
140+
use_receiver_trait: false,
128141
silence_errors: false,
129142
}
130143
}
@@ -137,8 +150,13 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
137150
return None;
138151
}
139152

140-
// <ty as Deref>
141-
let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]);
153+
// <ty as Deref>, or whatever the equivalent trait is that we've been asked to walk.
154+
let (trait_def_id, trait_target_def_id) = if self.use_receiver_trait {
155+
(tcx.lang_items().receiver_trait()?, tcx.lang_items().receiver_target()?)
156+
} else {
157+
(tcx.lang_items().deref_trait()?, tcx.lang_items().deref_target()?)
158+
};
159+
let trait_ref = ty::TraitRef::new(tcx, trait_def_id, [ty]);
142160
let cause = traits::ObligationCause::misc(self.span, self.body_id);
143161
let obligation = traits::Obligation::new(
144162
tcx,
@@ -151,11 +169,8 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
151169
return None;
152170
}
153171

154-
let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection(
155-
tcx,
156-
tcx.lang_items().deref_target()?,
157-
[ty],
158-
))?;
172+
let (normalized_ty, obligations) =
173+
self.structurally_normalize(Ty::new_projection(tcx, trait_target_def_id, [ty]))?;
159174
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
160175
self.state.obligations.extend(obligations);
161176

@@ -234,6 +249,14 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
234249
self
235250
}
236251

252+
/// Use `core::ops::Receiver` and `core::ops::Receiver::Target` as
253+
/// the trait and associated type to iterate, instead of
254+
/// `core::ops::Deref` and `core::ops::Deref::Target`
255+
pub fn use_receiver_trait(mut self) -> Self {
256+
self.use_receiver_trait = true;
257+
self
258+
}
259+
237260
pub fn silence_errors(mut self) -> Self {
238261
self.silence_errors = true;
239262
self

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -1805,15 +1805,17 @@ fn receiver_is_valid<'tcx>(
18051805

18061806
confirm_type_is_not_a_method_generic_param(receiver_ty, method_generics)?;
18071807

1808-
let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
1808+
let hard_coded_receiver_trait_def_id =
1809+
tcx.require_lang_item(LangItem::LegacyReceiver, Some(span));
1810+
1811+
let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty)
1812+
.use_receiver_trait();
18091813

18101814
// The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`.
18111815
if arbitrary_self_types_enabled == Some(ArbitrarySelfTypesLevel::WithPointers) {
18121816
autoderef = autoderef.include_raw_pointers();
18131817
}
18141818

1815-
let receiver_trait_def_id = tcx.require_lang_item(LangItem::LegacyReceiver, Some(span));
1816-
18171819
// Keep dereferencing `receiver_ty` until we get to `self_ty`.
18181820
while let Some((potential_self_ty, _)) = autoderef.next() {
18191821
debug!(
@@ -1839,7 +1841,7 @@ fn receiver_is_valid<'tcx>(
18391841
if arbitrary_self_types_enabled.is_none() {
18401842
if !receiver_is_implemented(
18411843
wfcx,
1842-
receiver_trait_def_id,
1844+
hard_coded_receiver_trait_def_id,
18431845
cause.clone(),
18441846
potential_self_ty,
18451847
) {
@@ -1852,7 +1854,7 @@ fn receiver_is_valid<'tcx>(
18521854
cause.clone(),
18531855
wfcx.param_env,
18541856
potential_self_ty,
1855-
receiver_trait_def_id,
1857+
hard_coded_receiver_trait_def_id,
18561858
);
18571859
}
18581860
}

compiler/rustc_hir_typeck/src/method/probe.rs

+89-20
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,36 +517,90 @@ 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_receiver =
530+
Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty)
531+
.include_raw_pointers()
532+
.use_receiver_trait()
533+
.silence_errors();
534+
535+
let mut autoderef_via_deref =
520536
Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty)
521537
.include_raw_pointers()
522538
.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();
540539

541-
let final_ty = autoderef.final_ty(true);
540+
let mut reached_raw_pointer = false;
541+
let arbitrary_self_types_enabled =
542+
tcx.features().arbitrary_self_types() || tcx.features().arbitrary_self_types_pointers();
543+
let mut final_ty_reachable_by_deref = None;
544+
let mut steps: Vec<_> = if arbitrary_self_types_enabled {
545+
let reachable_via_deref =
546+
autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false));
547+
autoderef_via_receiver
548+
.by_ref()
549+
.zip(reachable_via_deref)
550+
.map(|((ty, d), reachable_via_deref)| {
551+
if reachable_via_deref {
552+
final_ty_reachable_by_deref = Some(ty);
553+
}
554+
let step = CandidateStep {
555+
self_ty: infcx
556+
.make_query_response_ignoring_pending_obligations(inference_vars, ty),
557+
autoderefs: d,
558+
from_unsafe_deref: reached_raw_pointer,
559+
unsize: false,
560+
reachable_via_deref,
561+
};
562+
if let ty::RawPtr(_, _) = ty.kind() {
563+
// all the subsequent steps will be from_unsafe_deref
564+
reached_raw_pointer = true;
565+
}
566+
step
567+
})
568+
.collect()
569+
} else {
570+
let steps = autoderef_via_deref
571+
.by_ref()
572+
.map(|(ty, d)| {
573+
let step = CandidateStep {
574+
self_ty: infcx
575+
.make_query_response_ignoring_pending_obligations(inference_vars, ty),
576+
autoderefs: d,
577+
from_unsafe_deref: reached_raw_pointer,
578+
unsize: false,
579+
reachable_via_deref: true,
580+
};
581+
if let ty::RawPtr(_, _) = ty.kind() {
582+
// all the subsequent steps will be from_unsafe_deref
583+
reached_raw_pointer = true;
584+
}
585+
step
586+
})
587+
.collect();
588+
final_ty_reachable_by_deref = Some(autoderef_via_deref.by_ref().final_ty(false));
589+
steps
590+
};
591+
let final_ty =
592+
final_ty_reachable_by_deref.expect("Should be at least one type in any autoderef");
593+
let final_ty = infcx.resolve_vars_if_possible(final_ty);
542594
let opt_bad_ty = match final_ty.kind() {
543595
ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
544596
reached_raw_pointer,
545597
ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
546598
}),
599+
547600
ty::Array(elem_ty, _) => {
548601
let dereferences = steps.len() - 1;
602+
let reachable_via_deref =
603+
steps.last().map(|cs| cs.reachable_via_deref).unwrap_or_default();
549604

550605
steps.push(CandidateStep {
551606
self_ty: infcx.make_query_response_ignoring_pending_obligations(
@@ -557,19 +612,29 @@ fn method_autoderef_steps<'tcx>(
557612
// a *mut/const [T; N]
558613
from_unsafe_deref: reached_raw_pointer,
559614
unsize: true,
615+
reachable_via_deref,
560616
});
561617

562618
None
563619
}
564620
_ => None,
565621
};
566622

623+
// FIXME - the previous section may emit an error if the final type reachable
624+
// by the Deref chain is ty::Infer(ty::TyVar(_)). We should consider emitting a warning
625+
// if the final type reachable by the Receiver chain is similarly problematic.
626+
567627
debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
568628

569629
MethodAutoderefStepsResult {
570630
steps: tcx.arena.alloc_from_iter(steps),
571631
opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)),
572-
reached_recursion_limit: autoderef.reached_recursion_limit(),
632+
reached_recursion_limit: if arbitrary_self_types_enabled {
633+
autoderef_via_receiver
634+
} else {
635+
autoderef_via_deref
636+
}
637+
.reached_recursion_limit(),
573638
}
574639
}
575640

@@ -1065,6 +1130,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
10651130
) -> Option<PickResult<'tcx>> {
10661131
self.steps
10671132
.iter()
1133+
// At this point we're considering the types to which the receiver can be converted,
1134+
// so we want to follow the `Deref` chain not the `Receiver` chain. Filter out
1135+
// steps which can only be reached by following the (longer) `Receiver` chain.
1136+
.filter(|step| step.reachable_via_deref)
10681137
.filter(|step| {
10691138
debug!("pick_all_method: step={:?}", step);
10701139
// skip types that are from a type error or that would require dereferencing

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>` 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>>,

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

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)