Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #19012 from ShoyuVanilla/arbitrary-self
feat: Implement `arbitrary-self-types`
Lukas Wirth 2025-01-24
parent 0f6feb0 · parent f4dfbc3 · commit 3a163fa
-rw-r--r--crates/hir-def/src/lang_item.rs1
-rw-r--r--crates/hir-ty/src/autoderef.rs41
-rw-r--r--crates/hir-ty/src/infer/coerce.rs2
-rw-r--r--crates/hir-ty/src/infer/expr.rs6
-rw-r--r--crates/hir-ty/src/method_resolution.rs10
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs47
-rw-r--r--crates/ide-completion/src/completions/dot.rs30
-rw-r--r--crates/intern/src/symbol/symbols.rs1
-rw-r--r--crates/test-utils/src/minicore.rs17
9 files changed, 129 insertions, 26 deletions
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index afdc49a2dc..e83ce6dc42 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -372,6 +372,7 @@ language_item_table! {
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
+ ReceiverTarget, sym::receiver_target, receiver_target, Target::AssocTy, GenericRequirement::None;
Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1);
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs
index 2c7076da11..62feca5f8c 100644
--- a/crates/hir-ty/src/autoderef.rs
+++ b/crates/hir-ty/src/autoderef.rs
@@ -39,7 +39,7 @@ pub fn autoderef(
) -> impl Iterator<Item = Ty> {
let mut table = InferenceTable::new(db, env);
let ty = table.instantiate_canonical(ty);
- let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false);
+ let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false);
let mut v = Vec::new();
while let Some((ty, _steps)) = autoderef.next() {
// `ty` may contain unresolved inference variables. Since there's no chance they would be
@@ -89,12 +89,18 @@ pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> {
at_start: bool,
steps: T,
explicit: bool,
+ use_receiver_trait: bool,
}
impl<'table, 'db> Autoderef<'table, 'db> {
- pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self {
+ pub(crate) fn new(
+ table: &'table mut InferenceTable<'db>,
+ ty: Ty,
+ explicit: bool,
+ use_receiver_trait: bool,
+ ) -> Self {
let ty = table.resolve_ty_shallow(&ty);
- Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit }
+ Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait }
}
pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
@@ -107,9 +113,10 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> {
table: &'table mut InferenceTable<'db>,
ty: Ty,
explicit: bool,
+ use_receiver_trait: bool,
) -> Self {
let ty = table.resolve_ty_shallow(&ty);
- Autoderef { table, ty, at_start: true, steps: 0, explicit }
+ Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait }
}
}
@@ -137,7 +144,8 @@ impl<T: TrackAutoderefSteps> Iterator for Autoderef<'_, '_, T> {
return None;
}
- let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?;
+ let (kind, new_ty) =
+ autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?;
self.steps.push(kind, &self.ty);
self.ty = new_ty;
@@ -150,11 +158,12 @@ pub(crate) fn autoderef_step(
table: &mut InferenceTable<'_>,
ty: Ty,
explicit: bool,
+ use_receiver_trait: bool,
) -> Option<(AutoderefKind, Ty)> {
if let Some(derefed) = builtin_deref(table.db, &ty, explicit) {
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
} else {
- Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
+ Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?))
}
}
@@ -176,6 +185,7 @@ pub(crate) fn builtin_deref<'ty>(
pub(crate) fn deref_by_trait(
table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>,
ty: Ty,
+ use_receiver_trait: bool,
) -> Option<Ty> {
let _p = tracing::info_span!("deref_by_trait").entered();
if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
@@ -183,14 +193,25 @@ pub(crate) fn deref_by_trait(
return None;
}
- let deref_trait =
- db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
+ let trait_id = || {
+ if use_receiver_trait {
+ if let Some(receiver) =
+ db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
+ {
+ return Some(receiver);
+ }
+ }
+ // Old rustc versions might not have `Receiver` trait.
+ // Fallback to `Deref` if they don't
+ db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())
+ };
+ let trait_id = trait_id()?;
let target = db
- .trait_data(deref_trait)
+ .trait_data(trait_id)
.associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?;
let projection = {
- let b = TyBuilder::subst_for_def(db, deref_trait, None);
+ let b = TyBuilder::subst_for_def(db, trait_id, None);
if b.remaining() != 1 {
// the Target type + Deref trait should only have one generic parameter,
// namely Deref's Self type
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 2fe90a8a92..d40816ba8c 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -420,7 +420,7 @@ impl InferenceTable<'_> {
let snapshot = self.snapshot();
- let mut autoderef = Autoderef::new(self, from_ty.clone(), false);
+ let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false);
let mut first_error = None;
let mut found = None;
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 6b6c0348dc..b951443897 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -487,7 +487,7 @@ impl InferenceContext<'_> {
}
Expr::Call { callee, args, .. } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes);
- let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
+ let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
let (res, derefed_callee) = loop {
let Some((callee_deref_ty, _)) = derefs.next() else {
break (None, callee_ty.clone());
@@ -854,7 +854,7 @@ impl InferenceContext<'_> {
if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) {
self.resolve_ty_shallow(derefed)
} else {
- deref_by_trait(&mut self.table, inner_ty)
+ deref_by_trait(&mut self.table, inner_ty, false)
.unwrap_or_else(|| self.err_ty())
}
}
@@ -1718,7 +1718,7 @@ impl InferenceContext<'_> {
receiver_ty: &Ty,
name: &Name,
) -> Option<(Ty, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> {
- let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false);
+ let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false);
let mut private_field = None;
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
let (field_id, parameters) = match derefed_ty.kind(Interner) {
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 182032f048..1cea67ee96 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -528,7 +528,7 @@ impl ReceiverAdjustments {
let mut ty = table.resolve_ty_shallow(&ty);
let mut adjust = Vec::new();
for _ in 0..self.autoderefs {
- match autoderef::autoderef_step(table, ty.clone(), true) {
+ match autoderef::autoderef_step(table, ty.clone(), true, false) {
None => {
never!("autoderef not possible for {:?}", ty);
ty = TyKind::Error.intern(Interner);
@@ -1106,7 +1106,8 @@ fn iterate_method_candidates_by_receiver(
// be found in any of the derefs of receiver_ty, so we have to go through
// that, including raw derefs.
table.run_in_snapshot(|table| {
- let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
+ let mut autoderef =
+ autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true);
while let Some((self_ty, _)) = autoderef.next() {
iterate_inherent_methods(
&self_ty,
@@ -1123,7 +1124,8 @@ fn iterate_method_candidates_by_receiver(
ControlFlow::Continue(())
})?;
table.run_in_snapshot(|table| {
- let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
+ let mut autoderef =
+ autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true);
while let Some((self_ty, _)) = autoderef.next() {
if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) {
// don't try to resolve methods on unknown types
@@ -1709,7 +1711,7 @@ fn autoderef_method_receiver(
ty: Ty,
) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
let mut deref_chain: Vec<_> = Vec::new();
- let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false);
+ let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true);
while let Some((ty, derefs)) = autoderef.next() {
deref_chain.push((
autoderef.table.canonicalize(ty),
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 74acf23b75..8866de22df 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1343,7 +1343,7 @@ fn foo<T: Trait>(a: &T) {
fn autoderef_visibility_field() {
check(
r#"
-//- minicore: deref
+//- minicore: receiver
mod a {
pub struct Foo(pub char);
pub struct Bar(i32);
@@ -1375,7 +1375,7 @@ fn autoderef_visibility_method() {
cov_mark::check!(autoderef_candidate_not_visible);
check(
r#"
-//- minicore: deref
+//- minicore: receiver
mod a {
pub struct Foo(pub char);
impl Foo {
@@ -1741,7 +1741,7 @@ fn main() {
fn deref_fun_1() {
check_types(
r#"
-//- minicore: deref
+//- minicore: receiver
struct A<T, U>(T, U);
struct B<T>(T);
@@ -1782,7 +1782,7 @@ fn test() {
fn deref_fun_2() {
check_types(
r#"
-//- minicore: deref
+//- minicore: receiver
struct A<T, U>(T, U);
struct B<T>(T);
@@ -1903,7 +1903,7 @@ pub fn test(generic_args: impl Into<Foo>) {
fn bad_inferred_reference_2() {
check_no_mismatches(
r#"
-//- minicore: deref
+//- minicore: receiver
trait ExactSizeIterator {
fn len(&self) -> usize;
}
@@ -2054,7 +2054,7 @@ fn foo() {
fn box_deref_is_builtin() {
check(
r#"
-//- minicore: deref
+//- minicore: receiver
use core::ops::Deref;
#[lang = "owned_box"]
@@ -2087,7 +2087,7 @@ fn test() {
fn manually_drop_deref_is_not_builtin() {
check(
r#"
-//- minicore: manually_drop, deref
+//- minicore: manually_drop, receiver
struct Foo;
impl Foo {
fn foo(&self) {}
@@ -2105,7 +2105,7 @@ fn test() {
fn mismatched_args_due_to_supertraits_with_deref() {
check_no_mismatches(
r#"
-//- minicore: deref
+//- minicore: receiver
use core::ops::Deref;
trait Trait1 {
@@ -2139,3 +2139,34 @@ fn problem_method<T: Trait3>() {
"#,
);
}
+
+#[test]
+fn receiver_without_deref_impl() {
+ check(
+ r#"
+//- minicore: receiver
+use core::ops::Receiver;
+
+struct Foo;
+
+impl Foo {
+ fn foo1(self: &Bar) -> i32 { 42 }
+ fn foo2(self: Bar) -> bool { true }
+}
+
+struct Bar;
+
+impl Receiver for Bar {
+ type Target = Foo;
+}
+
+fn main() {
+ let bar = Bar;
+ let _v1 = bar.foo1();
+ //^^^ type: i32
+ let _v2 = bar.foo2();
+ //^^^ type: bool
+}
+"#,
+ );
+}
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 0d1765a7b7..d12654665c 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -1475,4 +1475,34 @@ async fn bar() {
"#,
);
}
+
+ #[test]
+ fn receiver_without_deref_impl_completion() {
+ check_no_kw(
+ r#"
+//- minicore: receiver
+use core::ops::Receiver;
+
+struct Foo;
+
+impl Foo {
+ fn foo(self: Bar) {}
+}
+
+struct Bar;
+
+impl Receiver for Bar {
+ type Target = Foo;
+}
+
+fn main() {
+ let bar = Bar;
+ bar.$0
+}
+"#,
+ expect![[r#"
+ me foo() fn(self: Bar)
+"#]],
+ );
+ }
}
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 7a090a6b6b..9bc78ff87b 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -394,6 +394,7 @@ define_symbols! {
RangeToInclusive,
Ready,
receiver,
+ receiver_target,
recursion_limit,
register_attr,
register_tool,
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index fd06736a25..4ed68d18e8 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -53,6 +53,7 @@
//! pin:
//! pointee: copy, send, sync, ord, hash, unpin
//! range:
+//! receiver: deref
//! result:
//! send: sized
//! size_of: sized
@@ -513,10 +514,26 @@ pub mod ops {
fn deref_mut(&mut self) -> &mut Self::Target;
}
// endregion:deref_mut
+
+ // region:receiver
+ #[lang = "receiver"]
+ pub trait Receiver {
+ #[lang = "receiver_target"]
+ type Target: ?Sized;
+ }
+
+ impl<P: ?Sized, T: ?Sized> Receiver for P
+ where
+ P: Deref<Target = T>,
+ {
+ type Target = T;
+ }
+ // endregion:receiver
}
pub use self::deref::{
Deref,
DerefMut, // :deref_mut
+ Receiver, // :receiver
};
// endregion:deref