Skip to content

Commit 56f897f

Browse files
committed
first coherence tests -- neat!
1 parent b18b1f5 commit 56f897f

File tree

12 files changed

+171
-49
lines changed

12 files changed

+171
-49
lines changed

crates/formality-check/src/coherence.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use anyhow::bail;
2+
use formality_prove::Env;
23
use formality_rust::grammar::{Crate, TraitImpl};
3-
use formality_types::{cast::Downcasted, grammar::Fallible};
4+
use formality_types::{
5+
cast::Downcasted,
6+
grammar::{Fallible, Wcs},
7+
};
48
use itertools::Itertools;
59

610
use crate::Check;
@@ -38,7 +42,24 @@ impl Check<'_> {
3842
Ok(())
3943
}
4044

41-
fn overlap_check(&self, _impl_a: &TraitImpl, _impl_b: &TraitImpl) -> Fallible<()> {
42-
Ok(())
45+
fn overlap_check(&self, impl_a: &TraitImpl, impl_b: &TraitImpl) -> Fallible<()> {
46+
let mut env = Env::default();
47+
48+
let a = env.instantiate_universally(&impl_a.binder);
49+
let b = env.instantiate_universally(&impl_b.binder);
50+
51+
let trait_ref_a = a.trait_ref();
52+
let trait_ref_b = b.trait_ref();
53+
54+
// If the parameters from the two impls cannot be equal, then they do not overlap.
55+
if let Ok(()) = self.prove_not_goal(
56+
&env,
57+
(&a.where_clauses, &b.where_clauses),
58+
Wcs::all_eq(&trait_ref_a.parameters, &trait_ref_b.parameters),
59+
) {
60+
return Ok(());
61+
}
62+
63+
bail!("impls may overlap: `{impl_a:?}` vs `{impl_b:?}`")
4364
}
4465
}

crates/formality-check/src/lib.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use formality_rust::{
1010
};
1111
use formality_types::{
1212
cast::Upcast,
13-
grammar::{Fallible, Wcs},
13+
grammar::{Fallible, Substitution, Wcs},
1414
};
1515

1616
/// Check all crates in the program. The crates must be in dependency order
@@ -107,4 +107,45 @@ impl Check<'_> {
107107

108108
bail!("failed to prove {goal:?} given {assumptions:?}, got {cs:?}")
109109
}
110+
111+
fn prove_not_goal(&self, env: &Env, assumptions: impl ToWcs, goal: impl ToWcs) -> Fallible<()> {
112+
let goal: Wcs = goal.to_wcs();
113+
let assumptions: Wcs = assumptions.to_wcs();
114+
115+
assert!(env.only_universal_variables());
116+
assert!(env.encloses((&assumptions, &goal)));
117+
118+
// Proving `∀X. not(F(X))` is the same as proving: `not(∃X. F(X))`.
119+
// Therefore, since we have only universal variables, we can change them all to
120+
// existential and then try to prove. If we get back no solutions, we know that
121+
// we've proven the negation. (This is called the "negation as failure" property,
122+
// and it relies on our solver being complete -- i.e., if there is a solution,
123+
// we'll find it, or at least return ambiguous.)
124+
let mut existential_env = Env::default();
125+
let universal_to_existential: Substitution = env
126+
.variables()
127+
.iter()
128+
.map(|v| {
129+
assert!(v.is_universal());
130+
let v1 = existential_env.fresh_existential(v.kind());
131+
(v, v1)
132+
})
133+
.collect();
134+
135+
let existential_assumptions = universal_to_existential.apply(&assumptions);
136+
let existential_goal = universal_to_existential.apply(&goal);
137+
138+
let cs = formality_prove::prove(
139+
self.decls,
140+
&existential_env,
141+
existential_assumptions.to_wcs(),
142+
&existential_goal,
143+
);
144+
145+
if cs.is_empty() {
146+
return Ok(());
147+
}
148+
149+
bail!("failed to disprove {goal:?} given {assumptions:?}, got {cs:?}")
150+
}
110151
}

