Skip to content

Commit 9c5e7dd

Browse files
committed
Implement autoderef using the Deref trait
- add support for other lang item targets, since we need the Deref lang item
1 parent 49489dc commit 9c5e7dd

File tree

11 files changed

+152
-47
lines changed

11 files changed

+152
-47
lines changed

crates/ra_assists/src/fill_match_arms.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
2222
let expr = match_expr.expr()?;
2323
let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None);
2424
let match_expr_ty = analyzer.type_of(ctx.db, expr)?;
25-
let enum_def = match_expr_ty.autoderef(ctx.db).find_map(|ty| match ty.as_adt() {
25+
let enum_def = analyzer.autoderef(ctx.db, match_expr_ty).find_map(|ty| match ty.as_adt() {
2626
Some((AdtDef::Enum(e), _)) => Some(e),
2727
_ => None,
2828
})?;

crates/ra_hir/src/lang_item.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_hash::FxHashMap;
44
use ra_syntax::{SmolStr, ast::AttrsOwner};
55

66
use crate::{
7-
Crate, DefDatabase, Enum, Function, HirDatabase, ImplBlock, Module, Static, Struct, Trait, AstDatabase,
7+
Crate, DefDatabase, Enum, Function, HirDatabase, ImplBlock, Module, Static, Struct, Trait, ModuleDef, AstDatabase, HasSource
88
};
99

1010
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -87,23 +87,60 @@ impl LangItems {
8787
let source = module.definition_source(db).ast;
8888
for (impl_id, _) in impl_blocks.impls.iter() {
8989
let impl_block = source_map.get(&source, impl_id);
90-
let lang_item_name = impl_block
91-
.attrs()
92-
.filter_map(|a| a.as_key_value())
93-
.filter(|(key, _)| key == "lang")
94-
.map(|(_, val)| val)
95-
.nth(0);
96-
if let Some(lang_item_name) = lang_item_name {
90+
if let Some(lang_item_name) = lang_item_name(&*impl_block) {
9791
let imp = ImplBlock::from_id(*module, impl_id);
9892
self.items.entry(lang_item_name).or_insert_with(|| LangItemTarget::ImplBlock(imp));
9993
}
10094
}
10195

102-
// FIXME we should look for the other lang item targets (traits, structs, ...)
96+
// FIXME make this nicer
97+
for def in module.declarations(db) {
98+
match def {
99+
ModuleDef::Trait(trait_) => {
100+
let node = trait_.source(db).ast;
101+
if let Some(lang_item_name) = lang_item_name(&*node) {
102+
self.items.entry(lang_item_name).or_insert(LangItemTarget::Trait(trait_));
103+
}
104+
}
105+
ModuleDef::Enum(e) => {
106+
let node = e.source(db).ast;
107+
if let Some(lang_item_name) = lang_item_name(&*node) {
108+
self.items.entry(lang_item_name).or_insert(LangItemTarget::Enum(e));
109+
}
110+
}
111+
ModuleDef::Struct(s) => {
112+
let node = s.source(db).ast;
113+
if let Some(lang_item_name) = lang_item_name(&*node) {
114+
self.items.entry(lang_item_name).or_insert(LangItemTarget::Struct(s));
115+
}
116+
}
117+
ModuleDef::Function(f) => {
118+
let node = f.source(db).ast;
119+
if let Some(lang_item_name) = lang_item_name(&*node) {
120+
self.items.entry(lang_item_name).or_insert(LangItemTarget::Function(f));
121+
}
122+
}
123+
ModuleDef::Static(s) => {
124+
let node = s.source(db).ast;
125+
if let Some(lang_item_name) = lang_item_name(&*node) {
126+
self.items.entry(lang_item_name).or_insert(LangItemTarget::Static(s));
127+
}
128+
}
129+
_ => {}
130+
}
131+
}
103132

104133
// Look for lang items in the children
105134
for child in module.children(db) {
106135
self.collect_lang_items_recursive(db, &child);
107136
}
108137
}
109138
}
139+
140+
fn lang_item_name<T: AttrsOwner>(node: &T) -> Option<SmolStr> {
141+
node.attrs()
142+
.filter_map(|a| a.as_key_value())
143+
.filter(|(key, _)| key == "lang")
144+
.map(|(_, val)| val)
145+
.nth(0)
146+
}

crates/ra_hir/src/name.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ impl Name {
4646
Name::new(idx.to_string().into())
4747
}
4848

49+
// Needed for Deref
50+
pub(crate) fn target() -> Name {
51+
Name::new("Target".into())
52+
}
53+
4954
// There's should be no way to extract a string out of `Name`: `Name` in the
5055
// future, `Name` will include hygiene information, and you can't encode
5156
// hygiene into a String.

crates/ra_hir/src/source_binder.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,17 @@ impl SourceAnalyzer {
369369
)
370370
}
371371

372+
pub fn autoderef<'a>(
373+
&'a self,
374+
db: &'a impl HirDatabase,
375+
ty: Ty,
376+
) -> impl Iterator<Item = Ty> + 'a {
377+
// There should be no inference vars in types passed here
378+
// FIXME check that?
379+
let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
380+
crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value)
381+
}
382+
372383
#[cfg(test)]
373384
pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
374385
self.body_source_map.clone().unwrap()

