Skip to content

Commit 21008a3

Browse files
authored
Merge branch 'main' into uniq
2 parents 8b5e7a2 + 8732bc3 commit 21008a3

40 files changed

+771
-538
lines changed

book/src/formality_core/judgment_fn.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Judgment functions and inference rules
22

3-
The next thing is the `judgement_fn!` macro. This lets you write a *judgment* using *inference rules*. A "judgment" just means some kind of predicate that the computer can judge to hold or not hold. Inference rules are those rules you may have seen in papers and things:
3+
The next thing is the `judgment_fn!` macro. This lets you write a *judgment* using *inference rules*. A "judgment" just means some kind of predicate that the computer can judge to hold or not hold. Inference rules are those rules you may have seen in papers and things:
44

55
```
66
premise1

book/src/formality_core/lang.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ The language module you create has various items in it:
2323

2424
## Specifying the language for a crate
2525

26-
That module will contain a language struct named `FormalityLang`. It
26+
That module will contain a language struct named `FormalityLang`.
2727
Other parts of the formality system (e.g., autoderives and the like)
2828
will need to know the current language you are defining,
2929
and they expect to find it at `crate::FormalityLang`.

crates/formality-check/src/coherence.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ impl Check<'_> {
5353
let a = env.instantiate_universally(&impl_a.binder);
5454
let trait_ref = a.trait_ref();
5555

56-
self.prove_goal(
57-
&env.with_coherence_mode(true),
58-
&a.where_clauses,
59-
trait_ref.is_local(),
60-
)
56+
// The orphan check passes if
57+
// ∀P. ⌐ (coherence_mode => (wf(Ts) && cannot_be_proven(is_local_trait_ref)))
58+
//
59+
// TODO: feels like we do want a general "not goal", flipping existentials
60+
// and universals and the coherence mode
61+
// self.prove_not_goal(&env, &(Wcs::wf))
62+
63+
self.prove_goal(&env, &a.where_clauses, trait_ref.is_local())
6164
}
6265

6366
#[context("orphan_check_neg({impl_a:?})")]
@@ -67,11 +70,7 @@ impl Check<'_> {
6770
let a = env.instantiate_universally(&impl_a.binder);
6871
let trait_ref = a.trait_ref();
6972

70-
self.prove_goal(
71-
&env.with_coherence_mode(true),
72-
&a.where_clauses,
73-
trait_ref.is_local(),
74-
)
73+
self.prove_goal(&env, &a.where_clauses, trait_ref.is_local())
7574
}
7675

7776
#[tracing::instrument(level = "Debug", skip(self))]
@@ -100,7 +99,7 @@ impl Check<'_> {
10099
//
101100
// ∀P_a, ∀P_b. ⌐ (coherence_mode => (Ts_a = Ts_b && WC_a && WC_b))
102101
if let Ok(()) = self.prove_not_goal(
103-
&env.with_coherence_mode(true),
102+
&env,
104103
(),
105104
(
106105
Wcs::all_eq(&trait_ref_a.parameters, &trait_ref_b.parameters),

crates/formality-check/src/lib.rs

Lines changed: 13 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
use std::{collections::VecDeque, fmt::Debug};
44

55
use anyhow::bail;
6-
use formality_prove::{Decls, Env};
6+
use formality_prove::{is_definitely_not_proveable, Decls, Env};
77
use formality_rust::{
88
grammar::{Crate, CrateItem, Program, Test, TestBoundData},
99
prove::ToWcs,
1010
};
11-
use formality_types::grammar::{Fallible, Substitution, Wcs};
11+
use formality_types::grammar::{Fallible, Wcs};
1212

1313
/// Check all crates in the program. The crates must be in dependency order
1414
/// such that any prefix of the crates is a complete program.
@@ -115,58 +115,24 @@ impl Check<'_> {
115115
}
116116

117117
#[tracing::instrument(level = "Debug", skip(self, assumptions, goal))]
118-
fn prove_not_goal(
119-
&self,
120-
env: &Env,
121-
assumptions: impl ToWcs,
122-
goal: impl ToWcs + Debug,
123-
) -> Fallible<()> {
118+
fn prove_not_goal(&self, env: &Env, assumptions: impl ToWcs, goal: impl ToWcs) -> Fallible<()> {
124119
let goal: Wcs = goal.to_wcs();
125120
let assumptions: Wcs = assumptions.to_wcs();
126121

127-
tracing::debug!("assumptions = {assumptions:?}");
128-
tracing::debug!("goal = {goal:?}");
129-
130122
assert!(env.only_universal_variables());
131123
assert!(env.encloses((&assumptions, &goal)));
132124

133-
// Proving `∀X. not(F(X))` is the same as proving: `not(∃X. F(X))`.
134-
// Therefore, since we have only universal variables, we can change them all to
135-
// existential and then try to prove. If we get back no solutions, we know that
136-
// we've proven the negation. (This is called the "negation as failure" property,
137-
// and it relies on our solver being complete -- i.e., if there is a solution,
138-
// we'll find it, or at least return ambiguous.)
139-
let mut existential_env = Env::default().with_coherence_mode(env.is_in_coherence_mode());
140-
let universal_to_existential: Substitution = env
141-
.variables()
142-
.iter()
143-
.map(|v| {
144-
assert!(v.is_universal());
145-
let v1 = existential_env.fresh_existential(v.kind());
146-
(v, v1)
147-
})
148-
.collect();
149-
150-
let existential_assumptions = universal_to_existential.apply(&assumptions);
151-
let existential_goal = universal_to_existential.apply(&goal);
152-
153-
let cs = formality_prove::prove(
154-
self.decls,
155-
&existential_env,
156-
existential_assumptions.to_wcs(),
157-
&existential_goal,
125+
let cs = is_definitely_not_proveable(
126+
env,
127+
&assumptions,
128+
goal.clone(),
129+
|env, assumptions, goal| formality_prove::prove(self.decls, env, &assumptions, &goal),
158130
);
159-
160-
match cs.into_set() {
161-
Ok(proofs) => {
162-
bail!(
163-
"failed to disprove\n {goal:?}\ngiven\n {assumptions:?}\ngot\n{proofs:?}"
164-
)
165-
}
166-
Err(err) => {
167-
tracing::debug!("Proved not goal, error = {err}");
168-
return Ok(());
169-
}
131+
let cs = cs.into_set()?;
132+
if cs.iter().any(|c| c.unconditionally_true()) {
133+
return Ok(());
170134
}
135+
136+
bail!("failed to prove {goal:?} given {assumptions:?}, got {cs:?}")
171137
}
172138
}

crates/formality-check/src/traits.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use anyhow::bail;
12
use fn_error_context::context;
3+
use formality_core::Set;
24
use formality_prove::Env;
35
use formality_rust::grammar::{
46
AssociatedTy, AssociatedTyBoundData, Fn, Trait, TraitBoundData, TraitItem, WhereClause,
@@ -31,8 +33,27 @@ impl super::Check<'_> {
3133
Ok(())
3234
}
3335

34-
fn check_trait_items_have_unique_names(&self, _trait_items: &[TraitItem]) -> Fallible<()> {
35-
// FIXME:
36+
fn check_trait_items_have_unique_names(&self, trait_items: &[TraitItem]) -> Fallible<()> {
37+
let mut functions = Set::new();
38+
let mut associated_types = Set::new();
39+
for trait_item in trait_items {
40+
match trait_item {
41+
TraitItem::Fn(f) => {
42+
if !functions.insert(&f.id) {
43+
bail!("the function name `{:?}` is defined multiple times", f.id);
44+
}
45+
}
46+
TraitItem::AssociatedTy(associated_ty) => {
47+
let AssociatedTy { id, .. } = associated_ty;
48+
if !associated_types.insert(id) {
49+
bail!(
50+
"the associated type name `{:?}` is defined multiple times",
51+
id
52+
);
53+
}
54+
}
55+
}
56+
}
3657
Ok(())
3758
}
3859

Lines changed: 8 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,20 @@
11
use fn_error_context::context;
2-
use formality_core::Upcast;
32
use formality_prove::Env;
4-
use formality_rust::{
5-
grammar::{WhereClause, WhereClauseData},
6-
prove::ToWcs,
7-
};
8-
use formality_types::grammar::{ConstData, Fallible, Parameter, Relation, TraitRef};
3+
use formality_rust::{grammar::WhereClause, prove::ToWcs};
4+
use formality_types::grammar::{Fallible, Wcs};
95

106
impl super::Check<'_> {
7+
#[context("prove_where_clauses_well_formed({where_clauses:?})")]
118
pub(crate) fn prove_where_clauses_well_formed(
129
&self,
1310
env: &Env,
1411
assumptions: impl ToWcs,
1512
where_clauses: &[WhereClause],
1613
) -> Fallible<()> {
17-
for where_clause in where_clauses {
18-
self.prove_where_clause_well_formed(env, &assumptions, where_clause)?;
19-
}
20-
Ok(())
21-
}
22-
23-
#[context("prove_where_clause_well_formed({where_clause:?})")]
24-
// FIXME(oli-obk): figure out why is this a function and not a `judgment_fn`.
25-
fn prove_where_clause_well_formed(
26-
&self,
27-
in_env: &Env,
28-
assumptions: impl ToWcs,
29-
where_clause: &WhereClause,
30-
) -> Fallible<()> {
31-
match where_clause.data() {
32-
WhereClauseData::IsImplemented(self_ty, trait_id, parameters) => self
33-
.prove_trait_ref_well_formed(
34-
in_env,
35-
assumptions,
36-
trait_id.with(self_ty, parameters),
37-
),
38-
WhereClauseData::AliasEq(alias_ty, ty) => {
39-
self.prove_parameter_well_formed(in_env, &assumptions, alias_ty)?;
40-
self.prove_parameter_well_formed(in_env, &assumptions, ty)
41-
}
42-
WhereClauseData::Outlives(a, b) => {
43-
self.prove_parameter_well_formed(in_env, &assumptions, a)?;
44-
self.prove_parameter_well_formed(in_env, assumptions, b)
45-
}
46-
WhereClauseData::ForAll(binder) => {
47-
let mut e = in_env.clone();
48-
let wc = e.instantiate_universally(binder);
49-
self.prove_where_clause_well_formed(&e, assumptions, &wc)
50-
}
51-
WhereClauseData::TypeOfConst(ct, ty) => {
52-
match ct.data() {
53-
ConstData::Value(_, t) => {
54-
self.prove_goal(in_env, &assumptions, Relation::equals(ty, t))?
55-
}
56-
ConstData::Variable(_) => {}
57-
}
58-
// FIXME(oli-obk): prove that there is no `TypeOfConst` bound for a different type.
59-
self.prove_parameter_well_formed(in_env, &assumptions, ct.clone())?;
60-
self.prove_parameter_well_formed(in_env, assumptions, ty.clone())
61-
}
62-
}
63-
}
64-
65-
fn prove_parameter_well_formed(
66-
&self,
67-
env: &Env,
68-
assumptions: impl ToWcs,
69-
parameter: impl Upcast<Parameter>,
70-
) -> Fallible<()> {
71-
let parameter: Parameter = parameter.upcast();
72-
self.prove_goal(env, assumptions, parameter.well_formed())
73-
}
74-
75-
fn prove_trait_ref_well_formed(
76-
&self,
77-
env: &Env,
78-
assumptions: impl ToWcs,
79-
trait_ref: impl Upcast<TraitRef>,
80-
) -> Fallible<()> {
81-
let trait_ref: TraitRef = trait_ref.upcast();
82-
self.prove_goal(env, assumptions, trait_ref.well_formed())?;
83-
Ok(())
14+
let wcs: Wcs = where_clauses
15+
.into_iter()
16+
.flat_map(|wc| wc.well_formed().into_iter())
17+
.collect();
18+
self.prove_goal(env, assumptions, wcs)
8419
}
8520
}

crates/formality-core/src/test_util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use std::fmt::{Debug, Display};
4141
/// with `"src/file.rs:LL:CC"`. This makes error messages resilient against changes to the source code.
4242
pub fn normalize_paths(s: impl Display) -> String {
4343
let s = s.to_string();
44-
let re = regex::Regex::new(r"\([^():]+.rs:\d+:\d+\)").unwrap();
44+
let re = regex::Regex::new(r"\([^()]+.rs:\d+:\d+\)").unwrap();
4545
re.replace_all(&s, "(src/file.rs:LL:CC)").to_string()
4646
}
4747

crates/formality-core/src/visit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::sync::Arc;
22

33
use crate::{collections::Set, language::Language, variable::CoreVariable};
44

5-
pub trait CoreVisit<L: Language> {
5+
pub trait CoreVisit<L: Language>: std::fmt::Debug {
66
/// Extract the list of free variables (for the purposes of this function, defined by `Variable::is_free`).
77
/// The list may contain duplicates and must be in a determinstic order (though the order itself isn't important).
88
fn free_variables(&self) -> Vec<CoreVariable<L>>;

crates/formality-prove/src/decls.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use formality_core::{set, Set, Upcast};
22
use formality_macros::term;
33
use formality_types::grammar::{
44
AdtId, AliasName, AliasTy, Binder, Parameter, Predicate, Relation, TraitId, TraitRef, Ty, Wc,
5-
Wcs, PR,
5+
Wcs,
66
};
77

88
#[term]
@@ -175,13 +175,14 @@ impl TraitDecl {
175175

176176
fn is_supertrait(self_var: &Parameter, wc: &Wc) -> bool {
177177
match wc.data() {
178-
formality_types::grammar::WcData::PR(PR::Predicate(Predicate::IsImplemented(
178+
formality_types::grammar::WcData::Predicate(Predicate::IsImplemented(
179179
trait_ref,
180-
))) => trait_ref.parameters[0] == *self_var,
181-
formality_types::grammar::WcData::PR(PR::Relation(Relation::Outlives(a, _))) => {
180+
)) => trait_ref.parameters[0] == *self_var,
181+
formality_types::grammar::WcData::Relation(Relation::Outlives(a, _)) => {
182182
*a == *self_var
183183
}
184-
formality_types::grammar::WcData::PR(_) => false,
184+
formality_types::grammar::WcData::Predicate(_) => false,
185+
formality_types::grammar::WcData::Relation(_) => false,
185186
formality_types::grammar::WcData::ForAll(binder) => {
186187
is_supertrait(self_var, binder.peek())
187188
}

crates/formality-prove/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ mod prove;
88
pub use decls::*;
99
pub use prove::prove;
1010
pub use prove::Constraints;
11-
pub use prove::Env;
11+
pub use prove::{is_definitely_not_proveable, may_not_be_provable, negation_via_failure};
12+
pub use prove::{Bias, Env};
1213

1314
#[cfg(test)]
1415
mod test;

0 commit comments

Comments
 (0)