Skip to content

Commit 6c041af

Browse files
committed
Applying not to compatible instead of vice versa
1 parent 8732b5a commit 6c041af

File tree

2 files changed

+29
-18
lines changed

2 files changed

+29
-18
lines changed

src/coherence/solve.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ir::*;
77
use cast::*;
88
use solve::{SolverChoice, Solution};
99

10-
struct OverlapSolver {
10+
struct DisjointSolver {
1111
env: Arc<ProgramEnvironment>,
1212
solver_choice: SolverChoice,
1313
}
@@ -21,7 +21,7 @@ impl Program {
2121
where
2222
F: FnMut(ItemId, ItemId),
2323
{
24-
let mut solver = OverlapSolver {
24+
let mut solver = DisjointSolver {
2525
env: Arc::new(self.environment()),
2626
solver_choice,
2727
};
@@ -65,7 +65,7 @@ impl Program {
6565
// Check if the impls overlap, then if they do, check if one specializes
6666
// the other. Note that specialization can only run one way - if both
6767
// specialization checks return *either* true or false, that's an error.
68-
if solver.overlap(lhs, rhs) {
68+
if !solver.disjoint(lhs, rhs) {
6969
match (solver.specializes(lhs, rhs), solver.specializes(rhs, lhs)) {
7070
(true, false) => record_specialization(l_id, r_id),
7171
(false, true) => record_specialization(r_id, l_id),
@@ -82,36 +82,41 @@ impl Program {
8282
}
8383
}
8484

85-
impl OverlapSolver {
85+
impl DisjointSolver {
8686
// Test if the set of types that these two impls apply to overlap. If the test succeeds, these
87-
// two impls overlap.
87+
// two impls are disjoint.
8888
//
89-
// We combine the binders of the two impls & treat them as existential
90-
// quantifiers. Then we attempt to unify the input types to the trait provided
91-
// by each impl, as well as prove that the where clauses from both impls all
92-
// hold.
89+
// We combine the binders of the two impls & treat them as existential quantifiers. Then we
90+
// attempt to unify the input types to the trait provided by each impl, as well as prove that
91+
// the where clauses from both impls all hold. At the end, we apply the `compatible` modality
92+
// and negate the query. Negating the query means that we are asking chalk to prove that no
93+
// such overlapping impl exists. By applying `compatible { G }`, chalk attempts to prove that
94+
// "there exists a compatible world where G is provable." When we negate compatible, it turns
95+
// into the statement "for all compatible worlds, G is not provable." This is exactly what we
96+
// want since we want to ensure that there is no overlap in *all* compatible worlds, not just
97+
// that there is no overlap in *some* compatible world.
9398
//
9499
// Examples:
95100
//
96101
// Impls:
97102
// impl<T> Foo for T { }
98103
// impl Foo for i32 { }
99104
// Generates:
100-
// compatible { exists<T> { T = i32 } }
105+
// not { compatible { exists<T> { T = i32 } } }
101106
//
102107
// Impls:
103108
// impl<T1, U> Foo<T1> for Vec<U> { }
104109
// impl<T2> Foo<T2> for Vec<i32> { }
105110
// Generates:
106-
// compatible { exists<T1, U, T2> { Vec<U> = Vec<i32>, T1 = T2 } }
111+
// not { compatible { exists<T1, U, T2> { Vec<U> = Vec<i32>, T1 = T2 } } }
107112
//
108113
// Impls:
109114
// impl<T> Foo for Vec<T> where T: Bar { }
110115
// impl<U> Foo for Vec<U> where U: Baz { }
111116
// Generates:
112-
// compatible { exists<T, U> { Vec<T> = Vec<U>, T: Bar, U: Baz } }
117+
// not { compatible { exists<T, U> { Vec<T> = Vec<U>, T: Bar, U: Baz } } }
113118
//
114-
fn overlap(&self, lhs: &ImplDatum, rhs: &ImplDatum) -> bool {
119+
fn disjoint(&self, lhs: &ImplDatum, rhs: &ImplDatum) -> bool {
115120
debug_heading!("overlaps(lhs={:#?}, rhs={:#?})", lhs, rhs);
116121

117122
let lhs_len = lhs.binders.len();
@@ -150,19 +155,20 @@ impl OverlapSolver {
150155
.fold1(|goal, leaf| Goal::And(Box::new(goal), Box::new(leaf)))
151156
.expect("Every trait takes at least one input type")
152157
.quantify(QuantifierKind::Exists, binders)
153-
.compatible();
158+
.compatible()
159+
.negate();
154160

155161
let canonical_goal = &goal.into_closed_goal();
156162
let solution = self.solver_choice
157163
.solve_root_goal(&self.env, canonical_goal)
158164
.unwrap(); // internal errors in the solver are fatal
159165
let result = match solution {
160-
// Goal was proven with a unique solution, so an impl was found that causes these two
166+
// Goal was proven with a unique solution, so no impl was found that causes these two
161167
// to overlap
162-
Some(Solution::Unique(_)) |
168+
Some(Solution::Unique(_)) => true,
163169
// Goal was ambiguous, so there *may* be overlap
164-
Some(Solution::Ambig(_)) => true,
165-
// Goal cannot be proven, so impls are disjoint
170+
Some(Solution::Ambig(_)) |
171+
// Goal cannot be proven, so there is some impl that causes overlap
166172
None => false,
167173
};
168174
debug!("overlaps: result = {:?}", result);

src/ir.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,11 @@ impl Goal {
11021102
)
11031103
}
11041104

1105+
/// Takes a goal `G` and turns it into `not { G }`
1106+
crate fn negate(self) -> Self {
1107+
Goal::Not(Box::new(self))
1108+
}
1109+
11051110
/// Takes a goal `G` and turns it into `compatible { G }`
11061111
crate fn compatible(self) -> Self {
11071112
// compatible { G } desugars into: forall<T> { if (Compatible, DownstreamType(T)) { G } }

0 commit comments

Comments
 (0)