Skip to content

Commit 0b02135

Browse files
nikomatsakisnathanwhit
authored andcommitted
expand coinductive reasoning
1 parent 031768f commit 0b02135

File tree

3 files changed

+54
-6
lines changed

3 files changed

+54
-6
lines changed

chalk-engine/src/logic.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,9 +1571,14 @@ impl<'forest, I: Interner> SolveState<'forest, I> {
15711571
/// would be true, since `Send` is an auto trait, which yields a
15721572
/// coinductive goal. But `top_of_stack_is_coinductive_from(0)` is
15731573
/// false, since `XXX` is not an auto trait.
1574+
#[instrument(level = "debug", skip(self))]
15741575
pub(super) fn top_of_stack_is_coinductive_from(&self, depth: StackIndex) -> bool {
15751576
StackIndex::iterate_range(self.stack.top_of_stack_from(depth)).all(|d| {
15761577
let table = self.stack[d].table;
1578+
debug!(
1579+
"d = {:?}, table = {:?}",
1580+
d, self.forest.tables[table].table_goal
1581+
);
15771582
self.forest.tables[table].coinductive_goal
15781583
})
15791584
}

chalk-solve/src/clauses.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,11 +527,19 @@ fn program_clauses_that_could_match<I: Interner>(
527527
})
528528
});
529529
}
530-
DomainGoal::WellFormed(WellFormed::Trait(trait_ref))
531-
| DomainGoal::LocalImplAllowed(trait_ref) => {
530+
DomainGoal::LocalImplAllowed(trait_ref) => {
532531
db.trait_datum(trait_ref.trait_id)
533532
.to_program_clauses(builder, environment);
534533
}
534+
535+
DomainGoal::WellFormed(WellFormed::Trait(trait_predicate)) => {
536+
let self_ty = trait_predicate.self_type_parameter(interner);
537+
if self_ty.bound_var(interner).is_some() || self_ty.inference_var(interner).is_some() {
538+
return Err(Floundered);
539+
}
540+
db.trait_datum(trait_predicate.trait_id)
541+
.to_program_clauses(builder, environment);
542+
}
535543
DomainGoal::ObjectSafe(trait_id) => {
536544
if builder.db.is_object_safe(*trait_id) {
537545
builder.push_fact(DomainGoal::ObjectSafe(*trait_id));

chalk-solve/src/coinductive_goal.rs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ pub trait IsCoinductive<I: Interner> {
1515
}
1616

1717
impl<I: Interner> IsCoinductive<I> for Goal<I> {
18+
/// A "coinductive" goal `G` is a goal where `G :- G` should be considered
19+
/// true. When we are doing trait solving, if we encounter a cycle of goals
20+
/// where solving `G1` requires `G2..Gn` and solving `Gn` requires `G1`,
21+
/// then our behavior depends on whether each goal `Gi` in that cycle is
22+
/// coinductive.
23+
///
24+
/// If all the goals are coinductive, then `G1` is considered provable,
25+
/// presuming that all the other subgoals for `G2..Gn` within can be fully
26+
/// proven.
27+
///
28+
/// If any goal `Gi` in the cycle is inductive, however, then the cycle is
29+
/// considered unprovable.
1830
fn is_coinductive(&self, db: &dyn RustIrDatabase<I>) -> bool {
1931
let interner = db.interner();
2032
match self.data(interner) {
@@ -28,10 +40,33 @@ impl<I: Interner> IsCoinductive<I> for Goal<I> {
2840
WhereClause::TypeOutlives(..) => false,
2941
},
3042
GoalData::DomainGoal(DomainGoal::WellFormed(WellFormed::Trait(..))) => true,
31-
GoalData::Quantified(QuantifierKind::ForAll, goal) => {
32-
goal.skip_binders().is_coinductive(db)
33-
}
34-
_ => false,
43+
GoalData::DomainGoal(_) => false,
44+
45+
// Goals like `forall<..> { G }` or `... => G` we will consider to
46+
// be coinductive if G is coinductive, although in practice I think
47+
// it would be ok to simply consider them coinductive all the time.
48+
GoalData::Quantified(_, goal) => goal.skip_binders().is_coinductive(db),
49+
GoalData::Implies(_, goal) => goal.is_coinductive(db),
50+
51+
// The "All(...)" quantifier is considered coinductive. This could
52+
// be somewhat surprising as you might have `All(Gc, Gi)` where `Gc`
53+
// is coinductive and `Gi` is inductive. This however is really no
54+
// different than defining a fresh coinductive goal like
55+
//
56+
// ```notrust
57+
// Gx :- Gc, Gi
58+
// ```
59+
//
60+
// and requiring `Gx` in place of `All(Gc, Gi)`, and that would be
61+
// perfectly reasonable.
62+
GoalData::All(..) => true,
63+
64+
// For simplicity, just assume these remaining types of goals must
65+
// be inductive, since there is no pressing reason to consider them
66+
// coinductive.
67+
GoalData::Not(_) => false,
68+
GoalData::EqGoal(_) => false,
69+
GoalData::CannotProve => false,
3570
}
3671
}
3772
}

0 commit comments

Comments
 (0)