Skip to content

Commit b81caed

Browse files
bors[bot]flodiebold
andcommitted
Merge #1408
1408: Associated type basics & Deref support r=matklad a=flodiebold This adds the necessary Chalk integration to handle associated types and uses it to implement support for `Deref` in the `*` operator and autoderef; so e.g. dot completions through an `Arc` work now. It doesn't yet implement resolution of associated types in paths, though. Also, there's a big FIXME about handling variables in the solution we get from Chalk correctly. Co-authored-by: Florian Diebold <[email protected]>
2 parents e6fbff3 + ad3673d commit b81caed

19 files changed

+454
-70
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_assists/src/fill_match_arms.rs

+1-1
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/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ ra_prof = { path = "../ra_prof" }
2525
chalk-solve = { git = "https://github.com/flodiebold/chalk.git", branch = "fuel" }
2626
chalk-rust-ir = { git = "https://github.com/flodiebold/chalk.git", branch = "fuel" }
2727
chalk-ir = { git = "https://github.com/flodiebold/chalk.git", branch = "fuel" }
28+
lalrpop-intern = "0.15.1"
2829

2930
[dev-dependencies]
3031
flexi_logger = "0.11.0"

crates/ra_hir/src/code_model.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,18 @@ impl Trait {
779779
self.trait_data(db).items().to_vec()
780780
}
781781

782+
pub fn associated_type_by_name(self, db: &impl DefDatabase, name: Name) -> Option<TypeAlias> {
783+
let trait_data = self.trait_data(db);
784+
trait_data
785+
.items()
786+
.iter()
787+
.filter_map(|item| match item {
788+
TraitItem::TypeAlias(t) => Some(*t),
789+
_ => None,
790+
})
791+
.find(|t| t.name(db) == name)
792+
}
793+
782794
pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc<TraitData> {
783795
db.trait_data(self)
784796
}
@@ -831,8 +843,12 @@ impl TypeAlias {
831843
}
832844
}
833845

