Skip to content

Commit 6e7e017

Browse files
authored
Merge pull request #123 from nikomatsakis/negative-trait-impls
Negative trait impls
2 parents 89f28d4 + 6e5a569 commit 6e7e017

28 files changed

+592
-59
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ rustc_private=true
1111
[dev-dependencies]
1212
pretty_assertions = "1.3.0"
1313
expect-test = "1.4.0"
14+
formality-macros = { version = "0.1.0", path = "crates/formality-macros" }
15+
formality-core = { version = "0.1.0", path = "crates/formality-core" }
1416

1517
[dependencies]
1618
anyhow = "1"

crates/formality-check/src/coherence.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use anyhow::bail;
2+
use fn_error_context::context;
23
use formality_prove::Env;
34
use formality_rust::grammar::{Crate, TraitImpl};
45
use formality_types::{
@@ -38,10 +39,21 @@ impl Check<'_> {
3839
Ok(())
3940
}
4041

41-
fn orphan_check(&self, _impl_a: &TraitImpl) -> Fallible<()> {
42-
Ok(())
42+
#[context("orphan_check({impl_a:?})")]
43+
fn orphan_check(&self, impl_a: &TraitImpl) -> Fallible<()> {
44+
let mut env = Env::default();
45+
46+
let a = env.instantiate_universally(&impl_a.binder);
47+
let trait_ref = a.trait_ref();
48+
49+
self.prove_goal(
50+
&env.with_coherence_mode(true),
51+
&a.where_clauses,
52+
trait_ref.is_local(),
53+
)
4354
}
4455

56+
#[tracing::instrument(level = "Debug", skip(self))]
4557
fn overlap_check(&self, impl_a: &TraitImpl, impl_b: &TraitImpl) -> Fallible<()> {
4658
let mut env = Env::default();
4759

@@ -53,7 +65,7 @@ impl Check<'_> {
5365

5466
// If the parameters from the two impls cannot be equal, then they do not overlap.
5567
if let Ok(()) = self.prove_not_goal(
56-
&env,
68+
&env.with_coherence_mode(true),
5769
(&a.where_clauses, &b.where_clauses),
5870
Wcs::all_eq(&trait_ref_a.parameters, &trait_ref_b.parameters),
5971
) {
@@ -62,7 +74,7 @@ impl Check<'_> {
6274

6375
// If we can disprove the where clauses, then they do not overlap.
6476
if let Ok(()) = self.prove_not_goal(
65-
&env,
77+
&env.with_coherence_mode(true),
6678
Wcs::all_eq(&trait_ref_a.parameters, &trait_ref_b.parameters),
6779
(&a.where_clauses, &b.where_clauses),
6880
) {

crates/formality-check/src/impls.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ impl super::Check<'_> {
3535

3636
self.prove_goal(&env, &where_clauses, trait_ref.is_implemented())?;
3737

38+
self.prove_not_goal(&env, &where_clauses, trait_ref.not_implemented())?;
39+
3840
let trait_decl = self.program.trait_named(&trait_ref.trait_id)?;
3941
let TraitBoundData {
4042
where_clauses: _,

crates/formality-check/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ impl Check<'_> {
114114
// we've proven the negation. (This is called the "negation as failure" property,
115115
// and it relies on our solver being complete -- i.e., if there is a solution,
116116
// we'll find it, or at least return ambiguous.)
117-
let mut existential_env = Env::default();
117+
let mut existential_env = Env::default().with_coherence_mode(env.is_in_coherence_mode());
118118
let universal_to_existential: Substitution = env
119119
.variables()
120120
.iter()

crates/formality-prove/src/decls.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ use formality_types::{
33
cast::Upcast,
44
collections::Set,
55
grammar::{
6-
AliasName, AliasTy, Binder, Parameter, Predicate, Relation, TraitId, TraitRef, Ty, Wc, Wcs,
7-
PR,
6+
AdtId, AliasName, AliasTy, Binder, Parameter, Predicate, Relation, TraitId, TraitRef, Ty,
7+
Wc, Wcs, PR,
88
},
9+
set,
910
};
1011

1112
#[term]
@@ -16,12 +17,22 @@ pub struct Decls {
1617
pub neg_impl_decls: Vec<NegImplDecl>,
1718
pub alias_eq_decls: Vec<AliasEqDecl>,
1819
pub alias_bound_decls: Vec<AliasBoundDecl>,
20+
pub local_trait_ids: Set<TraitId>,
21+
pub local_adt_ids: Set<AdtId>,
1922
}
2023

2124
impl Decls {
2225
/// Max size used in unit tests that are not stress testing maximum size.
2326
pub const DEFAULT_MAX_SIZE: usize = 222;
2427

28+
pub fn is_local_trait_id(&self, trait_id: &TraitId) -> bool {
29+
self.local_trait_ids.contains(trait_id)
30+
}
31+
32+
pub fn is_local_adt_id(&self, adt_id: &AdtId) -> bool {
33+
self.local_adt_ids.contains(adt_id)
34+
}
35+
2536
pub fn impl_decls<'s>(&'s self, trait_id: &'s TraitId) -> impl Iterator<Item = &'s ImplDecl> {
2637
self.impl_decls
2738
.iter()
@@ -78,6 +89,8 @@ impl Decls {
7889
neg_impl_decls: vec![],
7990
alias_eq_decls: vec![],
8091
alias_bound_decls: vec![],
92+
local_trait_ids: set![],
93+
local_adt_ids: set![],
8194
}
8295
}
8396
}

crates/formality-prove/src/prove.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
mod constraints;
22
mod env;
3+
mod is_local;
34
mod minimize;
45
mod prove_after;
56
mod prove_eq;
67
mod prove_normalize;
78
mod prove_via;
89
mod prove_wc;
910
mod prove_wc_list;
10-
mod zip;
11+
mod combinators;
1112

1213
pub use constraints::Constraints;
1314
use formality_types::{cast::Upcast, collections::Set, grammar::Wcs, set, visit::Visit};

crates/formality-prove/src/prove/zip.rs renamed to crates/formality-prove/src/prove/combinators.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,32 @@ where
3737
})
3838
.collect()
3939
}
40+
41+
pub fn for_all<A, C>(
42+
decls: &Decls,
43+
env: &Env,
44+
context: &C,
45+
a: &[A],
46+
op: &impl Fn(Decls, Env, C, A) -> Set<Constraints>,
47+
) -> Set<Constraints>
48+
where
49+
A: Term,
50+
C: Term,
51+
{
52+
if a.is_empty() {
53+
return set![Constraints::none(env.upcast())];
54+
}
55+
56+
let a0 = a[0].clone();
57+
let a_remaining: Vec<A> = a[1..].to_vec();
58+
op(decls.clone(), env.clone(), context.clone(), a0)
59+
.into_iter()
60+
.flat_map(|c1| {
61+
let context = c1.substitution().apply(context);
62+
let a_remaining = c1.substitution().apply(&a_remaining);
63+
for_all(&decls, c1.env(), &context, &a_remaining, op)
64+
.into_iter()
65+
.map(move |c2| c1.seq(c2))
66+
})
67+
.collect()
68+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ impl Constraints {
3333
Self::from(env, v)
3434
}
3535

36+
pub fn with_coherence_mode(self, b: bool) -> Self {
37+
Self {
38+
env: self.env.with_coherence_mode(b),
39+
..self
40+
}
41+
}
42+
3643
pub fn unconditionally_true(&self) -> bool {
3744
self.known_true && self.substitution.is_empty()
3845
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,24 @@ use formality_types::{
1313
#[derive(Default, Debug, Clone, Hash, Ord, Eq, PartialEq, PartialOrd)]
1414
pub struct Env {
1515
variables: Vec<Variable>,
16+
coherence_mode: bool,
1617
}
1718

1819
impl Env {
1920
pub fn only_universal_variables(&self) -> bool {
2021
self.variables.iter().all(|v| v.is_universal())
2122
}
23+
24+
pub fn is_in_coherence_mode(&self) -> bool {
25+
self.coherence_mode
26+
}
27+
28+
pub fn with_coherence_mode(&self, b: bool) -> Env {
29+
Env {
30+
coherence_mode: b,
31+
..self.clone()
32+
}
33+
}
2234
}
2335

2436
cast_impl!(Env);
@@ -199,6 +211,7 @@ impl Env {
199211
.iter()
200212
.map(|&v| vs.map_var(v).unwrap_or(v))
201213
.collect(),
214+
coherence_mode: self.coherence_mode,
202215
}
203216
}
204217

0 commit comments

Comments
 (0)