Skip to content

Commit f59cd1a

Browse files
bors[bot]flodiebold
andcommitted
Merge #1515
1515: Trait environment r=matklad a=flodiebold This adds the environment, i.e. the set of `where` clauses in scope, when solving trait goals. That means that e.g. in ```rust fn foo<T: SomeTrait>(t: T) {} ``` , we are able to complete methods of `SomeTrait` on the `t`. This affects the trait APIs quite a bit (since every method that needs to be able to solve for some trait needs to get this environment somehow), so I thought I'd do it rather sooner than later ;) Co-authored-by: Florian Diebold <flodiebold@gmail.com>
2 parents 35f28c5 + 9afbf2d commit f59cd1a

File tree

11 files changed

+286
-119
lines changed

11 files changed

+286
-119
lines changed

crates/ra_hir/src/db.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -213,18 +213,11 @@ pub trait HirDatabase: DefDatabase + AstDatabase {
213213
#[salsa::invoke(crate::ty::traits::chalk::impl_datum_query)]
214214
fn impl_datum(&self, krate: Crate, impl_id: chalk_ir::ImplId) -> Arc<chalk_rust_ir::ImplDatum>;
215215

216-
#[salsa::invoke(crate::ty::traits::implements_query)]
217-
fn implements(
216+
#[salsa::invoke(crate::ty::traits::solve_query)]
217+
fn solve(
218218
&self,
219219
krate: Crate,
220-
goal: crate::ty::Canonical<crate::ty::TraitRef>,
221-
) -> Option<crate::ty::traits::Solution>;
222-
223-
#[salsa::invoke(crate::ty::traits::normalize_query)]
224-
fn normalize(
225-
&self,
226-
krate: Crate,
227-
goal: crate::ty::Canonical<crate::ty::ProjectionPredicate>,
220+
goal: crate::ty::Canonical<crate::ty::InEnvironment<crate::ty::Obligation>>,
228221
) -> Option<crate::ty::traits::Solution>;
229222
}
230223

crates/ra_hir/src/ty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub(crate) use lower::{
2626
callable_item_sig, generic_defaults_query, generic_predicates_query, type_for_def,
2727
type_for_field, TypableDef,
2828
};
29-
pub(crate) use traits::ProjectionPredicate;
29+
pub(crate) use traits::{Environment, InEnvironment, Obligation, ProjectionPredicate};
3030

3131
/// A type constructor or type name: this might be something like the primitive
3232
/// type `bool`, a struct like `Vec`, or things like function pointers or

crates/ra_hir/src/ty/autoderef.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ fn deref_by_trait(
5252

5353
// FIXME make the Canonical handling nicer
5454

55+
let env = super::lower::trait_env(db, resolver);
56+
5557
let projection = super::traits::ProjectionPredicate {
5658
ty: Ty::Bound(0),
5759
projection_ty: super::ProjectionTy {
@@ -60,9 +62,13 @@ fn deref_by_trait(
6062
},
6163
};
6264

63-
let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: projection };
65+
let obligation = super::Obligation::Projection(projection);
66+
67+
let in_env = super::traits::InEnvironment { value: obligation, environment: env };
68+
69+
let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: in_env };
6470

65-
let solution = db.normalize(krate, canonical)?;
71+
let solution = db.solve(krate, canonical)?;
6672

6773
match &solution {
6874
Solution::Unique(vars) => {

crates/ra_hir/src/ty/infer.rs

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ use ra_prof::profile;
2727
use test_utils::tested_by;
2828

2929
use super::{
30-
autoderef, method_resolution, op, primitive,
30+
autoderef, lower, method_resolution, op, primitive,
3131
traits::{Guidance, Obligation, ProjectionPredicate, Solution},
32-
ApplicationTy, CallableDef, ProjectionTy, Substs, TraitRef, Ty, TypableDef, TypeCtor,
32+
ApplicationTy, CallableDef, Environment, InEnvironment, ProjectionTy, Substs, TraitRef, Ty,
33+
TypableDef, TypeCtor,
3334
};
3435
use crate::{
3536
adt::VariantDef,
@@ -165,6 +166,7 @@ struct InferenceContext<'a, D: HirDatabase> {
165166
body: Arc<Body>,
166167
resolver: Resolver,
167168
var_unification_table: InPlaceUnificationTable<TypeVarId>,
169+
trait_env: Arc<Environment>,
168170
obligations: Vec<Obligation>,
169171
method_resolutions: FxHashMap<ExprId, Function>,
170172
field_resolutions: FxHashMap<ExprId, StructField>,
@@ -188,6 +190,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
188190
var_unification_table: InPlaceUnificationTable::new(),
189191
obligations: Vec::default(),
190192
return_ty: Ty::Unknown, // set in collect_fn_signature
193+
trait_env: lower::trait_env(db, &resolver),
191194
db,
192195
body,
193196
resolver,
@@ -328,51 +331,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
328331
fn resolve_obligations_as_possible(&mut self) {
329332
let obligations = mem::replace(&mut self.obligations, Vec::new());
330333
for obligation in obligations {
331-
match &obligation {
332-
Obligation::Trait(tr) => {
333-
let canonicalized = self.canonicalizer().canonicalize_trait_ref(tr.clone());
334-
let solution = self
335-
.db
336-
.implements(self.resolver.krate().unwrap(), canonicalized.value.clone());
337-
match solution {
338-
Some(Solution::Unique(substs)) => {
339-
canonicalized.apply_solution(self, substs.0);
340-
}
341-
Some(Solution::Ambig(Guidance::Definite(substs))) => {
342-
canonicalized.apply_solution(self, substs.0);
343-
self.obligations.push(obligation);
344-
}
345-
Some(_) => {
346-
// FIXME use this when trying to resolve everything at the end
347-
self.obligations.push(obligation);
348-
}
349-
None => {
350-
// FIXME obligation cannot be fulfilled => diagnostic
351-
}
352-
};
334+
let in_env = InEnvironment::new(self.trait_env.clone(), obligation.clone());
335+
let canonicalized = self.canonicalizer().canonicalize_obligation(in_env);
336+
let solution =
337+
self.db.solve(self.resolver.krate().unwrap(), canonicalized.value.clone());
338+
339+
match solution {
340+
Some(Solution::Unique(substs)) => {
341+
canonicalized.apply_solution(self, substs.0);
353342
}
354-
Obligation::Projection(pr) => {
355-
let canonicalized = self.canonicalizer().canonicalize_projection(pr.clone());
356-
let solution = self
357-
.db
358-
.normalize(self.resolver.krate().unwrap(), canonicalized.value.clone());
359-
360-
match solution {
361-
Some(Solution::Unique(substs)) => {
362-
canonicalized.apply_solution(self, substs.0);
363-
}
364-
Some(Solution::Ambig(Guidance::Definite(substs))) => {
365-
canonicalized.apply_solution(self, substs.0);
366-
self.obligations.push(obligation);
367-
}
368-
Some(_) => {
369-
// FIXME use this when trying to resolve everything at the end
370-
self.obligations.push(obligation);
371-
}
372-
None => {
373-
// FIXME obligation cannot be fulfilled => diagnostic
374-
}
375-
};
343+
Some(Solution::Ambig(Guidance::Definite(substs))) => {
344+
canonicalized.apply_solution(self, substs.0);
345+
self.obligations.push(obligation);
346+
}
347+
Some(_) => {
348+
// FIXME use this when trying to resolve everything at the end
349+
self.obligations.push(obligation);
350+
}
351+
None => {
352+
// FIXME obligation cannot be fulfilled => diagnostic
376353
}
377354
};
378355
}

crates/ra_hir/src/ty/infer/unify.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
//! Unification and canonicalization logic.
22
3-
use super::InferenceContext;
3+
use super::{InferenceContext, Obligation};
44
use crate::db::HirDatabase;
5-
use crate::ty::{Canonical, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty};
5+
use crate::ty::{
6+
Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty,
7+
};
68

79
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
810
pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D>
@@ -105,22 +107,28 @@ where
105107
ProjectionPredicate { ty, projection_ty }
106108
}
107109

108-
pub fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
109-
let result = self.do_canonicalize_ty(ty);
110-
self.into_canonicalized(result)
111-
}
110+
// FIXME: add some point, we need to introduce a `Fold` trait that abstracts
111+
// over all the things that can be canonicalized (like Chalk and rustc have)
112112

113-
pub fn canonicalize_trait_ref(mut self, trait_ref: TraitRef) -> Canonicalized<TraitRef> {
114-
let result = self.do_canonicalize_trait_ref(trait_ref);
113+
pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
114+
let result = self.do_canonicalize_ty(ty);
115115
self.into_canonicalized(result)
116116
}
117117

118-
pub fn canonicalize_projection(
118+
pub(crate) fn canonicalize_obligation(
119119
mut self,
120-
projection: ProjectionPredicate,
121-
) -> Canonicalized<ProjectionPredicate> {
122-
let result = self.do_canonicalize_projection_predicate(projection);
123-
self.into_canonicalized(result)
120+
obligation: InEnvironment<Obligation>,
121+
) -> Canonicalized<InEnvironment<Obligation>> {
122+
let result = match obligation.value {
123+
Obligation::Trait(tr) => Obligation::Trait(self.do_canonicalize_trait_ref(tr)),
124+
Obligation::Projection(pr) => {
125+
Obligation::Projection(self.do_canonicalize_projection_predicate(pr))
126+
}
127+
};
128+
self.into_canonicalized(InEnvironment {
129+
value: result,
130+
environment: obligation.environment,
131+
})
124132
}
125133
}
126134

crates/ra_hir/src/ty/lower.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,18 @@ pub(crate) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty {
317317
Ty::from_hir(db, &resolver, type_ref)
318318
}
319319

320+
pub(crate) fn trait_env(db: &impl HirDatabase, resolver: &Resolver) -> Arc<super::Environment> {
321+
let predicates = resolver
322+
.where_predicates_in_scope()
323+
.map(|pred| {
324+
TraitRef::for_where_predicate(db, &resolver, pred)
325+
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
326+
})
327+
.collect::<Vec<_>>();
328+
329+
Arc::new(super::Environment { predicates })
330+
}
331+
320332
/// Resolve the where clause(s) of an item with generics.
321333
pub(crate) fn generic_predicates_query(
322334
db: &impl HirDatabase,

crates/ra_hir/src/ty/method_resolution.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::sync::Arc;
77
use arrayvec::ArrayVec;
88
use rustc_hash::FxHashMap;
99

10-
use super::{autoderef, Canonical, TraitRef};
10+
use super::{autoderef, lower, Canonical, Environment, InEnvironment, TraitRef};
1111
use crate::{
1212
generics::HasGenericParams,
1313
impl_block::{ImplBlock, ImplId, ImplItem},
@@ -200,6 +200,8 @@ fn iterate_trait_method_candidates<T>(
200200
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
201201
) -> Option<T> {
202202
let krate = resolver.krate()?;
203+
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
204+
let env = lower::trait_env(db, resolver);
203205
'traits: for t in resolver.traits_in_scope(db) {
204206
let data = t.trait_data(db);
205207
// we'll be lazy about checking whether the type implements the
@@ -211,8 +213,8 @@ fn iterate_trait_method_candidates<T>(
211213
let data = m.data(db);
212214
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
213215
if !known_implemented {
214-
let trait_ref = canonical_trait_ref(db, t, ty.clone());
215-
if db.implements(krate, trait_ref).is_none() {
216+
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
217+
if db.solve(krate, goal).is_none() {
216218
continue 'traits;
217219
}
218220
}
@@ -279,11 +281,12 @@ impl Ty {
279281

280282
/// This creates Substs for a trait with the given Self type and type variables
281283
/// for all other parameters, to query Chalk with it.
282-
fn canonical_trait_ref(
284+
fn generic_implements_goal(
283285
db: &impl HirDatabase,
286+
env: Arc<Environment>,
284287
trait_: Trait,
285288
self_ty: Canonical<Ty>,
286-
) -> Canonical<TraitRef> {
289+
) -> Canonical<InEnvironment<super::Obligation>> {
287290
let mut substs = Vec::new();
288291
let generics = trait_.generic_params(db);
289292
let num_vars = self_ty.num_vars;
@@ -296,8 +299,8 @@ fn canonical_trait_ref(
296299
.enumerate()
297300
.map(|(i, _p)| Ty::Bound((i + num_vars) as u32)),
298301
);
299-
Canonical {
300-
num_vars: substs.len() - 1 + self_ty.num_vars,
301-
value: TraitRef { trait_, substs: substs.into() },
302-
}
302+
let num_vars = substs.len() - 1 + self_ty.num_vars;
303+
let trait_ref = TraitRef { trait_, substs: substs.into() };
304+
let obligation = super::Obligation::Trait(trait_ref);
305+
Canonical { num_vars, value: InEnvironment::new(env, obligation) }
303306
}

crates/ra_hir/src/ty/tests.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3003,6 +3003,85 @@ fn test(o: O<S>) {
30033003
assert_eq!(t, "&str");
30043004
}
30053005

3006+
#[test]
3007+
fn generic_param_env_1() {
3008+
let t = type_at(
3009+
r#"
3010+
//- /main.rs
3011+
trait Clone {}
3012+
trait Trait { fn foo(self) -> u128; }
3013+
struct S;
3014+
impl Clone for S {}
3015+
impl<T> Trait for T where T: Clone {}
3016+
fn test<T: Clone>(t: T) { t.foo()<|>; }
3017+
"#,
3018+
);
3019+
assert_eq!(t, "u128");
3020+
}
3021+
3022+
#[test]
3023+
fn generic_param_env_1_not_met() {
3024+
let t = type_at(
3025+
r#"
3026+
//- /main.rs
3027+
trait Clone {}
3028+
trait Trait { fn foo(self) -> u128; }
3029+
struct S;
3030+
impl Clone for S {}
3031+
impl<T> Trait for T where T: Clone {}
3032+
fn test<T>(t: T) { t.foo()<|>; }
3033+
"#,
3034+
);
3035+
assert_eq!(t, "{unknown}");
3036+
}
3037+
3038+
#[test]
3039+
fn generic_param_env_2() {
3040+
let t = type_at(
3041+
r#"
3042+
//- /main.rs
3043+
trait Trait { fn foo(self) -> u128; }
3044+
struct S;
3045+
impl Trait for S {}
3046+
fn test<T: Trait>(t: T) { t.foo()<|>; }
3047+
"#,
3048+
);
3049+
assert_eq!(t, "u128");
3050+
}
3051+
3052+
#[test]
3053+
fn generic_param_env_2_not_met() {
3054+
let t = type_at(
3055+
r#"
3056+
//- /main.rs
3057+
trait Trait { fn foo(self) -> u128; }
3058+
struct S;
3059+
impl Trait for S {}
3060+
fn test<T>(t: T) { t.foo()<|>; }
3061+
"#,
3062+
);
3063+
assert_eq!(t, "{unknown}");
3064+
}
3065+
3066+
#[test]
3067+
fn generic_param_env_deref() {
3068+
let t = type_at(
3069+
r#"
3070+
//- /main.rs
3071+
#[lang = "deref"]
3072+
trait Deref {
3073+
type Target;
3074+
}
3075+
trait Trait {}
3076+
impl<T> Deref for T where T: Trait {
3077+
type Target = i128;
3078+
}
3079+
fn test<T: Trait>(t: T) { (*t)<|>; }
3080+
"#,
3081+
);
3082+
assert_eq!(t, "i128");
3083+
}
3084+
30063085
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
30073086
let file = db.parse(pos.file_id).ok().unwrap();
30083087
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();

0 commit comments

Comments
 (0)