834-
pub fn type_ref(self, db: &impl DefDatabase) -> Arc<TypeRef> {
835-
db.type_alias_ref(self)
846+
pub fn type_ref(self, db: &impl DefDatabase) -> Option<TypeRef> {
847+
db.type_alias_data(self).type_ref.clone()
848+
}
849+
850+
pub fn name(self, db: &impl DefDatabase) -> Name {
851+
db.type_alias_data(self).name.clone()
836852
}
837853

838854
/// Builds a resolver for the type references in this type alias.

crates/ra_hir/src/db.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ use crate::{
1616
adt::{StructData, EnumData},
1717
impl_block::{ModuleImplBlocks, ImplSourceMap, ImplBlock},
1818
generics::{GenericParams, GenericDef},
19-
type_ref::TypeRef,
2019
traits::TraitData,
21-
lang_item::{LangItems, LangItemTarget},
20+
lang_item::{LangItems, LangItemTarget}, type_alias::TypeAliasData,
2221
};
2322

2423
// This database has access to source code, so queries here are not really
@@ -113,8 +112,8 @@ pub trait DefDatabase: SourceDatabase {
113112
#[salsa::invoke(crate::FnSignature::fn_signature_query)]
114113
fn fn_signature(&self, func: Function) -> Arc<FnSignature>;
115114

116-
#[salsa::invoke(crate::type_alias::type_alias_ref_query)]
117-
fn type_alias_ref(&self, typ: TypeAlias) -> Arc<TypeRef>;
115+
#[salsa::invoke(crate::type_alias::type_alias_data_query)]
116+
fn type_alias_data(&self, typ: TypeAlias) -> Arc<TypeAliasData>;
118117

119118
#[salsa::invoke(crate::ConstSignature::const_signature_query)]
120119
fn const_signature(&self, konst: Const) -> Arc<ConstSignature>;
@@ -185,6 +184,13 @@ pub trait HirDatabase: DefDatabase + AstDatabase {
185184
krate: Crate,
186185
goal: crate::ty::Canonical<crate::ty::TraitRef>,
187186
) -> Option<crate::ty::traits::Solution>;
187+
188+
#[salsa::invoke(crate::ty::traits::normalize_query)]
189+
fn normalize(
190+
&self,
191+
krate: Crate,
192+
goal: crate::ty::Canonical<crate::ty::ProjectionPredicate>,
193+
) -> Option<crate::ty::traits::Solution>;
188194
}
189195

190196
#[test]

crates/ra_hir/src/lang_item.rs

+39-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::sync::Arc;
22
use rustc_hash::FxHashMap;
33

4-
use ra_syntax::{SmolStr, ast::AttrsOwner};
4+
use ra_syntax::{SmolStr, TreeArc, 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,
8+
Static, Struct, Trait, ModuleDef, AstDatabase, HasSource
89
};
910

1011
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -87,23 +88,51 @@ impl LangItems {
8788
let source = module.definition_source(db).ast;
8889
for (impl_id, _) in impl_blocks.impls.iter() {
8990
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 {
91+
if let Some(lang_item_name) = lang_item_name(&*impl_block) {
9792
let imp = ImplBlock::from_id(*module, impl_id);
9893
self.items.entry(lang_item_name).or_insert_with(|| LangItemTarget::ImplBlock(imp));
9994
}
10095
}
10196

102-
// FIXME we should look for the other lang item targets (traits, structs, ...)
97+
for def in module.declarations(db) {
98+
match def {
99+
ModuleDef::Trait(trait_) => {
100+
self.collect_lang_item(db, trait_, LangItemTarget::Trait)
101+
}
102+
ModuleDef::Enum(e) => self.collect_lang_item(db, e, LangItemTarget::Enum),
103+
ModuleDef::Struct(s) => self.collect_lang_item(db, s, LangItemTarget::Struct),
104+
ModuleDef::Function(f) => self.collect_lang_item(db, f, LangItemTarget::Function),
105+
ModuleDef::Static(s) => self.collect_lang_item(db, s, LangItemTarget::Static),
106+
_ => {}
107+
}
108+
}
103109

104110
// Look for lang items in the children
105111
for child in module.children(db) {
106112
self.collect_lang_items_recursive(db, &child);
107113
}
108114
}
115+
116+
fn collect_lang_item<T, N>(
117+
&mut self,
118+
db: &(impl DefDatabase + AstDatabase),
119+
item: T,
120+
constructor: fn(T) -> LangItemTarget,
121+
) where
122+
T: Copy + HasSource<Ast = TreeArc<N>>,
123+
N: AttrsOwner,
124+
{
125+
let node = item.source(db).ast;
126+
if let Some(lang_item_name) = lang_item_name(&*node) {
127+
self.items.entry(lang_item_name).or_insert(constructor(item));
128+
}
129+
}
130+
}
131+
132+
fn lang_item_name<T: AttrsOwner>(node: &T) -> Option<SmolStr> {
133+
node.attrs()
134+
.filter_map(|a| a.as_key_value())
135+
.filter(|(key, _)| key == "lang")
136+
.map(|(_, val)| val)
137+
.nth(0)
109138
}

crates/ra_hir/src/name.rs

+5
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

+11
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

+25-3
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ use std::sync::Arc;
1616
use std::ops::Deref;
1717
use std::{fmt, mem};
1818

19-
use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait, GenericParams};
19+
use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait, GenericParams, TypeAlias};
2020
use display::{HirDisplay, HirFormatter};
2121

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;
26+
pub(crate) use traits::ProjectionPredicate;
2527

2628
/// A type constructor or type name: this might be something like the primitive
2729
/// type `bool`, a struct like `Vec`, or things like function pointers or
@@ -100,6 +102,15 @@ pub struct ApplicationTy {
100102
pub parameters: Substs,
101103
}
102104

105+
/// A "projection" type corresponds to an (unnormalized)
106+
/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
107+
/// trait and all its parameters are fully known.
108+
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
109+
pub struct ProjectionTy {
110+
pub associated_ty: TypeAlias,
111+
pub parameters: Substs,
112+
}
113+
103114
/// A type.
104115
///
105116
/// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents
@@ -216,8 +227,8 @@ impl Deref for Substs {
216227
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
217228
pub struct TraitRef {
218229
/// FIXME name?
219-
trait_: Trait,
220-
substs: Substs,
230+
pub trait_: Trait,
231+
pub substs: Substs,
221232
}
222233

223234
impl TraitRef {
@@ -464,6 +475,17 @@ impl Ty {
464475
_ => None,
465476
}
466477
}
478+
479+
/// Shifts up `Ty::Bound` vars by `n`.
480+
pub fn shift_bound_vars(self, n: i32) -> Ty {
481+
self.fold(&mut |ty| match ty {
482+
Ty::Bound(idx) => {
483+
assert!(idx as i32 >= -n);
484+
Ty::Bound((idx as i32 + n) as u32)
485+
}
486+
ty => ty,
487+
})
488+
}
467489
}
468490