crates/ra_hir/src/ty.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use display::{HirDisplay, HirFormatter};
2222
pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig, generic_predicates, generic_defaults};
2323
pub(crate) use infer::{infer_query, InferenceResult, InferTy};
2424
pub use lower::CallableDef;
25+
pub(crate) use autoderef::autoderef;
2526

2627
/// A type constructor or type name: this might be something like the primitive
2728
/// type `bool`, a struct like `Vec`, or things like function pointers or
@@ -225,8 +226,8 @@ impl Deref for Substs {
225226
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
226227
pub struct TraitRef {
227228
/// FIXME name?
228-
trait_: Trait,
229-
substs: Substs,
229+
pub trait_: Trait,
230+
pub substs: Substs,
230231
}
231232

232233
impl TraitRef {

crates/ra_hir/src/ty/autoderef.rs

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,67 @@
55
66
use std::iter::successors;
77

8-
use crate::HirDatabase;
9-
use super::Ty;
8+
use log::info;
109

11-
impl Ty {
12-
/// Iterates over the possible derefs of `ty`.
13-
pub fn autoderef<'a>(self, db: &'a impl HirDatabase) -> impl Iterator<Item = Ty> + 'a {
14-
successors(Some(self), move |ty| ty.autoderef_step(db))
10+
use crate::{HirDatabase, Name, Resolver};
11+
use super::{traits::Solution, Ty, Canonical};
12+
13+
pub(crate) fn autoderef<'a>(
14+
db: &'a impl HirDatabase,
15+
resolver: &'a Resolver,
16+
ty: Canonical<Ty>,
17+
) -> impl Iterator<Item = Canonical<Ty>> + 'a {
18+
successors(Some(ty), move |ty| deref(db, resolver, ty))
19+
}
20+
21+
pub(crate) fn deref(
22+
db: &impl HirDatabase,
23+
resolver: &Resolver,
24+
ty: &Canonical<Ty>,
25+
) -> Option<Canonical<Ty>> {
26+
if let Some(derefed) = ty.value.builtin_deref() {
27+
Some(Canonical { value: derefed, num_vars: ty.num_vars })
28+
} else {
29+
deref_by_trait(db, resolver, ty)
1530
}
31+
}
32+
33+
fn deref_by_trait(
34+
db: &impl HirDatabase,
35+
resolver: &Resolver,
36+
ty: &Canonical<Ty>,
37+
) -> Option<Canonical<Ty>> {
38+
let krate = resolver.krate()?;
39+
let deref_trait = match db.lang_item(krate, "deref".into())? {
40+
crate::lang_item::LangItemTarget::Trait(t) => t,
41+
_ => return None,
42+
};
43+
let target = deref_trait.associated_type_by_name(db, Name::target())?;
44+
45+
// FIXME we should check that Deref has no type parameters, because we assume it below
46+
47+
// FIXME make the Canonical handling nicer
48+
// TODO shift inference variables in ty
49+
50+
let projection = super::traits::ProjectionPredicate {
51+
ty: Ty::Bound(0),
52+
projection_ty: super::ProjectionTy {
53+
associated_ty: target,
54+
parameters: vec![ty.value.clone()].into(),
55+
},
56+
};
57+
58+
let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: projection };
59+
60+
let solution = db.normalize(krate, canonical)?;
1661

17-
fn autoderef_step(&self, _db: &impl HirDatabase) -> Option<Ty> {
18-
// FIXME Deref::deref
19-
self.builtin_deref()
62+
match &solution {
63+
Solution::Unique(vars) => {
64+
Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars })
65+
}
66+
Solution::Ambig(_) => {
67+
info!("Ambiguous solution for deref: {:?}", solution);
68+
None
69+
}
2070
}
2171
}

