Skip to content

Commit 023290e

Browse files
committed
Add new exhaustive covering predicate
& Add additional tests
1 parent dc57601 commit 023290e

26 files changed

+266
-230
lines changed

crates/formality-core/src/judgment/proven_set.rs

+11
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ impl<T: Ord + Debug> ProvenSet<T> {
9999
}
100100
}
101101

102+
/// Convert to a non-empty set of proven results (if ok) or an error (otherwise).
103+
pub fn into_iter(self) -> Box<dyn Iterator<Item = T>>
104+
where
105+
T: 'static,
106+
{
107+
match self.data {
108+
Data::Failure(_) => Box::new(std::iter::empty()),
109+
Data::Success(s) => Box::new(s.into_iter()),
110+
}
111+
}
112+
102113
/// For each item `t` that was proven,
103114
/// invoke `op(t)` to yield a new set of proven results
104115
/// and then flatten those into a new proven set.

crates/formality-prove/src/prove.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ pub fn prove(
4949
return ProvenSet::singleton(Constraints::none(env).ambiguous());
5050
}
5151

52-
assert!(env.encloses(term_in));
52+
assert!(
53+
env.encloses(term_in),
54+
"{env:?} does not enclose {term_in:?}: {:?}",
55+
term_in.free_variables()
56+
);
5357

5458
let result_set = prove_wc_list(decls, &env, assumptions, goal);
5559

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

