Rework obligation handling

We can't do the easy hack that we did before anymore, where we kept
track of whether any inference variables changed since the last time we
rechecked obligations. Instead, we store the obligations in
canonicalized form; that way we can easily check the inference variables
to see whether they have changed since the goal was canonicalized.
This commit is contained in:
Florian Diebold 2021-05-16 15:50:28 +02:00
parent a3d9cac690
commit 1250ddc5cf
11 changed files with 240 additions and 143 deletions

View file

@ -1712,15 +1712,17 @@ impl Type {
resolver: &Resolver, resolver: &Resolver,
ty: Ty, ty: Ty,
) -> Type { ) -> Type {
let environment = let environment = resolver
resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d)); .generic_def()
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
Type { krate, env: environment, ty } Type { krate, env: environment, ty }
} }
fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
let resolver = lexical_env.resolver(db.upcast()); let resolver = lexical_env.resolver(db.upcast());
let environment = let environment = resolver
resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d)); .generic_def()
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
Type { krate, env: environment, ty } Type { krate, env: environment, ty }
} }

View file

@ -134,14 +134,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_solve( fn trait_solve(
&self, &self,
krate: CrateId, krate: CrateId,
goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>; ) -> Option<crate::Solution>;
#[salsa::invoke(crate::traits::trait_solve_query)] #[salsa::invoke(crate::traits::trait_solve_query)]
fn trait_solve_query( fn trait_solve_query(
&self, &self,
krate: CrateId, krate: CrateId,
goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>; ) -> Option<crate::Solution>;
#[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)]
@ -168,7 +168,7 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
fn trait_solve_wait( fn trait_solve_wait(
db: &dyn HirDatabase, db: &dyn HirDatabase,
krate: CrateId, krate: CrateId,
goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution> { ) -> Option<crate::Solution> {
let _p = profile::span("trait_solve::wait"); let _p = profile::span("trait_solve::wait");
db.trait_solve_query(krate, goal) db.trait_solve_query(krate, goal)

View file

@ -14,7 +14,7 @@
//! the `ena` crate, which is extracted from rustc. //! the `ena` crate, which is extracted from rustc.
use std::borrow::Cow; use std::borrow::Cow;
use std::mem;
use std::ops::Index; use std::ops::Index;
use std::sync::Arc; use std::sync::Arc;
@ -27,8 +27,8 @@ use hir_def::{
path::{path, Path}, path::{path, Path},
resolver::{HasResolver, Resolver, TypeNs}, resolver::{HasResolver, Resolver, TypeNs},
type_ref::TypeRef, type_ref::TypeRef,
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, Lookup, TraitId, AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
TypeAliasId, VariantId, TraitId, TypeAliasId, VariantId,
}; };
use hir_expand::{diagnostics::DiagnosticSink, name::name}; use hir_expand::{diagnostics::DiagnosticSink, name::name};
use la_arena::ArenaMap; use la_arena::ArenaMap;
@ -36,13 +36,11 @@ use rustc_hash::FxHashMap;
use stdx::impl_from; use stdx::impl_from;
use syntax::SmolStr; use syntax::SmolStr;
use super::{ use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
DomainGoal, Guidance, InEnvironment, ProjectionTy, Solution, TraitEnvironment, TraitRef, Ty,
};
use crate::{ use crate::{
db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Canonical, Interner, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Interner, TyBuilder, TyExt,
TyBuilder, TyExt, TyKind, TyKind,
}; };
// This lint has a false positive here. See the link below for details. // This lint has a false positive here. See the link below for details.
@ -227,8 +225,6 @@ struct InferenceContext<'a> {
resolver: Resolver, resolver: Resolver,
table: unify::InferenceTable<'a>, table: unify::InferenceTable<'a>,
trait_env: Arc<TraitEnvironment>, trait_env: Arc<TraitEnvironment>,
obligations: Vec<DomainGoal>,
last_obligations_check: Option<u32>,
result: InferenceResult, result: InferenceResult,
/// The return type of the function being inferred, or the closure if we're /// The return type of the function being inferred, or the closure if we're
/// currently within one. /// currently within one.
@ -260,15 +256,15 @@ fn find_breakable<'c>(
impl<'a> InferenceContext<'a> { impl<'a> InferenceContext<'a> {
fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Self { fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Self {
let trait_env = let krate = owner.module(db.upcast()).krate();
owner.as_generic_def_id().map_or_else(Default::default, |d| db.trait_environment(d)); let trait_env = owner
.as_generic_def_id()
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
InferenceContext { InferenceContext {
result: InferenceResult::default(), result: InferenceResult::default(),
table: unify::InferenceTable::new(db, trait_env.clone()), table: unify::InferenceTable::new(db, trait_env.clone()),
obligations: Vec::default(),
last_obligations_check: None,
return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature
trait_env, trait_env,
return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature
db, db,
owner, owner,
body: db.body(owner), body: db.body(owner),
@ -284,6 +280,7 @@ impl<'a> InferenceContext<'a> {
fn resolve_all(mut self) -> InferenceResult { fn resolve_all(mut self) -> InferenceResult {
// FIXME resolve obligations as well (use Guidance if necessary) // FIXME resolve obligations as well (use Guidance if necessary)
self.table.resolve_obligations_as_possible();
// make sure diverging type variables are marked as such // make sure diverging type variables are marked as such
self.table.propagate_diverging_flag(); self.table.propagate_diverging_flag();
@ -357,44 +354,11 @@ impl<'a> InferenceContext<'a> {
} }
fn resolve_obligations_as_possible(&mut self) { fn resolve_obligations_as_possible(&mut self) {
let _span = profile::span("resolve_obligations_as_possible"); self.table.resolve_obligations_as_possible();
let obligations = mem::replace(&mut self.obligations, Vec::new());
for obligation in obligations {
let in_env = InEnvironment::new(&self.trait_env.env, obligation.clone());
let canonicalized = self.canonicalize(in_env);
let solution =
self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone());
match solution {
Some(Solution::Unique(canonical_subst)) => {
canonicalized.apply_solution(
self,
Canonical {
binders: canonical_subst.binders,
// FIXME: handle constraints
value: canonical_subst.value.subst,
},
);
}
Some(Solution::Ambig(Guidance::Definite(substs))) => {
canonicalized.apply_solution(self, substs);
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
}
};
}
} }
fn push_obligation(&mut self, o: DomainGoal) { fn push_obligation(&mut self, o: DomainGoal) {
self.obligations.push(o); self.table.register_obligation(o.cast(&Interner));
self.last_obligations_check = None;
} }
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
@ -467,25 +431,7 @@ impl<'a> InferenceContext<'a> {
/// call). `make_ty` handles this already, but e.g. for field types we need /// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well. /// to do it as well.
fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
let ty = self.resolve_ty_as_possible(ty); self.table.normalize_associated_types_in(ty)
fold_tys(
ty,
|ty, _| match ty.kind(&Interner) {
TyKind::Alias(AliasTy::Projection(proj_ty)) => {
self.normalize_projection_ty(proj_ty.clone())
}
_ => ty,
},
DebruijnIndex::INNERMOST,
)
}
fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
let var = self.table.new_type_var();
let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
let obligation = alias_eq.cast(&Interner);
self.push_obligation(obligation);
var
} }
fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantId>) { fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantId>) {

View file

@ -402,12 +402,15 @@ impl<'a> InferenceContext<'a> {
// solve `CoerceUnsized` and `Unsize` goals at this point and leaves the // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the
// rest for later. Also, there's some logic about sized type variables. // rest for later. Also, there's some logic about sized type variables.
// Need to find out in what cases this is necessary // Need to find out in what cases this is necessary
let solution = self.db.trait_solve(krate, canonicalized.value.clone()).ok_or(TypeError)?; let solution = self
.db
.trait_solve(krate, canonicalized.value.clone().cast(&Interner))
.ok_or(TypeError)?;
match solution { match solution {
Solution::Unique(v) => { Solution::Unique(v) => {
canonicalized.apply_solution( canonicalized.apply_solution(
self, &mut self.table,
Canonical { Canonical {
binders: v.binders, binders: v.binders,
// FIXME handle constraints // FIXME handle constraints

View file

@ -99,9 +99,9 @@ impl<'a> InferenceContext<'a> {
environment: trait_env, environment: trait_env,
}; };
let canonical = self.canonicalize(obligation.clone()); let canonical = self.canonicalize(obligation.clone());
if self.db.trait_solve(krate, canonical.value).is_some() { if self.db.trait_solve(krate, canonical.value.cast(&Interner)).is_some() {
self.push_obligation(obligation.goal); self.push_obligation(obligation.goal);
let return_ty = self.normalize_projection_ty(projection); let return_ty = self.table.normalize_projection_ty(projection);
Some((arg_tys, return_ty)) Some((arg_tys, return_ty))
} else { } else {
None None
@ -306,7 +306,7 @@ impl<'a> InferenceContext<'a> {
self.resolver.krate(), self.resolver.krate(),
InEnvironment { InEnvironment {
goal: canonicalized.value.clone(), goal: canonicalized.value.clone(),
environment: self.trait_env.env.clone(), environment: self.table.trait_env.env.clone(),
}, },
); );
let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs

View file

@ -225,7 +225,7 @@ impl<'a> InferenceContext<'a> {
method_resolution::iterate_method_candidates( method_resolution::iterate_method_candidates(
&canonical_ty.value, &canonical_ty.value,
self.db, self.db,
self.trait_env.clone(), self.table.trait_env.clone(),
krate, krate,
&traits_in_scope, &traits_in_scope,
None, None,

View file

@ -1,6 +1,6 @@
//! Unification and canonicalization logic. //! Unification and canonicalization logic.
use std::{borrow::Cow, fmt, sync::Arc}; use std::{borrow::Cow, fmt, mem, sync::Arc};
use chalk_ir::{ use chalk_ir::{
cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, TyVariableKind, cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, TyVariableKind,
@ -11,8 +11,9 @@ use ena::unify::UnifyKey;
use super::{InferOk, InferResult, InferenceContext, TypeError}; use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{ use crate::{
db::HirDatabase, fold_tys, static_lifetime, BoundVar, Canonical, DebruijnIndex, GenericArg, db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical,
InferenceVar, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyKind, VariableKind, DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, ProjectionTy,
Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind,
}; };
impl<'a> InferenceContext<'a> { impl<'a> InferenceContext<'a> {
@ -23,17 +24,11 @@ impl<'a> InferenceContext<'a> {
where where
T::Result: HasInterner<Interner = Interner>, T::Result: HasInterner<Interner = Interner>,
{ {
let result = self.table.var_unification_table.canonicalize(&Interner, t); self.table.canonicalize(t)
let free_vars = result
.free_vars
.into_iter()
.map(|free_var| free_var.to_generic_arg(&Interner))
.collect();
Canonicalized { value: result.quantified, free_vars }
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub(super) struct Canonicalized<T> pub(super) struct Canonicalized<T>
where where
T: HasInterner<Interner = Interner>, T: HasInterner<Interner = Interner>,
@ -49,22 +44,16 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
pub(super) fn apply_solution( pub(super) fn apply_solution(
&self, &self,
ctx: &mut InferenceContext<'_>, ctx: &mut InferenceTable,
solution: Canonical<Substitution>, solution: Canonical<Substitution>,
) { ) {
// the solution may contain new variables, which we need to convert to new inference vars // the solution may contain new variables, which we need to convert to new inference vars
let new_vars = Substitution::from_iter( let new_vars = Substitution::from_iter(
&Interner, &Interner,
solution.binders.iter(&Interner).map(|k| match k.kind { solution.binders.iter(&Interner).map(|k| match k.kind {
VariableKind::Ty(TyVariableKind::General) => { VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(&Interner),
ctx.table.new_type_var().cast(&Interner) VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(&Interner),
} VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(&Interner),
VariableKind::Ty(TyVariableKind::Integer) => {
ctx.table.new_integer_var().cast(&Interner)
}
VariableKind::Ty(TyVariableKind::Float) => {
ctx.table.new_float_var().cast(&Interner)
}
// Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere
VariableKind::Lifetime => static_lifetime().cast(&Interner), VariableKind::Lifetime => static_lifetime().cast(&Interner),
_ => panic!("const variable in solution"), _ => panic!("const variable in solution"),
@ -76,9 +65,9 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
// eagerly replace projections in the type; we may be getting types // eagerly replace projections in the type; we may be getting types
// e.g. from where clauses where this hasn't happened yet // e.g. from where clauses where this hasn't happened yet
let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), &Interner)); let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), &Interner));
ctx.table.unify(var.assert_ty_ref(&Interner), &ty); ctx.unify(var.assert_ty_ref(&Interner), &ty);
} else { } else {
let _ = ctx.table.unify_inner(&var, &new_vars.apply(v.clone(), &Interner)); let _ = ctx.unify_inner(&var, &new_vars.apply(v.clone(), &Interner));
} }
} }
} }
@ -167,10 +156,11 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct InferenceTable<'a> { pub(crate) struct InferenceTable<'a> {
db: &'a dyn HirDatabase, pub db: &'a dyn HirDatabase,
trait_env: Arc<TraitEnvironment>, pub trait_env: Arc<TraitEnvironment>,
pub(super) var_unification_table: ChalkInferenceTable, pub(super) var_unification_table: ChalkInferenceTable,
pub(super) type_variable_table: TypeVariableTable, pub(super) type_variable_table: TypeVariableTable,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
} }
impl<'a> InferenceTable<'a> { impl<'a> InferenceTable<'a> {
@ -180,6 +170,7 @@ impl<'a> InferenceTable<'a> {
trait_env, trait_env,
var_unification_table: ChalkInferenceTable::new(), var_unification_table: ChalkInferenceTable::new(),
type_variable_table: TypeVariableTable { inner: Vec::new() }, type_variable_table: TypeVariableTable { inner: Vec::new() },
pending_obligations: Vec::new(),
} }
} }
@ -202,6 +193,50 @@ impl<'a> InferenceTable<'a> {
} }
} }
pub(super) fn canonicalize<T: Fold<Interner> + HasInterner<Interner = Interner>>(
&mut self,
t: T,
) -> Canonicalized<T::Result>
where
T::Result: HasInterner<Interner = Interner>,
{
let result = self.var_unification_table.canonicalize(&Interner, t);
let free_vars = result
.free_vars
.into_iter()
.map(|free_var| free_var.to_generic_arg(&Interner))
.collect();
Canonicalized { value: result.quantified, free_vars }
}
/// Recurses through the given type, normalizing associated types mentioned
/// in it by replacing them by type variables and registering obligations to
/// resolve later. This should be done once for every type we get from some
/// type annotation (e.g. from a let type annotation, field type or function
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
pub(super) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
let ty = self.resolve_ty_as_possible(ty);
fold_tys(
ty,
|ty, _| match ty.kind(&Interner) {
TyKind::Alias(AliasTy::Projection(proj_ty)) => {
self.normalize_projection_ty(proj_ty.clone())
}
_ => ty,
},
DebruijnIndex::INNERMOST,
)
}
pub(super) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
let var = self.new_type_var();
let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
let obligation = alias_eq.cast(&Interner);
self.register_obligation(obligation);
var
}
fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty {
let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); let var = self.var_unification_table.new_variable(UniverseIndex::ROOT);
// Chalk might have created some type variables for its own purposes that we don't know about... // Chalk might have created some type variables for its own purposes that we don't know about...
@ -341,6 +376,94 @@ impl<'a> InferenceTable<'a> {
DebruijnIndex::INNERMOST, DebruijnIndex::INNERMOST,
) )
} }
pub fn register_obligation(&mut self, goal: Goal) {
let in_env = InEnvironment::new(&self.trait_env.env, goal);
self.register_obligation_in_env(in_env)
}
fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) {
let canonicalized = self.canonicalize(goal);
if !self.try_resolve_obligation(&canonicalized) {
self.pending_obligations.push(canonicalized);
}
}
pub fn resolve_obligations_as_possible(&mut self) {
let _span = profile::span("resolve_obligations_as_possible");
let mut changed = true;
let mut obligations = Vec::new();
while changed {
changed = false;
mem::swap(&mut self.pending_obligations, &mut obligations);
for canonicalized in obligations.drain(..) {
if !self.check_changed(&canonicalized) {
self.pending_obligations.push(canonicalized);
continue;
}
changed = true;
let uncanonical = chalk_ir::Substitute::apply(
&canonicalized.free_vars,
canonicalized.value.value,
&Interner,
);
self.register_obligation_in_env(uncanonical);
}
}
}
/// This checks whether any of the free variables in the `canonicalized`
/// have changed (either been unified with another variable, or with a
/// value). If this is not the case, we don't need to try to solve the goal
/// again -- it'll give the same result as last time.
fn check_changed(&mut self, canonicalized: &Canonicalized<InEnvironment<Goal>>) -> bool {
canonicalized.free_vars.iter().any(|var| {
let iv = match var.data(&Interner) {
chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(&Interner),
chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(&Interner),
chalk_ir::GenericArgData::Const(c) => c.inference_var(&Interner),
}
.expect("free var is not inference var");
if self.var_unification_table.probe_var(iv).is_some() {
return true;
}
let root = self.var_unification_table.inference_var_root(iv);
iv != root
})
}
fn try_resolve_obligation(
&mut self,
canonicalized: &Canonicalized<InEnvironment<Goal>>,
) -> bool {
let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value.clone());
match solution {
Some(Solution::Unique(canonical_subst)) => {
canonicalized.apply_solution(
self,
Canonical {
binders: canonical_subst.binders,
// FIXME: handle constraints
value: canonical_subst.value.subst,
},
);
true
}
Some(Solution::Ambig(Guidance::Definite(substs))) => {
canonicalized.apply_solution(self, substs);
false
}
Some(_) => {
// FIXME use this when trying to resolve everything at the end
false
}
None => {
// FIXME obligation cannot be fulfilled => diagnostic
true
}
}
}
} }
impl<'a> fmt::Debug for InferenceTable<'a> { impl<'a> fmt::Debug for InferenceTable<'a> {

View file

@ -45,7 +45,7 @@ use hir_def::{
}; };
use stdx::always; use stdx::always;
use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; use crate::{db::HirDatabase, utils::generics};
pub use autoderef::autoderef; pub use autoderef::autoderef;
pub use builder::TyBuilder; pub use builder::TyBuilder;
@ -114,6 +114,7 @@ pub type FnSig = chalk_ir::FnSig<Interner>;
pub type InEnvironment<T> = chalk_ir::InEnvironment<T>; pub type InEnvironment<T> = chalk_ir::InEnvironment<T>;
pub type DomainGoal = chalk_ir::DomainGoal<Interner>; pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
pub type Goal = chalk_ir::Goal<Interner>;
pub type AliasEq = chalk_ir::AliasEq<Interner>; pub type AliasEq = chalk_ir::AliasEq<Interner>;
pub type Solution = chalk_solve::Solution<Interner>; pub type Solution = chalk_solve::Solution<Interner>;
pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>; pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;

View file

@ -1035,9 +1035,11 @@ pub(crate) fn trait_environment_query(
clauses.push(program_clause.into_from_env_clause(&Interner)); clauses.push(program_clause.into_from_env_clause(&Interner));
} }
let krate = def.module(db.upcast()).krate();
let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses); let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses);
Arc::new(TraitEnvironment { traits_from_clauses: traits_in_scope, env }) Arc::new(TraitEnvironment { krate, traits_from_clauses: traits_in_scope, env })
} }
/// Resolve the where clause(s) of an item with generics. /// Resolve the where clause(s) of an item with generics.

View file

@ -577,6 +577,7 @@ fn iterate_method_candidates_by_receiver(
if iterate_inherent_methods( if iterate_inherent_methods(
self_ty, self_ty,
db, db,
env.clone(),
name, name,
Some(receiver_ty), Some(receiver_ty),
krate, krate,
@ -613,8 +614,16 @@ fn iterate_method_candidates_for_self_ty(
name: Option<&Name>, name: Option<&Name>,
mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
) -> bool { ) -> bool {
if iterate_inherent_methods(self_ty, db, name, None, krate, visible_from_module, &mut callback) if iterate_inherent_methods(
{ self_ty,
db,
env.clone(),
name,
None,
krate,
visible_from_module,
&mut callback,
) {
return true; return true;
} }
iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback) iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback)
@ -653,12 +662,12 @@ fn iterate_trait_method_candidates(
for (_name, item) in data.items.iter() { for (_name, item) in data.items.iter() {
// Don't pass a `visible_from_module` down to `is_valid_candidate`, // Don't pass a `visible_from_module` down to `is_valid_candidate`,
// since only inherent methods should be included into visibility checking. // since only inherent methods should be included into visibility checking.
if !is_valid_candidate(db, name, receiver_ty, *item, self_ty, None) { if !is_valid_candidate(db, env.clone(), name, receiver_ty, *item, self_ty, None) {
continue; continue;
} }
if !known_implemented { if !known_implemented {
let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone()); let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone());
if db.trait_solve(krate, goal).is_none() { if db.trait_solve(krate, goal.cast(&Interner)).is_none() {
continue 'traits; continue 'traits;
} }
} }
@ -675,6 +684,7 @@ fn iterate_trait_method_candidates(
fn iterate_inherent_methods( fn iterate_inherent_methods(
self_ty: &Canonical<Ty>, self_ty: &Canonical<Ty>,
db: &dyn HirDatabase, db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
name: Option<&Name>, name: Option<&Name>,
receiver_ty: Option<&Canonical<Ty>>, receiver_ty: Option<&Canonical<Ty>>,
krate: CrateId, krate: CrateId,
@ -690,14 +700,24 @@ fn iterate_inherent_methods(
for &impl_def in impls.for_self_ty(&self_ty.value) { for &impl_def in impls.for_self_ty(&self_ty.value) {
for &item in db.impl_data(impl_def).items.iter() { for &item in db.impl_data(impl_def).items.iter() {
if !is_valid_candidate(db, name, receiver_ty, item, self_ty, visible_from_module) { if !is_valid_candidate(
db,
env.clone(),
name,
receiver_ty,
item,
self_ty,
visible_from_module,
) {
continue; continue;
} }
// we have to check whether the self type unifies with the type // we have to check whether the self type unifies with the type
// that the impl is for. If we have a receiver type, this // that the impl is for. If we have a receiver type, this
// already happens in `is_valid_candidate` above; if not, we // already happens in `is_valid_candidate` above; if not, we
// check it here // check it here
if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { if receiver_ty.is_none()
&& inherent_impl_substs(db, env.clone(), impl_def, self_ty).is_none()
{
cov_mark::hit!(impl_self_type_match_without_receiver); cov_mark::hit!(impl_self_type_match_without_receiver);
continue; continue;
} }
@ -722,7 +742,7 @@ pub fn resolve_indexing_op(
let deref_chain = autoderef_method_receiver(db, krate, ty); let deref_chain = autoderef_method_receiver(db, krate, ty);
for ty in deref_chain { for ty in deref_chain {
let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone()); let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone());
if db.trait_solve(krate, goal).is_some() { if db.trait_solve(krate, goal.cast(&Interner)).is_some() {
return Some(ty); return Some(ty);
} }
} }
@ -731,6 +751,7 @@ pub fn resolve_indexing_op(
fn is_valid_candidate( fn is_valid_candidate(
db: &dyn HirDatabase, db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
name: Option<&Name>, name: Option<&Name>,
receiver_ty: Option<&Canonical<Ty>>, receiver_ty: Option<&Canonical<Ty>>,
item: AssocItemId, item: AssocItemId,
@ -749,7 +770,7 @@ fn is_valid_candidate(
if !data.has_self_param() { if !data.has_self_param() {
return false; return false;
} }
let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) { let transformed_receiver_ty = match transform_receiver_ty(db, env, m, self_ty) {
Some(ty) => ty, Some(ty) => ty,
None => return false, None => return false,
}; };
@ -776,6 +797,7 @@ fn is_valid_candidate(
pub(crate) fn inherent_impl_substs( pub(crate) fn inherent_impl_substs(
db: &dyn HirDatabase, db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
impl_id: ImplId, impl_id: ImplId,
self_ty: &Canonical<Ty>, self_ty: &Canonical<Ty>,
) -> Option<Substitution> { ) -> Option<Substitution> {
@ -798,8 +820,7 @@ pub(crate) fn inherent_impl_substs(
binders: CanonicalVarKinds::from_iter(&Interner, kinds), binders: CanonicalVarKinds::from_iter(&Interner, kinds),
value: (self_ty_with_vars, self_ty.value.clone()), value: (self_ty_with_vars, self_ty.value.clone()),
}; };
let trait_env = Arc::new(TraitEnvironment::default()); // FIXME let substs = super::infer::unify(db, env, &tys)?;
let substs = super::infer::unify(db, trait_env, &tys)?;
// We only want the substs for the vars we added, not the ones from self_ty. // We only want the substs for the vars we added, not the ones from self_ty.
// Also, if any of the vars we added are still in there, we replace them by // Also, if any of the vars we added are still in there, we replace them by
// Unknown. I think this can only really happen if self_ty contained // Unknown. I think this can only really happen if self_ty contained
@ -824,6 +845,7 @@ fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution
fn transform_receiver_ty( fn transform_receiver_ty(
db: &dyn HirDatabase, db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
function_id: FunctionId, function_id: FunctionId,
self_ty: &Canonical<Ty>, self_ty: &Canonical<Ty>,
) -> Option<Ty> { ) -> Option<Ty> {
@ -833,7 +855,7 @@ fn transform_receiver_ty(
.fill_with_unknown() .fill_with_unknown()
.build(), .build(),
AssocContainerId::ImplId(impl_id) => { AssocContainerId::ImplId(impl_id) => {
let impl_substs = inherent_impl_substs(db, impl_id, &self_ty)?; let impl_substs = inherent_impl_substs(db, env, impl_id, &self_ty)?;
TyBuilder::subst_for_def(db, function_id) TyBuilder::subst_for_def(db, function_id)
.use_parent_substs(&impl_substs) .use_parent_substs(&impl_substs)
.fill_with_unknown() .fill_with_unknown()
@ -853,7 +875,7 @@ pub fn implements_trait(
trait_: TraitId, trait_: TraitId,
) -> bool { ) -> bool {
let goal = generic_implements_goal(db, env, trait_, ty.clone()); let goal = generic_implements_goal(db, env, trait_, ty.clone());
let solution = db.trait_solve(krate, goal); let solution = db.trait_solve(krate, goal.cast(&Interner));
solution.is_some() solution.is_some()
} }
@ -866,7 +888,7 @@ pub fn implements_trait_unique(
trait_: TraitId, trait_: TraitId,
) -> bool { ) -> bool {
let goal = generic_implements_goal(db, env, trait_, ty.clone()); let goal = generic_implements_goal(db, env, trait_, ty.clone());
let solution = db.trait_solve(krate, goal); let solution = db.trait_solve(krate, goal.cast(&Interner));
matches!(solution, Some(crate::Solution::Unique(_))) matches!(solution, Some(crate::Solution::Unique(_)))
} }

View file

@ -2,7 +2,7 @@
use std::env::var; use std::env::var;
use chalk_ir::cast::Cast; use chalk_ir::GoalData;
use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
use base_db::CrateId; use base_db::CrateId;
@ -10,7 +10,7 @@ use hir_def::{lang_item::LangItemTarget, TraitId};
use stdx::panic_context; use stdx::panic_context;
use crate::{ use crate::{
db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Guidance, HirDisplay, InEnvironment, db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment,
Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause, Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause,
}; };
@ -38,6 +38,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
/// we assume that `T: Default`. /// we assume that `T: Default`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TraitEnvironment { pub struct TraitEnvironment {
pub krate: CrateId,
// When we're using Chalk's Ty we can make this a BTreeMap since it's Ord, // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord,
// but for now it's too annoying... // but for now it's too annoying...
pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>,
@ -45,6 +46,14 @@ pub struct TraitEnvironment {
} }
impl TraitEnvironment { impl TraitEnvironment {
pub fn empty(krate: CrateId) -> Self {
TraitEnvironment {
krate,
traits_from_clauses: Vec::new(),
env: chalk_ir::Environment::new(&Interner),
}
}
pub(crate) fn traits_in_scope_from_clauses<'a>( pub(crate) fn traits_in_scope_from_clauses<'a>(
&'a self, &'a self,
ty: &'a Ty, ty: &'a Ty,
@ -59,34 +68,25 @@ impl TraitEnvironment {
} }
} }
impl Default for TraitEnvironment {
fn default() -> Self {
TraitEnvironment {
traits_from_clauses: Vec::new(),
env: chalk_ir::Environment::new(&Interner),
}
}
}
/// Solve a trait goal using Chalk. /// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query( pub(crate) fn trait_solve_query(
db: &dyn HirDatabase, db: &dyn HirDatabase,
krate: CrateId, krate: CrateId,
goal: Canonical<InEnvironment<DomainGoal>>, goal: Canonical<InEnvironment<Goal>>,
) -> Option<Solution> { ) -> Option<Solution> {
let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal { let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(&Interner) {
DomainGoal::Holds(WhereClause::Implemented(it)) => { GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => {
db.trait_data(it.hir_trait_id()).name.to_string() db.trait_data(it.hir_trait_id()).name.to_string()
} }
DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(), GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(),
_ => "??".to_string(), _ => "??".to_string(),
}); });
log::info!("trait_solve_query({})", goal.value.goal.display(db)); log::info!("trait_solve_query({:?})", goal.value.goal);
if let DomainGoal::Holds(WhereClause::AliasEq(AliasEq { if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(projection_ty), alias: AliasTy::Projection(projection_ty),
.. ..
})) = &goal.value.goal }))) = &goal.value.goal.data(&Interner)
{ {
if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(&Interner).kind(&Interner) { if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(&Interner).kind(&Interner) {
// Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
@ -94,11 +94,9 @@ pub(crate) fn trait_solve_query(
} }
} }
let canonical = goal.cast(&Interner);
// 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: goal, universes: 1 };
solve(db, krate, &u_canonical) solve(db, krate, &u_canonical)
} }