469491
impl HirDisplay for &Ty {

crates/ra_hir/src/ty/autoderef.rs

+80-9
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,88 @@
55
66
use std::iter::successors;
77

8-
use crate::HirDatabase;
9-
use super::Ty;
8+
use log::{info, warn};
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, HasGenericParams};
11+
use super::{traits::Solution, Ty, Canonical};
12+
13+
const AUTODEREF_RECURSION_LIMIT: usize = 10;
14+
15+
pub(crate) fn autoderef<'a>(
16+
db: &'a impl HirDatabase,
17+
resolver: &'a Resolver,
18+
ty: Canonical<Ty>,
19+
) -> impl Iterator<Item = Canonical<Ty>> + 'a {
20+
successors(Some(ty), move |ty| deref(db, resolver, ty)).take(AUTODEREF_RECURSION_LIMIT)
21+
}
22+
23+
pub(crate) fn deref(
24+
db: &impl HirDatabase,
25+
resolver: &Resolver,
26+
ty: &Canonical<Ty>,
27+
) -> Option<Canonical<Ty>> {
28+
if let Some(derefed) = ty.value.builtin_deref() {
29+
Some(Canonical { value: derefed, num_vars: ty.num_vars })
30+
} else {
31+
deref_by_trait(db, resolver, ty)
32+
}
33+
}
34+
35+
fn deref_by_trait(
36+
db: &impl HirDatabase,
37+
resolver: &Resolver,
38+
ty: &Canonical<Ty>,
39+
) -> Option<Canonical<Ty>> {
40+
let krate = resolver.krate()?;
41+
let deref_trait = match db.lang_item(krate, "deref".into())? {
42+
crate::lang_item::LangItemTarget::Trait(t) => t,
43+
_ => return None,
44+
};
45+
let target = deref_trait.associated_type_by_name(db, Name::target())?;
46+
47+
if target.generic_params(db).count_params_including_parent() != 1 {
48+
// the Target type + Deref trait should only have one generic parameter,
49+
// namely Deref's Self type
50+
return None;
1551
}
1652

17-
fn autoderef_step(&self, _db: &impl HirDatabase) -> Option<Ty> {
18-
// FIXME Deref::deref
19-
self.builtin_deref()
53+
// FIXME make the Canonical handling nicer
54+
55+
let projection = super::traits::ProjectionPredicate {
56+
ty: Ty::Bound(0),
57+
projection_ty: super::ProjectionTy {
58+
associated_ty: target,
59+
parameters: vec![ty.value.clone().shift_bound_vars(1)].into(),
60+
},
61+
};
62+
63+
let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: projection };
64+
65+
let solution = db.normalize(krate, canonical)?;
66+
67+
match &solution {
68+
Solution::Unique(vars) => {
69+
// FIXME: vars may contain solutions for any inference variables
70+
// that happened to be inside ty. To correctly handle these, we
71+
// would have to pass the solution up to the inference context, but
72+
// that requires a larger refactoring (especially if the deref
73+
// happens during method resolution). So for the moment, we just
74+
// check that we're not in the situation we're we would actually
75+
// need to handle the values of the additional variables, i.e.
76+
// they're just being 'passed through'. In the 'standard' case where
77+
// we have `impl<T> Deref for Foo<T> { Target = T }`, that should be
78+
// the case.
79+
for i in 1..vars.0.num_vars {
80+
if vars.0.value[i] != Ty::Bound((i - 1) as u32) {
81+
warn!("complex solution for derefing {:?}: {:?}, ignoring", ty, solution);
82+
return None;
83+
}
84+
}
85+
Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars })
86+
}
87+
Solution::Ambig(_) => {
88+
info!("Ambiguous solution for derefing {:?}: {:?}", ty, solution);
89+
None
90+
}
2091
}
2192
}

0 commit comments

Comments
 (0)