Unify normalize and implements to simplify code

This commit is contained in:
Florian Diebold 2019-07-08 21:43:52 +02:00
parent 15862fc041
commit 9afbf2dff4
9 changed files with 67 additions and 107 deletions

View file

@ -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::InEnvironment<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::InEnvironment<crate::ty::ProjectionPredicate>>,
) -> Option<crate::ty::traits::Solution>; ) -> Option<crate::ty::traits::Solution>;
} }

View file

@ -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::{Environment, InEnvironment, 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

View file

@ -62,11 +62,13 @@ fn deref_by_trait(
}, },
}; };
let in_env = super::traits::InEnvironment { value: projection, environment: env }; let obligation = super::Obligation::Projection(projection);
let in_env = super::traits::InEnvironment { value: obligation, environment: env };
let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: in_env }; let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: in_env };
let solution = db.normalize(krate, canonical)?; let solution = db.solve(krate, canonical)?;
match &solution { match &solution {
Solution::Unique(vars) => { Solution::Unique(vars) => {

View file

@ -331,53 +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 in_env = InEnvironment::new(self.trait_env.clone(), tr.clone()); let solution =
let canonicalized = self.canonicalizer().canonicalize_trait_ref(in_env); self.db.solve(self.resolver.krate().unwrap(), canonicalized.value.clone());
let solution = self
.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 in_env = InEnvironment::new(self.trait_env.clone(), pr.clone());
let canonicalized = self.canonicalizer().canonicalize_projection(in_env);
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
}
};
} }
}; };
} }

View file

@ -1,6 +1,6 @@
//! 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::{ use crate::ty::{
Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty, Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty,
@ -110,32 +110,24 @@ where
// FIXME: add some point, we need to introduce a `Fold` trait that abstracts // 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) // over all the things that can be canonicalized (like Chalk and rustc have)
pub fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> { 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( pub(crate) fn canonicalize_obligation(
mut self, mut self,
trait_ref_in_env: InEnvironment<TraitRef>, obligation: InEnvironment<Obligation>,
) -> Canonicalized<InEnvironment<TraitRef>> { ) -> Canonicalized<InEnvironment<Obligation>> {
let result = self.do_canonicalize_trait_ref(trait_ref_in_env.value); let result = match obligation.value {
// FIXME canonicalize env 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 { self.into_canonicalized(InEnvironment {
value: result, value: result,
environment: trait_ref_in_env.environment, environment: obligation.environment,
})
}
pub fn canonicalize_projection(
mut self,
projection: InEnvironment<ProjectionPredicate>,
) -> Canonicalized<InEnvironment<ProjectionPredicate>> {
let result = self.do_canonicalize_projection_predicate(projection.value);
// FIXME canonicalize env
self.into_canonicalized(InEnvironment {
value: result,
environment: projection.environment,
}) })
} }
} }

View file

@ -211,8 +211,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, env.clone(), 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,12 +279,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>, env: Arc<Environment>,
trait_: Trait, trait_: Trait,
self_ty: Canonical<Ty>, self_ty: Canonical<Ty>,
) -> Canonical<InEnvironment<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;
@ -297,8 +297,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: InEnvironment::new(env, TraitRef { trait_, substs: substs.into() }), let obligation = super::Obligation::Trait(trait_ref);
} Canonical { num_vars, value: InEnvironment::new(env, obligation) }
} }

View file

@ -93,7 +93,7 @@ impl<T> InEnvironment<T> {
/// 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`).
@ -116,11 +116,11 @@ 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<InEnvironment<TraitRef>>, trait_ref: Canonical<InEnvironment<Obligation>>,
) -> Option<Solution> { ) -> Option<Solution> {
let _p = profile("implements_query"); let _p = profile("implements_query");
let canonical = trait_ref.to_chalk(db).cast(); let canonical = trait_ref.to_chalk(db).cast();
@ -131,19 +131,6 @@ pub(crate) fn implements_query(
solution.map(|solution| solution_from_chalk(db, solution)) solution.map(|solution| solution_from_chalk(db, solution))
} }
pub(crate) fn normalize_query(
db: &impl HirDatabase,
krate: Crate,
projection: Canonical<InEnvironment<ProjectionPredicate>>,
) -> Option<Solution> {
let canonical = projection.to_chalk(db).cast();
// 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))
}
fn solution_from_chalk(db: &impl HirDatabase, solution: chalk_solve::Solution) -> Solution { fn solution_from_chalk(db: &impl HirDatabase, solution: chalk_solve::Solution) -> Solution {
let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution>| { let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution>| {
let value = subst let value = subst

View file

@ -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::{Canonical, ChalkContext}; use super::{Canonical, ChalkContext, Obligation};
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
generics::GenericDef, generics::GenericDef,
@ -233,6 +233,21 @@ impl ToChalk for super::ProjectionPredicate {
} }
} }
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> impl<T> ToChalk for Canonical<T>
where where
T: ToChalk, T: ToChalk,

View file

@ -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