Skip to content

Commit

Permalink
Auto merge of rust-lang#132961 - adetaylor:arbitrary-self-types-the-b…
Browse files Browse the repository at this point in the history
…ig-bit, r=compiler-errors,wesleywiser

Arbitrary self types v2: main compiler changes

This is the main PR in a series of PRs related to Arbitrary Self Types v2, tracked in rust-lang#44874. Specifically this is step 7 of the plan [described here](rust-lang#44874 (comment)), for [RFC 3519](rust-lang/rfcs#3519).

Overall this PR:
* Switches from the `Deref` trait to the new `Receiver` trait when the unstable `arbitrary_self_types` feature is enabled (the simple bit)
* Introduces new algorithms to spot "shadowing"; that is, the case where a newly-added method in an outer smart pointer might end up overriding a pre-existing method in the pointee (the complex bit). Most of this bit was explored in [this earlier perf-testing PR](rust-lang#127812 (comment)).
* Lots of tests

This should not break compatibility for:
* Stable users, where it should have no effect
* Users of the existing `arbitrary_self_types` feature (because we implement `Receiver` for `T: Deref`) _unless_ those folks have added methods which may shadow methods in inner types, which we no longer want to allow

Subsequent PRs will add better diagnostics.

It's probably easiest to review this commit-by-commit.

r? `@wesleywiser`
  • Loading branch information
bors committed Dec 12, 2024
2 parents d4025ee + 337af8a commit 915e7eb
Show file tree
Hide file tree
Showing 50 changed files with 1,745 additions and 198 deletions.
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
Expand Up @@ -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)]
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 28 additions & 9 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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>,
Expand All @@ -39,6 +42,7 @@ pub struct Autoderef<'a, 'tcx> {

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

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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,
Expand All @@ -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,
}
}
Expand All @@ -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,
Expand All @@ -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);

Expand Down Expand Up @@ -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
Expand Down
27 changes: 17 additions & 10 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand All @@ -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,
) {
Expand All @@ -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,
);
}
}
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Loading

0 comments on commit 915e7eb

Please sign in to comment.