crates/ra_hir/src/ty/infer.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use crate::{
4646
use super::{
4747
Ty, TypableDef, Substs, primitive, op, ApplicationTy, TypeCtor, CallableDef, TraitRef,
4848
traits::{Solution, Obligation, Guidance},
49-
method_resolution,
49+
method_resolution, autoderef,
5050
};
5151

5252
mod unify;
@@ -1074,25 +1074,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
10741074
}
10751075
Expr::Field { expr, name } => {
10761076
let receiver_ty = self.infer_expr(*expr, &Expectation::none());
1077-
let ty = receiver_ty
1078-
.autoderef(self.db)
1079-
.find_map(|derefed_ty| match derefed_ty {
1080-
Ty::Apply(a_ty) => match a_ty.ctor {
1081-
TypeCtor::Tuple { .. } => {
1082-
let i = name.to_string().parse::<usize>().ok();
1083-
i.and_then(|i| a_ty.parameters.0.get(i).cloned())
1084-
}
1085-
TypeCtor::Adt(AdtDef::Struct(s)) => {
1086-
s.field(self.db, name).map(|field| {
1087-
self.write_field_resolution(tgt_expr, field);
1088-
field.ty(self.db).subst(&a_ty.parameters)
1089-
})
1090-
}
1091-
_ => None,
1092-
},
1077+
let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty);
1078+
let ty = autoderef::autoderef(
1079+
self.db,
1080+
&self.resolver.clone(),
1081+
canonicalized.value.clone(),
1082+
)
1083+
.find_map(|derefed_ty| match canonicalized.decanonicalize_ty(derefed_ty.value) {
1084+
Ty::Apply(a_ty) => match a_ty.ctor {
1085+
TypeCtor::Tuple { .. } => {
1086+
let i = name.to_string().parse::<usize>().ok();
1087+
i.and_then(|i| a_ty.parameters.0.get(i).cloned())
1088+
}
1089+
TypeCtor::Adt(AdtDef::Struct(s)) => s.field(self.db, name).map(|field| {
1090+
self.write_field_resolution(tgt_expr, field);
1091+
field.ty(self.db).subst(&a_ty.parameters)
1092+
}),
10931093
_ => None,
1094-
})
1095-
.unwrap_or(Ty::Unknown);
1094+
},
1095+
_ => None,
1096+
})
1097+
.unwrap_or(Ty::Unknown);
10961098
self.insert_type_vars(ty)
10971099
}
10981100
Expr::Try { expr } => {

crates/ra_hir/src/ty/method_resolution.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
generics::HasGenericParams,
1717
ty::primitive::{UncertainIntTy, UncertainFloatTy}
1818
};
19-
use super::{TraitRef, Canonical};
19+
use super::{TraitRef, Canonical, autoderef};
2020

2121
/// This is used as a key for indexing impls.
2222
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -162,8 +162,7 @@ pub(crate) fn iterate_method_candidates<T>(
162162
// rustc does an autoderef and then autoref again).
163163

164164
let krate = resolver.krate()?;
165-
for derefed_ty in ty.value.clone().autoderef(db) {
166-
let derefed_ty = Canonical { value: derefed_ty, num_vars: ty.num_vars };
165+
for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
167166
if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback)
168167
{
169168
return Some(result);

crates/ra_hir/src/ty/traits.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ pub enum Obligation {
8080

8181
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
8282
pub struct ProjectionPredicate {
83-
projection_ty: ProjectionTy,
84-
ty: Ty,
83+
pub projection_ty: ProjectionTy,
84+
pub ty: Ty,
8585
}
8686

8787
/// Check using Chalk whether trait is implemented for given parameters including `Self` type.

crates/ra_ide_api/src/completion/complete_dot.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
1515
}
1616

1717
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
18-
for receiver in receiver.autoderef(ctx.db) {
18+
for receiver in ctx.analyzer.autoderef(ctx.db, receiver) {
1919
if let Ty::Apply(a_ty) = receiver {
2020
match a_ty.ctor {
2121
TypeCtor::Adt(AdtDef::Struct(s)) => {

crates/ra_ide_api/src/goto_type_definition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub(crate) fn goto_type_definition(
3030
return None;
3131
};
3232

33-
let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt().map(|adt| adt.0))?;
33+
let adt_def = analyzer.autoderef(db, ty).find_map(|ty| ty.as_adt().map(|adt| adt.0))?;
3434

3535
let nav = NavigationTarget::from_adt_def(db, adt_def);
3636
Some(RangeInfo::new(node.range(), vec![nav]))

0 commit comments

Comments
 (0)