8285: Don't recheck obligations if we have learned nothing new r=matklad a=flodiebold

This is just the most trivial check: If no inference variables have been updated, and there are no new obligations, we can just skip trying to solve them again. We could be smarter about it, but this already helps quite a bit, and I don't want to touch this too much before we replace the inference table by Chalk's.

Fixes #8263 (well, improves it quite a bit).

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2021-04-02 11:25:40 +00:00 committed by GitHub
commit 00ce7ae524
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 26 additions and 8 deletions

View File

@ -210,6 +210,7 @@ struct InferenceContext<'a> {
table: unify::InferenceTable,
trait_env: Arc<TraitEnvironment>,
obligations: Vec<DomainGoal>,
last_obligations_check: Option<u32>,
result: InferenceResult,
/// The return type of the function being inferred, or the closure if we're
/// currently within one.
@ -245,6 +246,7 @@ fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Sel
result: InferenceResult::default(),
table: unify::InferenceTable::new(),
obligations: Vec::default(),
last_obligations_check: None,
return_ty: TyKind::Unknown.intern(&Interner), // set in collect_fn_signature
trait_env: owner
.as_generic_def_id()
@ -334,6 +336,11 @@ fn insert_type_vars(&mut self, ty: Ty) -> Ty {
}
fn resolve_obligations_as_possible(&mut self) {
if self.last_obligations_check == Some(self.table.revision) {
// no change
return;
}
self.last_obligations_check = Some(self.table.revision);
let obligations = mem::replace(&mut self.obligations, Vec::new());
for obligation in obligations {
let in_env = InEnvironment::new(self.trait_env.env.clone(), obligation.clone());
@ -360,6 +367,11 @@ fn resolve_obligations_as_possible(&mut self) {
}
}
fn push_obligation(&mut self, o: DomainGoal) {
self.obligations.push(o);
self.last_obligations_check = None;
}
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
self.table.unify(ty1, ty2)
}
@ -408,8 +420,8 @@ fn resolve_associated_type_with_params(
}),
ty: ty.clone(),
};
self.obligations.push(trait_ref.cast(&Interner));
self.obligations.push(alias_eq.cast(&Interner));
self.push_obligation(trait_ref.cast(&Interner));
self.push_obligation(alias_eq.cast(&Interner));
self.resolve_ty_as_possible(ty)
}
None => self.err_ty(),
@ -436,7 +448,7 @@ 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.obligations.push(obligation);
self.push_obligation(obligation);
var
}

View File

@ -99,7 +99,7 @@ fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Ve
environment: trait_env,
});
if self.db.trait_solve(krate, goal.value).is_some() {
self.obligations.push(implements_fn_trait);
self.push_obligation(implements_fn_trait);
let output_proj_ty = crate::ProjectionTy {
associated_ty_id: to_assoc_type_id(output_assoc_type),
substitution: substs,
@ -964,7 +964,7 @@ fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
let (predicate, binders) =
predicate.clone().subst(parameters).into_value_and_skipped_binders();
always!(binders == 0); // quantified where clauses not yet handled
self.obligations.push(predicate.cast(&Interner));
self.push_obligation(predicate.cast(&Interner));
}
// add obligation for trait implementation, if this is a trait method
match def {
@ -974,7 +974,7 @@ fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
// construct a TraitRef
let substs =
parameters.prefix(generics(self.db.upcast(), trait_.into()).len());
self.obligations.push(
self.push_obligation(
TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs }
.cast(&Interner),
);

View File

@ -258,7 +258,7 @@ fn resolve_ty_assoc_item(
.push(ty.clone())
.fill(std::iter::repeat_with(|| self.table.new_type_var()))
.build();
self.obligations.push(
self.push_obligation(
TraitRef {
trait_id: to_chalk_trait_id(trait_),
substitution: trait_substs.clone(),

View File

@ -231,6 +231,7 @@ pub(crate) struct TypeVariableData {
pub(crate) struct InferenceTable {
pub(super) var_unification_table: InPlaceUnificationTable<TypeVarId>,
pub(super) type_variable_table: TypeVariableTable,
pub(super) revision: u32,
}
impl InferenceTable {
@ -238,6 +239,7 @@ pub(crate) fn new() -> Self {
InferenceTable {
var_unification_table: InPlaceUnificationTable::new(),
type_variable_table: TypeVariableTable { inner: Vec::new() },
revision: 0,
}
}
@ -360,7 +362,10 @@ pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -
== self.type_variable_table.is_diverging(*tv2) =>
{
// both type vars are unknown since we tried to resolve them
self.var_unification_table.union(tv1.to_inner(), tv2.to_inner());
if !self.var_unification_table.unioned(tv1.to_inner(), tv2.to_inner()) {
self.var_unification_table.union(tv1.to_inner(), tv2.to_inner());
self.revision += 1;
}
true
}
@ -398,6 +403,7 @@ pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -
tv.to_inner(),
TypeVarValue::Known(other.clone().intern(&Interner)),
);
self.revision += 1;
true
}