diff options
author | Florian Diebold <[email protected]> | 2019-05-12 17:33:47 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-06-15 17:21:23 +0100 |
commit | 9c5e7dd849eff7bd6f20aa353feef083d089ff58 (patch) | |
tree | 5639ff8814d3f4ba0f5ac41de215ed3720f3d1b9 /crates/ra_hir/src | |
parent | 49489dc20cc9f340d43acb467677b9bc59495ed2 (diff) |
Implement autoderef using the Deref trait
- add support for other lang item targets, since we need the Deref lang item
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r-- | crates/ra_hir/src/lang_item.rs | 55 | ||||
-rw-r--r-- | crates/ra_hir/src/name.rs | 5 | ||||
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 5 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/autoderef.rs | 68 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 40 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 5 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/traits.rs | 4 |
8 files changed, 149 insertions, 44 deletions
diff --git a/crates/ra_hir/src/lang_item.rs b/crates/ra_hir/src/lang_item.rs index cdc9182d6..ada8aeb5b 100644 --- a/crates/ra_hir/src/lang_item.rs +++ b/crates/ra_hir/src/lang_item.rs | |||
@@ -4,7 +4,7 @@ use rustc_hash::FxHashMap; | |||
4 | use ra_syntax::{SmolStr, ast::AttrsOwner}; | 4 | use ra_syntax::{SmolStr, ast::AttrsOwner}; |
5 | 5 | ||
6 | use crate::{ | 6 | 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 |
8 | }; | 8 | }; |
9 | 9 | ||
10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -87,19 +87,48 @@ impl LangItems { | |||
87 | let source = module.definition_source(db).ast; | 87 | let source = module.definition_source(db).ast; |
88 | for (impl_id, _) in impl_blocks.impls.iter() { | 88 | for (impl_id, _) in impl_blocks.impls.iter() { |
89 | let impl_block = source_map.get(&source, impl_id); | 89 | let impl_block = source_map.get(&source, impl_id); |
90 | let lang_item_name = impl_block | 90 | if let Some(lang_item_name) = 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 { | ||
97 | let imp = ImplBlock::from_id(*module, impl_id); | 91 | let imp = ImplBlock::from_id(*module, impl_id); |
98 | self.items.entry(lang_item_name).or_insert_with(|| LangItemTarget::ImplBlock(imp)); | 92 | self.items.entry(lang_item_name).or_insert_with(|| LangItemTarget::ImplBlock(imp)); |
99 | } | 93 | } |
100 | } | 94 | } |
101 | 95 | ||
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 | } | ||
103 | 132 | ||
104 | // Look for lang items in the children | 133 | // Look for lang items in the children |
105 | for child in module.children(db) { | 134 | for child in module.children(db) { |
@@ -107,3 +136,11 @@ impl LangItems { | |||
107 | } | 136 | } |
108 | } | 137 | } |
109 | } | 138 | } |
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 | } | ||
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index e9003e00b..ba17958eb 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs | |||
@@ -46,6 +46,11 @@ impl Name { | |||
46 | Name::new(idx.to_string().into()) | 46 | Name::new(idx.to_string().into()) |
47 | } | 47 | } |
48 | 48 | ||
49 | // Needed for Deref | ||
50 | pub(crate) fn target() -> Name { | ||
51 | Name::new("Target".into()) | ||
52 | } | ||
53 | |||
49 | // There's should be no way to extract a string out of `Name`: `Name` in the | 54 | // There's should be no way to extract a string out of `Name`: `Name` in the |
50 | // future, `Name` will include hygiene information, and you can't encode | 55 | // future, `Name` will include hygiene information, and you can't encode |
51 | // hygiene into a String. | 56 | // hygiene into a String. |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 4f9e8c5a9..08e86844d 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -369,6 +369,17 @@ impl SourceAnalyzer { | |||
369 | ) | 369 | ) |
370 | } | 370 | } |
371 | 371 | ||
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 | |||
372 | #[cfg(test)] | 383 | #[cfg(test)] |
373 | pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { | 384 | pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { |
374 | self.body_source_map.clone().unwrap() | 385 | self.body_source_map.clone().unwrap() |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d9a50b230..d2f92a1f2 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -22,6 +22,7 @@ use display::{HirDisplay, HirFormatter}; | |||
22 | pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig, generic_predicates, generic_defaults}; | 22 | pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig, generic_predicates, generic_defaults}; |
23 | pub(crate) use infer::{infer_query, InferenceResult, InferTy}; | 23 | pub(crate) use infer::{infer_query, InferenceResult, InferTy}; |
24 | pub use lower::CallableDef; | 24 | pub use lower::CallableDef; |
25 | pub(crate) use autoderef::autoderef; | ||
25 | 26 | ||
26 | /// A type constructor or type name: this might be something like the primitive | 27 | /// A type constructor or type name: this might be something like the primitive |
27 | /// type `bool`, a struct like `Vec`, or things like function pointers or | 28 | /// type `bool`, a struct like `Vec`, or things like function pointers or |
@@ -225,8 +226,8 @@ impl Deref for Substs { | |||
225 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 226 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
226 | pub struct TraitRef { | 227 | pub struct TraitRef { |
227 | /// FIXME name? | 228 | /// FIXME name? |
228 | trait_: Trait, | 229 | pub trait_: Trait, |
229 | substs: Substs, | 230 | pub substs: Substs, |
230 | } | 231 | } |
231 | 232 | ||
232 | impl TraitRef { | 233 | impl TraitRef { |
diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index a442a856c..bee756d80 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs | |||
@@ -5,17 +5,67 @@ | |||
5 | 5 | ||
6 | use std::iter::successors; | 6 | use std::iter::successors; |
7 | 7 | ||
8 | use crate::HirDatabase; | 8 | use log::info; |
9 | use super::Ty; | ||
10 | 9 | ||
11 | impl Ty { | 10 | use crate::{HirDatabase, Name, Resolver}; |
12 | /// Iterates over the possible derefs of `ty`. | 11 | use super::{traits::Solution, Ty, Canonical}; |
13 | pub fn autoderef<'a>(self, db: &'a impl HirDatabase) -> impl Iterator<Item = Ty> + 'a { | 12 | |
14 | successors(Some(self), move |ty| ty.autoderef_step(db)) | 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) | ||
15 | } | 30 | } |
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)?; | ||
16 | 61 | ||
17 | fn autoderef_step(&self, _db: &impl HirDatabase) -> Option<Ty> { | 62 | match &solution { |
18 | // FIXME Deref::deref | 63 | Solution::Unique(vars) => { |
19 | self.builtin_deref() | 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 | } | ||
20 | } | 70 | } |
21 | } | 71 | } |
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index e150d7fd8..fdb444de2 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -46,7 +46,7 @@ use crate::{ | |||
46 | use super::{ | 46 | use super::{ |
47 | Ty, TypableDef, Substs, primitive, op, ApplicationTy, TypeCtor, CallableDef, TraitRef, | 47 | Ty, TypableDef, Substs, primitive, op, ApplicationTy, TypeCtor, CallableDef, TraitRef, |
48 | traits::{Solution, Obligation, Guidance}, | 48 | traits::{Solution, Obligation, Guidance}, |
49 | method_resolution, | 49 | method_resolution, autoderef, |
50 | }; | 50 | }; |
51 | 51 | ||
52 | mod unify; | 52 | mod unify; |
@@ -1074,25 +1074,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1074 | } | 1074 | } |
1075 | Expr::Field { expr, name } => { | 1075 | Expr::Field { expr, name } => { |
1076 | let receiver_ty = self.infer_expr(*expr, &Expectation::none()); | 1076 | let receiver_ty = self.infer_expr(*expr, &Expectation::none()); |
1077 | let ty = receiver_ty | 1077 | let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty); |
1078 | .autoderef(self.db) | 1078 | let ty = autoderef::autoderef( |
1079 | .find_map(|derefed_ty| match derefed_ty { | 1079 | self.db, |
1080 | Ty::Apply(a_ty) => match a_ty.ctor { | 1080 | &self.resolver.clone(), |
1081 | TypeCtor::Tuple { .. } => { | 1081 | canonicalized.value.clone(), |
1082 | let i = name.to_string().parse::<usize>().ok(); | 1082 | ) |
1083 | i.and_then(|i| a_ty.parameters.0.get(i).cloned()) | 1083 | .find_map(|derefed_ty| match canonicalized.decanonicalize_ty(derefed_ty.value) { |
1084 | } | 1084 | Ty::Apply(a_ty) => match a_ty.ctor { |
1085 | TypeCtor::Adt(AdtDef::Struct(s)) => { | 1085 | TypeCtor::Tuple { .. } => { |
1086 | s.field(self.db, name).map(|field| { | 1086 | let i = name.to_string().parse::<usize>().ok(); |
1087 | self.write_field_resolution(tgt_expr, field); | 1087 | i.and_then(|i| a_ty.parameters.0.get(i).cloned()) |
1088 | field.ty(self.db).subst(&a_ty.parameters) | 1088 | } |
1089 | }) | 1089 | TypeCtor::Adt(AdtDef::Struct(s)) => s.field(self.db, name).map(|field| { |
1090 | } | 1090 | self.write_field_resolution(tgt_expr, field); |
1091 | _ => None, | 1091 | field.ty(self.db).subst(&a_ty.parameters) |
1092 | }, | 1092 | }), |
1093 | _ => None, | 1093 | _ => None, |
1094 | }) | 1094 | }, |
1095 | .unwrap_or(Ty::Unknown); | 1095 | _ => None, |
1096 | }) | ||
1097 | .unwrap_or(Ty::Unknown); | ||
1096 | self.insert_type_vars(ty) | 1098 | self.insert_type_vars(ty) |
1097 | } | 1099 | } |
1098 | Expr::Try { expr } => { | 1100 | Expr::Try { expr } => { |
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 646e58aa9..ad26d591c 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -16,7 +16,7 @@ use crate::{ | |||
16 | generics::HasGenericParams, | 16 | generics::HasGenericParams, |
17 | ty::primitive::{UncertainIntTy, UncertainFloatTy} | 17 | ty::primitive::{UncertainIntTy, UncertainFloatTy} |
18 | }; | 18 | }; |
19 | use super::{TraitRef, Canonical}; | 19 | use super::{TraitRef, Canonical, autoderef}; |
20 | 20 | ||
21 | /// This is used as a key for indexing impls. | 21 | /// This is used as a key for indexing impls. |
22 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 22 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
@@ -162,8 +162,7 @@ pub(crate) fn iterate_method_candidates<T>( | |||
162 | // rustc does an autoderef and then autoref again). | 162 | // rustc does an autoderef and then autoref again). |
163 | 163 | ||
164 | let krate = resolver.krate()?; | 164 | let krate = resolver.krate()?; |
165 | for derefed_ty in ty.value.clone().autoderef(db) { | 165 | for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { |
166 | let derefed_ty = Canonical { value: derefed_ty, num_vars: ty.num_vars }; | ||
167 | if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) | 166 | if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) |
168 | { | 167 | { |
169 | return Some(result); | 168 | return Some(result); |
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index f3e488403..6cf3dd70a 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs | |||
@@ -80,8 +80,8 @@ pub enum Obligation { | |||
80 | 80 | ||
81 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | 81 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] |
82 | pub struct ProjectionPredicate { | 82 | pub struct ProjectionPredicate { |
83 | projection_ty: ProjectionTy, | 83 | pub projection_ty: ProjectionTy, |
84 | ty: Ty, | 84 | pub ty: Ty, |
85 | } | 85 | } |
86 | 86 | ||
87 | /// Check using Chalk whether trait is implemented for given parameters including `Self` type. | 87 | /// Check using Chalk whether trait is implemented for given parameters including `Self` type. |