From 0e8c4503bf4754f4437d8bd45a110b9d0ec671e0 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 1 Apr 2021 21:45:44 +0200 Subject: [PATCH] Don't recheck obligations if we have learned nothing new 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). --- crates/hir_ty/src/infer.rs | 18 +++++++++++++++--- crates/hir_ty/src/infer/expr.rs | 6 +++--- crates/hir_ty/src/infer/path.rs | 2 +- crates/hir_ty/src/infer/unify.rs | 8 +++++++- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index e4407ff5051..497a1beb7dc 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -210,6 +210,7 @@ struct InferenceContext<'a> { table: unify::InferenceTable, trait_env: Arc, obligations: Vec, + last_obligations_check: Option, result: InferenceResult, /// The return type of the function being inferred, or the closure if we're /// currently within one. @@ -245,6 +246,7 @@ impl<'a> InferenceContext<'a> { 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 @@ impl<'a> InferenceContext<'a> { } 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 @@ impl<'a> InferenceContext<'a> { } } + 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 @@ impl<'a> InferenceContext<'a> { }), 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 @@ impl<'a> InferenceContext<'a> { 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 } diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 6279aa572c5..25ab3ea4c1a 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -99,7 +99,7 @@ impl<'a> InferenceContext<'a> { 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 @@ impl<'a> InferenceContext<'a> { 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 @@ impl<'a> InferenceContext<'a> { // 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), ); diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index cefa3850948..717738789b9 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs @@ -258,7 +258,7 @@ impl<'a> InferenceContext<'a> { .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(), diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 6e7b0f5a633..5ea4b7481ec 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -231,6 +231,7 @@ pub(crate) struct TypeVariableData { pub(crate) struct InferenceTable { pub(super) var_unification_table: InPlaceUnificationTable, pub(super) type_variable_table: TypeVariableTable, + pub(super) revision: u32, } impl InferenceTable { @@ -238,6 +239,7 @@ impl InferenceTable { InferenceTable { var_unification_table: InPlaceUnificationTable::new(), type_variable_table: TypeVariableTable { inner: Vec::new() }, + revision: 0, } } @@ -360,7 +362,10 @@ impl InferenceTable { == 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 @@ impl InferenceTable { tv.to_inner(), TypeVarValue::Known(other.clone().intern(&Interner)), ); + self.revision += 1; true }