Skip to content

Commit a4e70aa

Browse files
nikomatsakisbasil-cow
authored andcommitted
expand coinductive reasoning
1 parent 3dfee39 commit a4e70aa

File tree

3 files changed

+53
-7
lines changed

3 files changed

+53
-7
lines changed

chalk-engine/src/logic.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,8 +1487,13 @@ impl<'forest, C: Context + 'forest, CO: ContextOps<C> + 'forest> SolveState<'for
14871487
/// coinductive goal. But `top_of_stack_is_coinductive_from(0)` is
14881488
/// false, since `XXX` is not an auto trait.
14891489
pub(super) fn top_of_stack_is_coinductive_from(&self, depth: StackIndex) -> bool {
1490+
debug_heading!("top_of_stack_is_coinductive_from(depth={:?})", depth);
14901491
StackIndex::iterate_range(self.stack.top_of_stack_from(depth)).all(|d| {
14911492
let table = self.stack[d].table;
1493+
debug!(
1494+
"d = {:?}, table = {:?}",
1495+
d, self.forest.tables[table].table_goal
1496+
);
14921497
self.forest.tables[table].coinductive_goal
14931498
})
14941499
}

chalk-solve/src/clauses.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,15 @@ fn program_clauses_that_could_match<I: Interner>(
243243
.opaque_ty_data(opaque_ty.opaque_ty_id)
244244
.to_program_clauses(builder),
245245
},
246-
DomainGoal::WellFormed(WellFormed::Trait(trait_ref))
247-
| DomainGoal::LocalImplAllowed(trait_ref) => {
248-
db.trait_datum(trait_ref.trait_id)
246+
DomainGoal::LocalImplAllowed(trait_ref) => db
247+
.trait_datum(trait_ref.trait_id)
248+
.to_program_clauses(builder),
249+
DomainGoal::WellFormed(WellFormed::Trait(trait_predicate)) => {
250+
let self_ty = trait_predicate.self_type_parameter(interner);
251+
if self_ty.bound_var(interner).is_some() || self_ty.inference_var(interner).is_some() {
252+
return Err(Floundered);
253+
}
254+
db.trait_datum(trait_predicate.trait_id)
249255
.to_program_clauses(builder);
250256
}
251257
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) {
@@ -26,10 +38,33 @@ impl<I: Interner> IsCoinductive<I> for Goal<I> {
2638
WhereClause::AliasEq(..) => false,
2739
},
2840
GoalData::DomainGoal(DomainGoal::WellFormed(WellFormed::Trait(..))) => true,
29-
GoalData::Quantified(QuantifierKind::ForAll, goal) => {
30-
goal.skip_binders().is_coinductive(db)
31-
}
32-
_ => false,
41+
GoalData::DomainGoal(_) => false,
42+
43+
// Goals like `forall<..> { G }` or `... => G` we will consider to
44+
// be coinductive if G is coinductive, although in practice I think
45+
// it would be ok to simply consider them coinductive all the time.
46+
GoalData::Quantified(_, goal) => goal.skip_binders().is_coinductive(db),
47+
GoalData::Implies(_, goal) => goal.is_coinductive(db),
48+
49+
// The "All(...)" quantifier is considered coinductive. This could
50+
// be somewhat surprising as you might have `All(Gc, Gi)` where `Gc`
51+
// is coinductive and `Gi` is inductive. This however is really no
52+
// different than defining a fresh coinductive goal like
53+
//
54+
// ```notrust
55+
// Gx :- Gc, Gi
56+
// ```
57+
//
58+
// and requiring `Gx` in place of `All(Gc, Gi)`, and that would be
59+
// perfectly reasonable.
60+
GoalData::All(..) => true,
61+
62+
// For simplicity, just assume these remaining types of goals must
63+
// be inductive, since there is no pressing reason to consider them
64+
// coinductive.
65+
GoalData::Not(_) => false,
66+
GoalData::EqGoal(_) => false,
67+
GoalData::CannotProve(_) => false,
3368
}
3469
}
3570
}

0 commit comments

Comments
 (0)