crates/formality-prove/src/prove/env.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ use formality_types::{
55
collections::Set,
66
fold::Fold,
77
grammar::{
8-
Binder, InferenceVar, ParameterKind, PlaceholderVar, VarIndex, VarSubstitution,
9-
Variable,
8+
Binder, InferenceVar, ParameterKind, PlaceholderVar, VarIndex, VarSubstitution, Variable,
109
},
1110
visit::Visit,
1211
};
@@ -18,11 +17,7 @@ pub struct Env {
1817

1918
impl Env {
2019
pub fn only_universal_variables(&self) -> bool {
21-
self.variables.iter().all(|v| match v {
22-
Variable::PlaceholderVar(_) => true,
23-
Variable::InferenceVar(_) => false,
24-
Variable::BoundVar(_) => false,
25-
})
20+
self.variables.iter().all(|v| v.is_universal())
2621
}
2722
}
2823

@@ -64,6 +59,13 @@ impl Env {
6459
}
6560
}
6661

62+
pub fn fresh_existential(&mut self, kind: ParameterKind) -> InferenceVar {
63+
let var_index = self.fresh_index();
64+
let v = InferenceVar { kind, var_index };
65+
self.variables.push(v.upcast());
66+
v
67+
}
68+
6769
pub fn insert_fresh_before(&mut self, kind: ParameterKind, rank: Universe) -> InferenceVar {
6870
let var_index = self.fresh_index();
6971
let v = InferenceVar { kind, var_index };

crates/formality-prove/src/prove/prove_eq.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use formality_types::{
22
cast::{Downcast, Upcast, Upcasted},
33
collections::{Deduplicate, Set},
44
grammar::{
5-
AliasTy, InferenceVar, Parameter, PlaceholderVar, Relation, RigidTy, Substitution,
6-
TyData, Variable, Wcs,
5+
AliasTy, InferenceVar, Parameter, PlaceholderVar, Relation, RigidTy, Substitution, TyData,
6+
Variable, Wcs,
77
},
88
judgment_fn, set,
99
visit::Visit,
@@ -18,18 +18,6 @@ use crate::{
1818

1919
use super::{constraints::Constraints, env::Env};
2020

21-
/// Goal(s) to prove `a` and `b` are equal (they must have equal length)
22-
pub fn all_eq(a: impl Upcast<Vec<Parameter>>, b: impl Upcast<Vec<Parameter>>) -> Wcs {
23-
let a: Vec<Parameter> = a.upcast();
24-
let b: Vec<Parameter> = b.upcast();
25-
assert_eq!(a.len(), b.len());
26-
a.into_iter()
27-
.zip(b)
28-
.map(|(a, b)| Relation::eq(a, b))
29-
.upcasted()
30-
.collect()
31-
}
32-
3321
/// Goal(s) to prove `a` and `b` are equal
3422
pub fn eq(a: impl Upcast<Parameter>, b: impl Upcast<Parameter>) -> Relation {
3523
Relation::eq(a, b)
@@ -59,7 +47,7 @@ judgment_fn! {
5947
(let RigidTy { name: a_name, parameters: a_parameters } = a)
6048
(let RigidTy { name: b_name, parameters: b_parameters } = b)
6149
(if a_name == b_name)
62-
(prove(decls, env, assumptions, all_eq(a_parameters, b_parameters)) => c)
50+
(prove(decls, env, assumptions, Wcs::all_eq(a_parameters, b_parameters)) => c)
6351
----------------------------- ("rigid")
6452
(prove_eq(decls, env, assumptions, TyData::RigidTy(a), TyData::RigidTy(b)) => c)
6553
)
@@ -68,7 +56,7 @@ judgment_fn! {
6856
(let AliasTy { name: a_name, parameters: a_parameters } = a)
6957
(let AliasTy { name: b_name, parameters: b_parameters } = b)
7058
(if a_name == b_name)
71-
(prove(decls, env, assumptions, all_eq(a_parameters, b_parameters)) => env_c)
59+
(prove(decls, env, assumptions, Wcs::all_eq(a_parameters, b_parameters)) => env_c)
7260
----------------------------- ("alias")
7361
(prove_eq(decls, env, assumptions, TyData::AliasTy(a), TyData::AliasTy(b)) => env_c)
7462
)

crates/formality-prove/src/prove/prove_normalize.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@ use formality_types::{
99
use crate::{
1010
decls::{AliasEqDeclBoundData, Decls},
1111
prove::{
12-
env::Env,
13-
prove,
14-
prove_after::prove_after,
15-
prove_eq::{all_eq, prove_existential_var_eq},
16-
zip::zip,
12+
env::Env, prove, prove_after::prove_after, prove_eq::prove_existential_var_eq, zip::zip,
1713
},
1814
};
1915

@@ -41,7 +37,7 @@ judgment_fn! {
4137
(let decl = decl.binder.instantiate_with(&subst).unwrap())
4238
(let AliasEqDeclBoundData { alias: AliasTy { name, parameters }, ty, where_clause } = decl)
4339
(assert a.name == name)
44-
(prove(&decls, env, &assumptions, all_eq(&a.parameters, &parameters)) => c)
40+
(prove(&decls, env, &assumptions, Wcs::all_eq(&a.parameters, &parameters)) => c)
4541
(prove_after(&decls, c, &assumptions, &where_clause) => c)
4642
(let ty = c.substitution().apply(&ty))
4743
(let c = c.pop_subst(&subst))

crates/formality-prove/src/prove/prove_via.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use formality_types::{
66
use crate::{
77
decls::Decls,
88
prove::{
9-
constraints::Constraints, env::Env, prove, prove_after::prove_after, prove_eq::all_eq,
9+
constraints::Constraints, env::Env, prove, prove_after::prove_after,
1010
},
1111
};
1212

@@ -24,7 +24,7 @@ judgment_fn! {
2424
(let (skel_c, parameters_c) = predicate.debone())
2525
(let (skel_g, parameters_g) = goal.debone())
2626
(if skel_c == skel_g)
27-
(prove(decls, env, assumptions, all_eq(parameters_c, parameters_g)) => c)
27+
(prove(decls, env, assumptions, Wcs::all_eq(parameters_c, parameters_g)) => c)
2828
----------------------------- ("predicate-congruence-axiom")
2929
(prove_via(decls, env, assumptions, PR::Predicate(predicate), goal) => c)
3030
)

crates/formality-prove/src/prove/prove_wc.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,7 @@ use formality_types::{
55

66
use crate::{
77
decls::Decls,
8-
prove::{
9-
env::Env,
10-
prove,
11-
prove_after::prove_after,
12-
prove_eq::{all_eq, prove_eq},
13-
prove_via::prove_via,
14-
},
8+
prove::{env::Env, prove, prove_after::prove_after, prove_eq::prove_eq, prove_via::prove_via},
159
};
1610

1711
use super::constraints::Constraints;
@@ -52,7 +46,7 @@ judgment_fn! {
5246
(let i = i.binder.instantiate_with(&subst).unwrap())
5347
(let t = decls.trait_decl(&i.trait_ref.trait_id).binder.instantiate_with(&i.trait_ref.parameters).unwrap())
5448
(let co_assumptions = (&assumptions, &trait_ref))
55-
(prove(&decls, env, &co_assumptions, all_eq(&trait_ref.parameters, &i.trait_ref.parameters)) => c)
49+
(prove(&decls, env, &co_assumptions, Wcs::all_eq(&trait_ref.parameters, &i.trait_ref.parameters)) => c)
5650
(prove_after(&decls, c, &co_assumptions, &i.where_clause) => c)
5751
(prove_after(&decls, c, &assumptions, &t.where_clause) => c)
5852
----------------------------- ("impl")

crates/formality-rust/src/grammar.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use formality_types::{
55
cast::Upcast,
66
grammar::{
77
AdtId, AssociatedItemId, Binder, CrateId, Fallible, FieldId, FnId, Lt, Parameter, TraitId,
8-
Ty,
8+
TraitRef, Ty,
99
},
1010
term::Term,
1111
};
@@ -254,6 +254,12 @@ pub struct TraitImplBoundData {
254254
pub impl_items: Vec<ImplItem>,
255255
}
256256

257+
impl TraitImplBoundData {
258+
pub fn trait_ref(&self) -> TraitRef {
259+
self.trait_id.with(&self.self_ty, &self.trait_parameters)
260+
}
261+
}
262+
257263
#[term]
258264
pub enum ImplItem {
259265
#[cast]

crates/formality-types/src/grammar/formulas.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ use crate::cast::To;
44
use crate::cast::Upcast;
55
use crate::cast_impl;
66

7-
use super::AdtId;
8-
use super::AliasName;
9-
use super::AliasTy;
107
use super::Parameter;
118
use super::Parameters;
129
use super::TraitId;

crates/formality-types/src/grammar/ty.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,14 @@ impl Variable {
486486
}) => false,
487487
}
488488
}
489+
490+
pub fn is_universal(&self) -> bool {
491+
match self {
492+
Variable::PlaceholderVar(_) => true,
493+
Variable::InferenceVar(_) => false,
494+
Variable::BoundVar(_) => false,
495+
}
496+
}
489497
}
490498

491499
impl Visit for Variable {

0 commit comments

Comments
 (0)