+75-15
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
1-
use formality_core::judgment_fn;
2-
use formality_types::grammar::{Predicate, Relation, Wc, WcData, Wcs};
3-
4-
use crate::{
5-
decls::Decls,
6-
prove::{
7-
env::Env,
8-
is_local::{is_local_trait_ref, may_be_remote},
9-
prove,
10-
prove_after::prove_after,
11-
prove_eq::prove_eq,
12-
prove_via::prove_via,
13-
prove_wf::prove_wf,
14-
},
1+
use crate::prove;
2+
use crate::prove::is_local::is_local_trait_ref;
3+
use crate::prove::is_local::may_be_remote;
4+
use crate::prove::prove_after::prove_after;
5+
use crate::prove::prove_eq::prove_eq;
6+
use crate::prove::prove_via::prove_via;
7+
use crate::prove::prove_wf::prove_wf;
8+
use crate::Decls;
9+
use crate::Env;
10+
11+
use crate::Constraints;
12+
use formality_core::{judgment_fn, ProvenSet, Upcasted};
13+
use formality_types::grammar::{
14+
Const, ExhaustiveState, Parameter, Predicate, Relation, Scalar, Ty, Wc, WcData, Wcs,
1515
};
1616

17-
use super::constraints::Constraints;
17+
pub fn is_covering(vals: &[ExhaustiveState], params: &[Parameter]) -> Wcs {
18+
assert_eq!(vals.len(), params.len());
19+
vals.iter()
20+
.zip(params.iter())
21+
.filter_map(|(a, b)| match a {
22+
ExhaustiveState::ExactMatch => None,
23+
ExhaustiveState::ConstCover(cs) => {
24+
let Parameter::Const(c) = b else {
25+
todo!();
26+
};
27+
Some(Predicate::Covers(cs.clone(), c.clone()))
28+
}
29+
})
30+
.upcasted()
31+
.collect()
32+
}
1833

1934
judgment_fn! {
2035
pub fn prove_wc(
@@ -46,6 +61,34 @@ judgment_fn! {
4661
(prove_wc(decls, env, assumptions, WcData::PR(goal)) => c)
4762
)
4863

64+
(
65+
(let mut covering_consts = vec![ExhaustiveState::ExactMatch; trait_ref.parameters.len()])
66+
(let asmp = &assumptions)
67+
(let d = &decls)
68+
(d.impl_decls(&trait_ref.trait_id).flat_map(|i| {
69+
70+
let (env, subst) = env.clone().universal_substitution(&i.binder);
71+
let i = i.binder.instantiate_with(&subst).unwrap();
72+
let co_assumptions = (asmp, &trait_ref);
73+
let cs = prove(
74+
&decls, env, &co_assumptions,
75+
Wcs::eq_or_cover(
76+
&i.trait_ref.parameters, &trait_ref.parameters, &mut covering_consts
77+
)
78+
);
79+
let cs = cs.flat_map(move |c| prove_after(d, c, &co_assumptions, &i.where_clause));
80+
let cs = cs.flat_map(move |c| {
81+
let t = d.trait_decl(&i.trait_ref.trait_id)
82+
.binder.instantiate_with(&i.trait_ref.parameters).unwrap();
83+
prove_after(d, c, asmp, &t.where_clause)
84+
});
85+
cs.into_iter()
86+
}).into_iter().collect::<ProvenSet<_>>() => c)
87+
(prove_after(d, c, asmp, is_covering(&covering_consts, &trait_ref.parameters)) => c)
88+
----------------------------- ("exhaustive positive impl")
89+
(prove_wc(decls, env, assumptions, Predicate::IsImplemented(trait_ref)) => c)
90+
)
91+
4992
(
5093
(decls.impl_decls(&trait_ref.trait_id) => i)!
5194
(let (env, subst) = env.existential_substitution(&i.binder))
@@ -112,6 +155,23 @@ judgment_fn! {
112155
(prove_wc(decls, env, assumptions, Predicate::IsLocal(trait_ref)) => c)
113156
)
114157

158+
(
159+
(let () = vals.sort_unstable())
160+
(prove(&decls, env, &assumptions, Predicate::ConstHasType(var, Ty::bool())) => c)
161+
(vals.iter().cloned() => v)
162+
(prove_after(&decls, &c, &assumptions, Predicate::ConstHasType(v, Ty::bool())) => c)
163+
(if vals.len() == 2)
164+
(vals.clone().into_iter().enumerate().flat_map(|(i, v)| {
165+
prove_after(
166+
&decls, &c, &assumptions,
167+
Relation::Equals(Parameter::Const(Const::valtree(Scalar::new(i as u128),
168+
Ty::bool())), Parameter::Const(v))
169+
).into_iter()
170+
}).collect::<ProvenSet<_>>() => c)
171+
----------------------------- ("exhaustive bool values cover variable")
172+
(prove_wc(decls, env, assumptions, Predicate::Covers(mut vals, var)) => c)
173+
)
174+
115175

116176
(
117177
(prove_wf(decls, env, assumptions, p) => c)

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

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ impl Const {
2626
Self::new(ConstData::Value(vt.upcast(), ty.upcast()))
2727
}
2828

29+
pub fn is_variable(&self) -> bool {
30+
matches!(self.data(), ConstData::Variable(_))
31+
}
32+
2933
pub fn as_variable(&self) -> Option<Variable> {
3034
match self.data() {
3135
ConstData::Value(_, _) => None,

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

+13
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ pub enum Predicate {
3636

3737
#[grammar(@ConstHasType($v0, $v1))]
3838
ConstHasType(Const, Ty),
39+
40+
#[grammar(@Covers($v0, $v1))]
41+
Covers(Vec<Const>, Const),
3942
}
4043

4144
/// A coinductive predicate is one that can be proven via a cycle.
@@ -94,6 +97,7 @@ pub enum Skeleton {
9497
Equals,
9598
Sub,
9699
Outlives,
100+
Covered,
97101
}
98102

99103
impl Predicate {
@@ -136,6 +140,15 @@ impl Predicate {
136140
Skeleton::ConstHasType,
137141
vec![ct.clone().upcast(), ty.clone().upcast()],
138142
),
143+
Predicate::Covers(consts, c) => (
144+
Skeleton::Covered,
145+
consts
146+
.iter()
147+
.cloned()
148+
.chain(std::iter::once(c.clone()))
149+
.map(|c| Parameter::Const(c))
150+
.collect::<Vec<_>>(),
151+
),
139152
}
140153
}
141154
}

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

+38-1
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@ use formality_core::{
66

77
use crate::grammar::PR;
88

9-
use super::{Binder, BoundVar, Parameter, Predicate, Relation, TraitRef};
9+
use super::{Binder, BoundVar, Const, Parameter, Predicate, Relation, TraitRef};
1010

1111
#[term($set)]
1212
#[derive(Default)]
1313
pub struct Wcs {
1414
set: Set<Wc>,
1515
}
1616

17+
#[derive(Debug, Clone, PartialEq)]
18+
pub enum ExhaustiveState {
19+
ExactMatch,
20+
ConstCover(Vec<Const>),
21+
}
22+
1723
impl Wcs {
1824
pub fn t() -> Self {
1925
set![].upcast()
@@ -30,6 +36,37 @@ impl Wcs {
3036
.upcasted()
3137
.collect()
3238
}
39+
/// Goal(s) to prove `a` and `b` are equal (they must have equal length)
40+
pub fn eq_or_cover(
41+
candidate: impl Upcast<Vec<Parameter>>,
42+
trait_impl: impl Upcast<Vec<Parameter>>,
43+
covering_items: &mut [ExhaustiveState],
44+
) -> Wcs {
45+
let a: Vec<Parameter> = candidate.upcast();
46+
let b: Vec<Parameter> = trait_impl.upcast();
47+
assert_eq!(a.len(), b.len());
48+
a.into_iter()
49+
.zip(b)
50+
.enumerate()
51+
.filter_map(|(i, (a, b))| match (&a, &b) {
52+
(Parameter::Const(ct), Parameter::Const(param)) if param.is_variable() => {
53+
if covering_items[i] == ExhaustiveState::ExactMatch {
54+
covering_items[i] = ExhaustiveState::ConstCover(vec![]);
55+
}
56+
let ExhaustiveState::ConstCover(ref mut c) = &mut covering_items[i] else {
57+
panic!();
58+
};
59+
c.push(ct.clone());
60+
None
61+
}
62+
_ => {
63+
assert!(matches!(covering_items[i], ExhaustiveState::ExactMatch));
64+
Some(Relation::equals(a, b))
65+
}
66+
})
67+
.upcasted()
68+
.collect()
69+
}
3370
}
3471

3572
impl<'w> IntoIterator for &'w Wcs {

tests/coherence_overlap.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,11 @@ fn test_overlap_normalize_alias_to_LocalType() {
4747
// ...but it's an error if LocalType implements Iterator (figuring *this* out also
4848
// requires normalizing).
4949

50-
test_program_ok(&gen_program(
51-
"impl Iterator for LocalType {}",
52-
)).assert_err(
50+
test_program_ok(&gen_program("impl Iterator for LocalType {}")).assert_err(
5351
expect_test::expect![[r#"
5452
impls may overlap:
5553
impl <ty> LocalTrait for ^ty0_0 where ^ty0_0 : Iterator { }
56-
impl LocalTrait for <LocalType as Mirror>::T { }"#]]
54+
impl LocalTrait for <LocalType as Mirror>::T { }"#]],
5755
);
5856
}
5957

@@ -103,12 +101,10 @@ fn test_overlap_alias_not_normalizable() {
103101

104102
// ...as long as there is at least one Iterator impl, however, we do flag an error.
105103

106-
test_program_ok(&gen_program(
107-
"impl Iterator for u32 {}",
108-
)).assert_err(
109-
expect_test::expect![[r#"
104+
test_program_ok(&gen_program("impl Iterator for u32 {}")).assert_err(expect_test::expect![[
105+
r#"
110106
impls may overlap:
111107
impl <ty> LocalTrait for ^ty0_0 where ^ty0_0 : Iterator { }
112-
impl <ty> LocalTrait for <^ty0_0 as Mirror>::T where ^ty0_0 : Mirror { }"#]]
113-
); // FIXME
108+
impl <ty> LocalTrait for <^ty0_0 as Mirror>::T where ^ty0_0 : Mirror { }"#
109+
]]); // FIXME
114110
}

0 commit comments

Comments
 (0)