Skip to content

Commit 91f2ee7

Browse files
committed
recursive: fix hang on fulfill by slightly smarter check for progress.
1 parent 9bba3a8 commit 91f2ee7

File tree

2 files changed

+60
-14
lines changed

2 files changed

+60
-14
lines changed

chalk-recursive/src/fulfill.rs

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use chalk_ir::interner::{HasInterner, Interner};
66
use chalk_ir::visit::Visit;
77
use chalk_ir::zip::Zip;
88
use chalk_ir::{
9-
Binders, Canonical, ConstrainedSubst, Constraint, Constraints, DomainGoal, Environment, EqGoal,
10-
Fallible, GenericArg, Goal, GoalData, InEnvironment, NoSolution, ProgramClauseImplication,
11-
QuantifierKind, Substitution, SubtypeGoal, TyKind, TyVariableKind, UCanonical,
12-
UnificationDatabase, UniverseMap, Variance,
9+
Binders, BoundVar, Canonical, ConstrainedSubst, Constraint, Constraints, DomainGoal,
10+
Environment, EqGoal, Fallible, GenericArg, GenericArgData, Goal, GoalData, InEnvironment,
11+
NoSolution, ProgramClauseImplication, QuantifierKind, Substitution, SubtypeGoal, TyKind,
12+
TyVariableKind, UCanonical, UnificationDatabase, UniverseMap, Variance,
1313
};
1414
use chalk_solve::debug_span;
1515
use chalk_solve::infer::{InferenceTable, ParameterEnaVariableExt};
@@ -466,17 +466,19 @@ impl<'s, I: Interner, Solver: SolveDatabase<I>> Fulfill<'s, I, Solver> {
466466
} = self.prove(wc.clone(), minimums)?;
467467

468468
if let Some(constrained_subst) = solution.definite_subst(self.interner()) {
469-
// If the substitution is empty, we won't actually make any progress by applying it!
469+
// If the substitution is trivial, we won't actually make any progress by applying it!
470470
// So we need to check this to prevent endless loops.
471-
// (An ambiguous solution with empty substitution
472-
// can probably not happen in valid code, but it can
473-
// happen e.g. when there are overlapping impls.)
474-
if !constrained_subst.value.subst.is_empty(self.interner())
475-
|| !constrained_subst
476-
.value
477-
.constraints
478-
.is_empty(self.interner())
479-
{
471+
let nontrivial_subst = !is_trivial_canonical_subst(
472+
self.interner(),
473+
&constrained_subst.value.subst,
474+
);
475+
476+
let has_constraints = !constrained_subst
477+
.value
478+
.constraints
479+
.is_empty(self.interner());
480+
481+
if nontrivial_subst || has_constraints {
480482
self.apply_solution(free_vars, universes, constrained_subst);
481483
progress = true;
482484
}
@@ -608,3 +610,28 @@ impl<'s, I: Interner, Solver: SolveDatabase<I>> Fulfill<'s, I, Solver> {
608610
self.solver.interner()
609611
}
610612
}
613+
614+
fn is_trivial_canonical_subst<I: Interner>(interner: I, subst: &Substitution<I>) -> bool {
615+
// A subst is trivial if..
616+
subst.iter(interner).enumerate().all(|(index, parameter)| {
617+
let is_trivial = |b: Option<BoundVar>| match b {
618+
None => false,
619+
Some(bound_var) => {
620+
if let Some(index1) = bound_var.index_if_innermost() {
621+
index == index1
622+
} else {
623+
false
624+
}
625+
}
626+
};
627+
628+
match parameter.data(interner) {
629+
// All types and consts are mapped to distinct variables. Since this
630+
// has been canonicalized, those will also be the first N
631+
// variables.
632+
GenericArgData::Ty(t) => is_trivial(t.bound_var(interner)),
633+
GenericArgData::Const(t) => is_trivial(t.bound_var(interner)),
634+
GenericArgData::Lifetime(t) => is_trivial(t.bound_var(interner)),
635+
}
636+
})
637+
}

tests/test/misc.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,3 +826,22 @@ fn env_bound_vars() {
826826
}
827827
}
828828
}
829+
830+
#[test]
831+
fn recursive_hang() {
832+
test! {
833+
program {}
834+
835+
goal {
836+
exists<'a, T> {
837+
if(T: 'a) {
838+
WellFormed(&'a T)
839+
}
840+
}
841+
} yields[SolverChoice::slg_default()] {
842+
expect![["Ambiguous; definite substitution for<?U0,?U0> { [?0 := ^0.0, ?1 := '^0.1] }"]]
843+
} yields[SolverChoice::recursive_default()] {
844+
expect![["Ambiguous; suggested substitution for<?U0,?U0> { [?0 := ^0.0, ?1 := '^0.1] }"]]
845+
}
846+
}
847+
}

0 commit comments

Comments
 (0)