Skip to content

Commit

Permalink
feat: Implement arbitrary-self-types
Browse files Browse the repository at this point in the history
  • Loading branch information
ShoyuVanilla committed Jan 24, 2025
1 parent 84d44d0 commit f4dfbc3
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 26 deletions.
1 change: 1 addition & 0 deletions crates/hir-def/src/lang_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
41 changes: 31 additions & 10 deletions crates/hir-ty/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)] {
Expand All @@ -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 }
}
}

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

Expand All @@ -176,21 +185,33 @@ 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() {
// don't try to deref unknown variables
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
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/coerce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 3 additions & 3 deletions crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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())
}
}
Expand Down Expand Up @@ -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) {
Expand Down
10 changes: 6 additions & 4 deletions crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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),
Expand Down
47 changes: 39 additions & 8 deletions crates/hir-ty/src/tests/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -2054,7 +2054,7 @@ fn foo() {
fn box_deref_is_builtin() {
check(
r#"
//- minicore: deref
//- minicore: receiver
use core::ops::Deref;
#[lang = "owned_box"]
Expand Down Expand Up @@ -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) {}
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
"#,
);
}
30 changes: 30 additions & 0 deletions crates/ide-completion/src/completions/dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1466,4 +1466,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)
"#]],
);
}
}
1 change: 1 addition & 0 deletions crates/intern/src/symbol/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ define_symbols! {
RangeToInclusive,
Ready,
receiver,
receiver_target,
recursion_limit,
register_attr,
register_tool,
Expand Down
17 changes: 17 additions & 0 deletions crates/test-utils/src/minicore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
//! pin:
//! pointee: copy, send, sync, ord, hash, unpin
//! range:
//! receiver: deref
//! result:
//! send: sized
//! size_of: sized
Expand Down Expand Up @@ -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

Expand Down

0 comments on commit f4dfbc3

Please sign in to comment.