mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
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>
This commit is contained in:
commit
f59cd1a4a0
11 changed files with 286 additions and 119 deletions
|
@ -213,18 +213,11 @@ pub trait HirDatabase: DefDatabase + AstDatabase {
|
||||||
#[salsa::invoke(crate::ty::traits::chalk::impl_datum_query)]
|
#[salsa::invoke(crate::ty::traits::chalk::impl_datum_query)]
|
||||||
fn impl_datum(&self, krate: Crate, impl_id: chalk_ir::ImplId) -> Arc<chalk_rust_ir::ImplDatum>;
|
fn impl_datum(&self, krate: Crate, impl_id: chalk_ir::ImplId) -> Arc<chalk_rust_ir::ImplDatum>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::ty::traits::implements_query)]
|
#[salsa::invoke(crate::ty::traits::solve_query)]
|
||||||
fn implements(
|
fn solve(
|
||||||
&self,
|
&self,
|
||||||
krate: Crate,
|
krate: Crate,
|
||||||
goal: crate::ty::Canonical<crate::ty::TraitRef>,
|
goal: crate::ty::Canonical<crate::ty::InEnvironment<crate::ty::Obligation>>,
|
||||||
) -> Option<crate::ty::traits::Solution>;
|
|
||||||
|
|
||||||
#[salsa::invoke(crate::ty::traits::normalize_query)]
|
|
||||||
fn normalize(
|
|
||||||
&self,
|
|
||||||
krate: Crate,
|
|
||||||
goal: crate::ty::Canonical<crate::ty::ProjectionPredicate>,
|
|
||||||
) -> Option<crate::ty::traits::Solution>;
|
) -> Option<crate::ty::traits::Solution>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub(crate) use lower::{
|
||||||
callable_item_sig, generic_defaults_query, generic_predicates_query, type_for_def,
|
callable_item_sig, generic_defaults_query, generic_predicates_query, type_for_def,
|
||||||
type_for_field, TypableDef,
|
type_for_field, TypableDef,
|
||||||
};
|
};
|
||||||
pub(crate) use traits::ProjectionPredicate;
|
pub(crate) use traits::{Environment, InEnvironment, Obligation, ProjectionPredicate};
|
||||||
|
|
||||||
/// A type constructor or type name: this might be something like the primitive
|
/// A type constructor or type name: this might be something like the primitive
|
||||||
/// type `bool`, a struct like `Vec`, or things like function pointers or
|
/// type `bool`, a struct like `Vec`, or things like function pointers or
|
||||||
|
|
|
@ -52,6 +52,8 @@ fn deref_by_trait(
|
||||||
|
|
||||||
// FIXME make the Canonical handling nicer
|
// FIXME make the Canonical handling nicer
|
||||||
|
|
||||||
|
let env = super::lower::trait_env(db, resolver);
|
||||||
|
|
||||||
let projection = super::traits::ProjectionPredicate {
|
let projection = super::traits::ProjectionPredicate {
|
||||||
ty: Ty::Bound(0),
|
ty: Ty::Bound(0),
|
||||||
projection_ty: super::ProjectionTy {
|
projection_ty: super::ProjectionTy {
|
||||||
|
@ -60,9 +62,13 @@ fn deref_by_trait(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: projection };
|
let obligation = super::Obligation::Projection(projection);
|
||||||
|
|
||||||
let solution = db.normalize(krate, canonical)?;
|
let in_env = super::traits::InEnvironment { value: obligation, environment: env };
|
||||||
|
|
||||||
|
let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: in_env };
|
||||||
|
|
||||||
|
let solution = db.solve(krate, canonical)?;
|
||||||
|
|
||||||
match &solution {
|
match &solution {
|
||||||
Solution::Unique(vars) => {
|
Solution::Unique(vars) => {
|
||||||
|
|
|
@ -27,9 +27,10 @@ use ra_prof::profile;
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
autoderef, method_resolution, op, primitive,
|
autoderef, lower, method_resolution, op, primitive,
|
||||||
traits::{Guidance, Obligation, ProjectionPredicate, Solution},
|
traits::{Guidance, Obligation, ProjectionPredicate, Solution},
|
||||||
ApplicationTy, CallableDef, ProjectionTy, Substs, TraitRef, Ty, TypableDef, TypeCtor,
|
ApplicationTy, CallableDef, Environment, InEnvironment, ProjectionTy, Substs, TraitRef, Ty,
|
||||||
|
TypableDef, TypeCtor,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
adt::VariantDef,
|
adt::VariantDef,
|
||||||
|
@ -165,6 +166,7 @@ struct InferenceContext<'a, D: HirDatabase> {
|
||||||
body: Arc<Body>,
|
body: Arc<Body>,
|
||||||
resolver: Resolver,
|
resolver: Resolver,
|
||||||
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
||||||
|
trait_env: Arc<Environment>,
|
||||||
obligations: Vec<Obligation>,
|
obligations: Vec<Obligation>,
|
||||||
method_resolutions: FxHashMap<ExprId, Function>,
|
method_resolutions: FxHashMap<ExprId, Function>,
|
||||||
field_resolutions: FxHashMap<ExprId, StructField>,
|
field_resolutions: FxHashMap<ExprId, StructField>,
|
||||||
|
@ -188,6 +190,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
var_unification_table: InPlaceUnificationTable::new(),
|
var_unification_table: InPlaceUnificationTable::new(),
|
||||||
obligations: Vec::default(),
|
obligations: Vec::default(),
|
||||||
return_ty: Ty::Unknown, // set in collect_fn_signature
|
return_ty: Ty::Unknown, // set in collect_fn_signature
|
||||||
|
trait_env: lower::trait_env(db, &resolver),
|
||||||
db,
|
db,
|
||||||
body,
|
body,
|
||||||
resolver,
|
resolver,
|
||||||
|
@ -328,51 +331,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
fn resolve_obligations_as_possible(&mut self) {
|
fn resolve_obligations_as_possible(&mut self) {
|
||||||
let obligations = mem::replace(&mut self.obligations, Vec::new());
|
let obligations = mem::replace(&mut self.obligations, Vec::new());
|
||||||
for obligation in obligations {
|
for obligation in obligations {
|
||||||
match &obligation {
|
let in_env = InEnvironment::new(self.trait_env.clone(), obligation.clone());
|
||||||
Obligation::Trait(tr) => {
|
let canonicalized = self.canonicalizer().canonicalize_obligation(in_env);
|
||||||
let canonicalized = self.canonicalizer().canonicalize_trait_ref(tr.clone());
|
let solution =
|
||||||
let solution = self
|
self.db.solve(self.resolver.krate().unwrap(), canonicalized.value.clone());
|
||||||
.db
|
|
||||||
.implements(self.resolver.krate().unwrap(), canonicalized.value.clone());
|
|
||||||
match solution {
|
|
||||||
Some(Solution::Unique(substs)) => {
|
|
||||||
canonicalized.apply_solution(self, substs.0);
|
|
||||||
}
|
|
||||||
Some(Solution::Ambig(Guidance::Definite(substs))) => {
|
|
||||||
canonicalized.apply_solution(self, substs.0);
|
|
||||||
self.obligations.push(obligation);
|
|
||||||
}
|
|
||||||
Some(_) => {
|
|
||||||
// FIXME use this when trying to resolve everything at the end
|
|
||||||
self.obligations.push(obligation);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// FIXME obligation cannot be fulfilled => diagnostic
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Obligation::Projection(pr) => {
|
|
||||||
let canonicalized = self.canonicalizer().canonicalize_projection(pr.clone());
|
|
||||||
let solution = self
|
|
||||||
.db
|
|
||||||
.normalize(self.resolver.krate().unwrap(), canonicalized.value.clone());
|
|
||||||
|
|
||||||
match solution {
|
match solution {
|
||||||
Some(Solution::Unique(substs)) => {
|
Some(Solution::Unique(substs)) => {
|
||||||
canonicalized.apply_solution(self, substs.0);
|
canonicalized.apply_solution(self, substs.0);
|
||||||
}
|
}
|
||||||
Some(Solution::Ambig(Guidance::Definite(substs))) => {
|
Some(Solution::Ambig(Guidance::Definite(substs))) => {
|
||||||
canonicalized.apply_solution(self, substs.0);
|
canonicalized.apply_solution(self, substs.0);
|
||||||
self.obligations.push(obligation);
|
self.obligations.push(obligation);
|
||||||
}
|
}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
// FIXME use this when trying to resolve everything at the end
|
// FIXME use this when trying to resolve everything at the end
|
||||||
self.obligations.push(obligation);
|
self.obligations.push(obligation);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// FIXME obligation cannot be fulfilled => diagnostic
|
// FIXME obligation cannot be fulfilled => diagnostic
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
//! Unification and canonicalization logic.
|
//! Unification and canonicalization logic.
|
||||||
|
|
||||||
use super::InferenceContext;
|
use super::{InferenceContext, Obligation};
|
||||||
use crate::db::HirDatabase;
|
use crate::db::HirDatabase;
|
||||||
use crate::ty::{Canonical, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty};
|
use crate::ty::{
|
||||||
|
Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty,
|
||||||
|
};
|
||||||
|
|
||||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D>
|
pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D>
|
||||||
|
@ -105,22 +107,28 @@ where
|
||||||
ProjectionPredicate { ty, projection_ty }
|
ProjectionPredicate { ty, projection_ty }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
|
// FIXME: add some point, we need to introduce a `Fold` trait that abstracts
|
||||||
|
// over all the things that can be canonicalized (like Chalk and rustc have)
|
||||||
|
|
||||||
|
pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
|
||||||
let result = self.do_canonicalize_ty(ty);
|
let result = self.do_canonicalize_ty(ty);
|
||||||
self.into_canonicalized(result)
|
self.into_canonicalized(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonicalize_trait_ref(mut self, trait_ref: TraitRef) -> Canonicalized<TraitRef> {
|
pub(crate) fn canonicalize_obligation(
|
||||||
let result = self.do_canonicalize_trait_ref(trait_ref);
|
|
||||||
self.into_canonicalized(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn canonicalize_projection(
|
|
||||||
mut self,
|
mut self,
|
||||||
projection: ProjectionPredicate,
|
obligation: InEnvironment<Obligation>,
|
||||||
) -> Canonicalized<ProjectionPredicate> {
|
) -> Canonicalized<InEnvironment<Obligation>> {
|
||||||
let result = self.do_canonicalize_projection_predicate(projection);
|
let result = match obligation.value {
|
||||||
self.into_canonicalized(result)
|
Obligation::Trait(tr) => Obligation::Trait(self.do_canonicalize_trait_ref(tr)),
|
||||||
|
Obligation::Projection(pr) => {
|
||||||
|
Obligation::Projection(self.do_canonicalize_projection_predicate(pr))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.into_canonicalized(InEnvironment {
|
||||||
|
value: result,
|
||||||
|
environment: obligation.environment,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -317,6 +317,18 @@ pub(crate) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty {
|
||||||
Ty::from_hir(db, &resolver, type_ref)
|
Ty::from_hir(db, &resolver, type_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn trait_env(db: &impl HirDatabase, resolver: &Resolver) -> Arc<super::Environment> {
|
||||||
|
let predicates = resolver
|
||||||
|
.where_predicates_in_scope()
|
||||||
|
.map(|pred| {
|
||||||
|
TraitRef::for_where_predicate(db, &resolver, pred)
|
||||||
|
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Arc::new(super::Environment { predicates })
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve the where clause(s) of an item with generics.
|
/// Resolve the where clause(s) of an item with generics.
|
||||||
pub(crate) fn generic_predicates_query(
|
pub(crate) fn generic_predicates_query(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::sync::Arc;
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use super::{autoderef, Canonical, TraitRef};
|
use super::{autoderef, lower, Canonical, Environment, InEnvironment, TraitRef};
|
||||||
use crate::{
|
use crate::{
|
||||||
generics::HasGenericParams,
|
generics::HasGenericParams,
|
||||||
impl_block::{ImplBlock, ImplId, ImplItem},
|
impl_block::{ImplBlock, ImplId, ImplItem},
|
||||||
|
@ -200,6 +200,8 @@ fn iterate_trait_method_candidates<T>(
|
||||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
let krate = resolver.krate()?;
|
let krate = resolver.krate()?;
|
||||||
|
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
|
||||||
|
let env = lower::trait_env(db, resolver);
|
||||||
'traits: for t in resolver.traits_in_scope(db) {
|
'traits: for t in resolver.traits_in_scope(db) {
|
||||||
let data = t.trait_data(db);
|
let data = t.trait_data(db);
|
||||||
// we'll be lazy about checking whether the type implements the
|
// we'll be lazy about checking whether the type implements the
|
||||||
|
@ -211,8 +213,8 @@ fn iterate_trait_method_candidates<T>(
|
||||||
let data = m.data(db);
|
let data = m.data(db);
|
||||||
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
||||||
if !known_implemented {
|
if !known_implemented {
|
||||||
let trait_ref = canonical_trait_ref(db, t, ty.clone());
|
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
|
||||||
if db.implements(krate, trait_ref).is_none() {
|
if db.solve(krate, goal).is_none() {
|
||||||
continue 'traits;
|
continue 'traits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,11 +281,12 @@ impl Ty {
|
||||||
|
|
||||||
/// This creates Substs for a trait with the given Self type and type variables
|
/// This creates Substs for a trait with the given Self type and type variables
|
||||||
/// for all other parameters, to query Chalk with it.
|
/// for all other parameters, to query Chalk with it.
|
||||||
fn canonical_trait_ref(
|
fn generic_implements_goal(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
|
env: Arc<Environment>,
|
||||||
trait_: Trait,
|
trait_: Trait,
|
||||||
self_ty: Canonical<Ty>,
|
self_ty: Canonical<Ty>,
|
||||||
) -> Canonical<TraitRef> {
|
) -> Canonical<InEnvironment<super::Obligation>> {
|
||||||
let mut substs = Vec::new();
|
let mut substs = Vec::new();
|
||||||
let generics = trait_.generic_params(db);
|
let generics = trait_.generic_params(db);
|
||||||
let num_vars = self_ty.num_vars;
|
let num_vars = self_ty.num_vars;
|
||||||
|
@ -296,8 +299,8 @@ fn canonical_trait_ref(
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _p)| Ty::Bound((i + num_vars) as u32)),
|
.map(|(i, _p)| Ty::Bound((i + num_vars) as u32)),
|
||||||
);
|
);
|
||||||
Canonical {
|
let num_vars = substs.len() - 1 + self_ty.num_vars;
|
||||||
num_vars: substs.len() - 1 + self_ty.num_vars,
|
let trait_ref = TraitRef { trait_, substs: substs.into() };
|
||||||
value: TraitRef { trait_, substs: substs.into() },
|
let obligation = super::Obligation::Trait(trait_ref);
|
||||||
}
|
Canonical { num_vars, value: InEnvironment::new(env, obligation) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -3003,6 +3003,85 @@ fn test(o: O<S>) {
|
||||||
assert_eq!(t, "&str");
|
assert_eq!(t, "&str");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_param_env_1() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Clone {}
|
||||||
|
trait Trait { fn foo(self) -> u128; }
|
||||||
|
struct S;
|
||||||
|
impl Clone for S {}
|
||||||
|
impl<T> Trait for T where T: Clone {}
|
||||||
|
fn test<T: Clone>(t: T) { t.foo()<|>; }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "u128");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_param_env_1_not_met() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Clone {}
|
||||||
|
trait Trait { fn foo(self) -> u128; }
|
||||||
|
struct S;
|
||||||
|
impl Clone for S {}
|
||||||
|
impl<T> Trait for T where T: Clone {}
|
||||||
|
fn test<T>(t: T) { t.foo()<|>; }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "{unknown}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_param_env_2() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Trait { fn foo(self) -> u128; }
|
||||||
|
struct S;
|
||||||
|
impl Trait for S {}
|
||||||
|
fn test<T: Trait>(t: T) { t.foo()<|>; }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "u128");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_param_env_2_not_met() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Trait { fn foo(self) -> u128; }
|
||||||
|
struct S;
|
||||||
|
impl Trait for S {}
|
||||||
|
fn test<T>(t: T) { t.foo()<|>; }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "{unknown}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_param_env_deref() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
#[lang = "deref"]
|
||||||
|
trait Deref {
|
||||||
|
type Target;
|
||||||
|
}
|
||||||
|
trait Trait {}
|
||||||
|
impl<T> Deref for T where T: Trait {
|
||||||
|
type Target = i128;
|
||||||
|
}
|
||||||
|
fn test<T: Trait>(t: T) { (*t)<|>; }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "i128");
|
||||||
|
}
|
||||||
|
|
||||||
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
|
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
|
||||||
let file = db.parse(pos.file_id).ok().unwrap();
|
let file = db.parse(pos.file_id).ok().unwrap();
|
||||||
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
|
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
|
||||||
|
|
|
@ -67,10 +67,33 @@ fn solve(
|
||||||
solution
|
solution
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A set of clauses that we assume to be true. E.g. if we are inside this function:
|
||||||
|
/// ```rust
|
||||||
|
/// fn foo<T: Default>(t: T) {}
|
||||||
|
/// ```
|
||||||
|
/// we assume that `T: Default`.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Environment {
|
||||||
|
pub predicates: Vec<GenericPredicate>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Something (usually a goal), along with an environment.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct InEnvironment<T> {
|
||||||
|
pub environment: Arc<Environment>,
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> InEnvironment<T> {
|
||||||
|
pub fn new(environment: Arc<Environment>, value: T) -> InEnvironment<T> {
|
||||||
|
InEnvironment { environment, value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Something that needs to be proven (by Chalk) during type checking, e.g. that
|
/// Something that needs to be proven (by Chalk) during type checking, e.g. that
|
||||||
/// a certain type implements a certain trait. Proving the Obligation might
|
/// a certain type implements a certain trait. Proving the Obligation might
|
||||||
/// result in additional information about inference variables.
|
/// result in additional information about inference variables.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Obligation {
|
pub enum Obligation {
|
||||||
/// Prove that a certain type implements a trait (the type is the `Self` type
|
/// Prove that a certain type implements a trait (the type is the `Self` type
|
||||||
/// parameter to the `TraitRef`).
|
/// parameter to the `TraitRef`).
|
||||||
|
@ -93,44 +116,14 @@ pub struct ProjectionPredicate {
|
||||||
pub ty: Ty,
|
pub ty: Ty,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check using Chalk whether trait is implemented for given parameters including `Self` type.
|
/// Solve a trait goal using Chalk.
|
||||||
pub(crate) fn implements_query(
|
pub(crate) fn solve_query(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
krate: Crate,
|
krate: Crate,
|
||||||
trait_ref: Canonical<TraitRef>,
|
trait_ref: Canonical<InEnvironment<Obligation>>,
|
||||||
) -> Option<Solution> {
|
) -> Option<Solution> {
|
||||||
let _p = profile("implements_query");
|
let _p = profile("implements_query");
|
||||||
let goal: chalk_ir::Goal = trait_ref.value.to_chalk(db).cast();
|
let canonical = trait_ref.to_chalk(db).cast();
|
||||||
debug!("goal: {:?}", goal);
|
|
||||||
let env = chalk_ir::Environment::new();
|
|
||||||
let in_env = chalk_ir::InEnvironment::new(&env, goal);
|
|
||||||
let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT);
|
|
||||||
let canonical =
|
|
||||||
chalk_ir::Canonical { value: in_env, binders: vec![parameter; trait_ref.num_vars] };
|
|
||||||
// We currently don't deal with universes (I think / hope they're not yet
|
|
||||||
// relevant for our use cases?)
|
|
||||||
let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
|
|
||||||
let solution = solve(db, krate, &u_canonical);
|
|
||||||
solution.map(|solution| solution_from_chalk(db, solution))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn normalize_query(
|
|
||||||
db: &impl HirDatabase,
|
|
||||||
krate: Crate,
|
|
||||||
projection: Canonical<ProjectionPredicate>,
|
|
||||||
) -> Option<Solution> {
|
|
||||||
let goal: chalk_ir::Goal = chalk_ir::Normalize {
|
|
||||||
projection: projection.value.projection_ty.to_chalk(db),
|
|
||||||
ty: projection.value.ty.to_chalk(db),
|
|
||||||
}
|
|
||||||
.cast();
|
|
||||||
debug!("goal: {:?}", goal);
|
|
||||||
// FIXME unify with `implements`
|
|
||||||
let env = chalk_ir::Environment::new();
|
|
||||||
let in_env = chalk_ir::InEnvironment::new(&env, goal);
|
|
||||||
let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT);
|
|
||||||
let canonical =
|
|
||||||
chalk_ir::Canonical { value: in_env, binders: vec![parameter; projection.num_vars] };
|
|
||||||
// We currently don't deal with universes (I think / hope they're not yet
|
// We currently don't deal with universes (I think / hope they're not yet
|
||||||
// relevant for our use cases?)
|
// relevant for our use cases?)
|
||||||
let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
|
let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
|
||||||
|
|
|
@ -12,7 +12,7 @@ use chalk_rust_ir::{AssociatedTyDatum, ImplDatum, StructDatum, TraitDatum};
|
||||||
use ra_db::salsa::{InternId, InternKey};
|
use ra_db::salsa::{InternId, InternKey};
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use super::ChalkContext;
|
use super::{Canonical, ChalkContext, Obligation};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
generics::GenericDef,
|
generics::GenericDef,
|
||||||
|
@ -218,6 +218,103 @@ impl ToChalk for ProjectionTy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToChalk for super::ProjectionPredicate {
|
||||||
|
type Chalk = chalk_ir::Normalize;
|
||||||
|
|
||||||
|
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize {
|
||||||
|
chalk_ir::Normalize {
|
||||||
|
projection: self.projection_ty.to_chalk(db),
|
||||||
|
ty: self.ty.to_chalk(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize) -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToChalk for Obligation {
|
||||||
|
type Chalk = chalk_ir::DomainGoal;
|
||||||
|
|
||||||
|
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal {
|
||||||
|
match self {
|
||||||
|
Obligation::Trait(tr) => tr.to_chalk(db).cast(),
|
||||||
|
Obligation::Projection(pr) => pr.to_chalk(db).cast(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal) -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ToChalk for Canonical<T>
|
||||||
|
where
|
||||||
|
T: ToChalk,
|
||||||
|
{
|
||||||
|
type Chalk = chalk_ir::Canonical<T::Chalk>;
|
||||||
|
|
||||||
|
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
|
||||||
|
let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT);
|
||||||
|
let value = self.value.to_chalk(db);
|
||||||
|
let canonical = chalk_ir::Canonical { value, binders: vec![parameter; self.num_vars] };
|
||||||
|
canonical
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_chalk(db: &impl HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
|
||||||
|
Canonical { num_vars: canonical.binders.len(), value: from_chalk(db, canonical.value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToChalk for Arc<super::Environment> {
|
||||||
|
type Chalk = Arc<chalk_ir::Environment>;
|
||||||
|
|
||||||
|
fn to_chalk(self, db: &impl HirDatabase) -> Arc<chalk_ir::Environment> {
|
||||||
|
let mut clauses = Vec::new();
|
||||||
|
for pred in &self.predicates {
|
||||||
|
if pred.is_error() {
|
||||||
|
// for env, we just ignore errors
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let GenericPredicate::Implemented(trait_ref) = pred {
|
||||||
|
if blacklisted_trait(db, trait_ref.trait_) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clauses.push(pred.clone().to_chalk(db).cast());
|
||||||
|
}
|
||||||
|
chalk_ir::Environment::new().add_clauses(clauses)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_chalk(
|
||||||
|
_db: &impl HirDatabase,
|
||||||
|
_env: Arc<chalk_ir::Environment>,
|
||||||
|
) -> Arc<super::Environment> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToChalk> ToChalk for super::InEnvironment<T> {
|
||||||
|
type Chalk = chalk_ir::InEnvironment<T::Chalk>;
|
||||||
|
|
||||||
|
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> {
|
||||||
|
chalk_ir::InEnvironment {
|
||||||
|
environment: self.environment.to_chalk(db),
|
||||||
|
goal: self.value.to_chalk(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_chalk(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
in_env: chalk_ir::InEnvironment<T::Chalk>,
|
||||||
|
) -> super::InEnvironment<T> {
|
||||||
|
super::InEnvironment {
|
||||||
|
environment: from_chalk(db, in_env.environment),
|
||||||
|
value: from_chalk(db, in_env.goal),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> {
|
fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> {
|
||||||
chalk_ir::Binders {
|
chalk_ir::Binders {
|
||||||
value,
|
value,
|
||||||
|
|
|
@ -295,8 +295,7 @@ impl RootDatabase {
|
||||||
hir::db::TraitDatumQuery
|
hir::db::TraitDatumQuery
|
||||||
hir::db::StructDatumQuery
|
hir::db::StructDatumQuery
|
||||||
hir::db::ImplDatumQuery
|
hir::db::ImplDatumQuery
|
||||||
hir::db::ImplementsQuery
|
hir::db::SolveQuery
|
||||||
hir::db::NormalizeQuery
|
|
||||||
];
|
];
|
||||||
acc.sort_by_key(|it| std::cmp::Reverse(it.1));
|
acc.sort_by_key(|it| std::cmp::Reverse(it.1));
|
||||||
acc
|
acc
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue