Skip to content

Arbitrary self types v2: main compiler changes #132961

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

Merged
merged 6 commits into from
Dec 13, 2024
Merged
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
6 changes: 4 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0307.md
Original file line number Diff line number Diff line change
@@ -65,8 +65,10 @@ impl Trait for Foo {
```

The nightly feature [Arbitrary self types][AST] extends the accepted
set of receiver types to also include any type that can dereference to
`Self`:
set of receiver types to also include any type that implements the
`Receiver` trait and can follow its chain of `Target` types to `Self`.
There's a blanket implementation of `Receiver` for `T: Deref`, so any
type which dereferences to `Self` can be used.

```
#![feature(arbitrary_self_types)]
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
@@ -241,10 +241,10 @@ hir_analysis_invalid_generic_receiver_ty_help =
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`)
hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty}`
.note = type of `self` must be `Self` or a type that dereferences to it
.note = type of `self` must be `Self` or some type implementing `Receiver`
hir_analysis_invalid_receiver_ty_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`)
consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>`
hir_analysis_invalid_union_field =
field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
37 changes: 28 additions & 9 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@ pub enum AutoderefKind {
/// A type which must dispatch to a `Deref` implementation.
Overloaded,
}

struct AutoderefSnapshot<'tcx> {
at_start: bool,
reached_recursion_limit: bool,
@@ -27,6 +26,10 @@ struct AutoderefSnapshot<'tcx> {
obligations: PredicateObligations<'tcx>,
}

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

// Configurations:
include_raw_pointers: bool,
use_receiver_trait: bool,
silence_errors: bool,
}

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

// Otherwise, deref if type is derefable:
// NOTE: in the case of self.use_receiver_trait = true, you might think it would
// be better to skip this clause and use the Overloaded case only, since &T
// and &mut T implement Receiver. But built-in derefs apply equally to Receiver
// and Deref, and this has benefits for const and the emitted MIR.
let (kind, new_ty) =
if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty));
@@ -111,7 +119,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
body_def_id: LocalDefId,
span: Span,
base_ty: Ty<'tcx>,
) -> Autoderef<'a, 'tcx> {
) -> Self {
Autoderef {
infcx,
span,
@@ -125,6 +133,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
reached_recursion_limit: false,
},
include_raw_pointers: false,
use_receiver_trait: false,
silence_errors: false,
}
}
@@ -137,8 +146,13 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
return None;
}

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

let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection(
tcx,
tcx.lang_items().deref_target()?,
[ty],
))?;
let (normalized_ty, obligations) =
self.structurally_normalize(Ty::new_projection(tcx, trait_target_def_id, [ty]))?;
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
self.state.obligations.extend(obligations);

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

/// Use `core::ops::Receiver` and `core::ops::Receiver::Target` as
/// the trait and associated type to iterate, instead of
/// `core::ops::Deref` and `core::ops::Deref::Target`
pub fn use_receiver_trait(mut self) -> Self {
self.use_receiver_trait = true;
self
}

pub fn silence_errors(mut self) -> Self {
self.silence_errors = true;
self
27 changes: 17 additions & 10 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
@@ -1821,13 +1821,18 @@ fn receiver_is_valid<'tcx>(

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

// The `arbitrary_self_types` feature allows custom smart pointer
// types to be method receivers, as identified by following the Receiver<Target=T>
// chain.
if arbitrary_self_types_enabled.is_some() {
autoderef = autoderef.use_receiver_trait();
}

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

let receiver_trait_def_id = tcx.require_lang_item(LangItem::LegacyReceiver, Some(span));

// Keep dereferencing `receiver_ty` until we get to `self_ty`.
while let Some((potential_self_ty, _)) = autoderef.next() {
debug!(
@@ -1849,11 +1854,13 @@ fn receiver_is_valid<'tcx>(
}

// Without `feature(arbitrary_self_types)`, we require that each step in the
// deref chain implement `receiver`.
// deref chain implement `LegacyReceiver`.
if arbitrary_self_types_enabled.is_none() {
if !receiver_is_implemented(
let legacy_receiver_trait_def_id =
tcx.require_lang_item(LangItem::LegacyReceiver, Some(span));
if !legacy_receiver_is_implemented(
wfcx,
receiver_trait_def_id,
legacy_receiver_trait_def_id,
cause.clone(),
potential_self_ty,
) {
@@ -1866,7 +1873,7 @@ fn receiver_is_valid<'tcx>(
cause.clone(),
wfcx.param_env,
potential_self_ty,
receiver_trait_def_id,
legacy_receiver_trait_def_id,
);
}
}
@@ -1875,22 +1882,22 @@ fn receiver_is_valid<'tcx>(
Err(ReceiverValidityError::DoesNotDeref)
}

fn receiver_is_implemented<'tcx>(
fn legacy_receiver_is_implemented<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
receiver_trait_def_id: DefId,
legacy_receiver_trait_def_id: DefId,
cause: ObligationCause<'tcx>,
receiver_ty: Ty<'tcx>,
) -> bool {
let tcx = wfcx.tcx();
let trait_ref = ty::TraitRef::new(tcx, receiver_trait_def_id, [receiver_ty]);
let trait_ref = ty::TraitRef::new(tcx, legacy_receiver_trait_def_id, [receiver_ty]);

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

if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) {
true
} else {
debug!(
"receiver_is_implemented: type `{:?}` does not implement `Receiver` trait",
"receiver_is_implemented: type `{:?}` does not implement `LegacyReceiver` trait",
receiver_ty
);
false
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
@@ -917,7 +917,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
[candidate] => format!(
"the method of the same name on {} `{}`",
match candidate.kind {
probe::CandidateKind::InherentImplCandidate(_) => "the inherent impl for",
probe::CandidateKind::InherentImplCandidate { .. } => "the inherent impl for",
_ => "trait",
},
self.tcx.def_path_str(candidate.item.container_id(self.tcx))
Loading