Skip to content

Commit 99906c6

Browse files
committed
implement is_local judgments and integrate
1 parent 31f219f commit 99906c6

21 files changed

+384
-126
lines changed

crates/formality-prove/src/decls.rs

Lines changed: 2 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use formality_types::{
33
cast::Upcast,
44
collections::Set,
55
grammar::{
6-
AdtId, AliasName, AliasTy, Binder, Parameter, ParameterData, Predicate, Relation,
7-
RigidName, TraitId, TraitRef, Ty, TyData, Variable, Wc, Wcs, PR,
6+
AdtId, AliasName, AliasTy, Binder, Parameter, Predicate, Relation, TraitId, TraitRef, Ty,
7+
Wc, Wcs, PR,
88
},
99
set,
1010
};
@@ -25,79 +25,6 @@ impl Decls {
2525
/// Max size used in unit tests that are not stress testing maximum size.
2626
pub const DEFAULT_MAX_SIZE: usize = 222;
2727

28-
pub fn is_local_trait_ref(&self, trait_ref: &TraitRef) -> bool {
29-
// From https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html:
30-
//
31-
// Given `impl<P1..=Pn> Trait<T1..=Tn> for T0`, an impl is valid only if at least one of the following is true:
32-
//
33-
// - `Trait` is a local trait
34-
// - All of
35-
// - At least one of the types `T0..=Tn` must be a local type. Let `Ti` be the
36-
// first such type.
37-
// - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti` (excluding
38-
// `Ti`)
39-
//
40-
// Given the following definitions:
41-
//
42-
// Covered Type: A type which appears as a parameter to another type. For example,
43-
// `T` is uncovered, but the `T` in `Vec<T>` is covered. This is only relevant for
44-
// type parameters.
45-
//
46-
// Fundamental Type: A type for which you cannot add a blanket impl backwards
47-
// compatibly. This includes `&`, `&mut`, and `Box`. Any time a type `T` is
48-
// considered local, `&T`, `&mut T`, and `Box<T>` are also considered local.
49-
// Fundamental types cannot cover other types. Any time the term "covered type" is
50-
// used, `&T`, `&mut T`, and `Box<T>` are not considered covered.
51-
//
52-
// Local Type: A struct, enum, or union which was defined in the current crate.
53-
// This is not affected by type parameters. `struct Foo` is considered local, but
54-
// `Vec<Foo>` is not. `LocalType<ForeignType>` is local. Type aliases and trait
55-
// aliases do not affect locality.
56-
self.is_local_trait_id(&trait_ref.trait_id) || {
57-
if let Some(first_local) = trait_ref
58-
.parameters
59-
.iter()
60-
.position(|p| self.is_local_parameter(p))
61-
{
62-
let parameters_before = &trait_ref.parameters[..first_local];
63-
!parameters_before
64-
.iter()
65-
.any(|p| self.may_have_downstream_type(p))
66-
} else {
67-
false
68-
}
69-
}
70-
}
71-
72-
pub fn is_local_parameter(&self, parameter: impl Upcast<Parameter>) -> bool {
73-
let parameter: Parameter = parameter.upcast();
74-
match parameter.data() {
75-
ParameterData::Ty(t) => match t {
76-
TyData::RigidTy(r) => {
77-
if self.is_fundamental(&r.name) {
78-
r.parameters.iter().all(|p| self.is_local_parameter(p))
79-
} else {
80-
match &r.name {
81-
RigidName::AdtId(a) => self.is_local_adt_id(a),
82-
RigidName::ScalarId(_) => false,
83-
RigidName::Ref(_) => unreachable!("refs are fundamental"),
84-
RigidName::Tuple(_) => todo!(),
85-
RigidName::FnPtr(_) => false,
86-
RigidName::FnDef(_) => todo!(),
87-
}
88-
}
89-
}
90-
TyData::AliasTy(_) => todo!(),
91-
TyData::PredicateTy(_) => todo!(),
92-
TyData::Variable(v) => match v {
93-
Variable::PlaceholderVar(_) | Variable::InferenceVar(_) => false,
94-
Variable::BoundVar(_) => todo!(),
95-
},
96-
},
97-
ParameterData::Lt(_) => false,
98-
}
99-
}
100-
10128
pub fn is_local_trait_id(&self, trait_id: &TraitId) -> bool {
10229
self.local_trait_ids.contains(trait_id)
10330
}
@@ -106,25 +33,6 @@ impl Decls {
10633
self.local_adt_ids.contains(adt_id)
10734
}
10835

109-
pub fn is_fundamental(&self, name: &RigidName) -> bool {
110-
// Fundamental Type: A type for which you cannot add a blanket impl backwards
111-
// compatibly. This includes `&`, `&mut`, and `Box`. Any time a type `T` is
112-
// considered local, `&T`, `&mut T`, and `Box<T>` are also considered local.
113-
// Fundamental types cannot cover other types. Any time the term "covered type" is
114-
// used, `&T`, `&mut T`, and `Box<T>` are not considered covered.
115-
116-
match name {
117-
RigidName::AdtId(_) => false, // FIXME
118-
119-
RigidName::Ref(_) => true,
120-
121-
RigidName::ScalarId(_)
122-
| RigidName::Tuple(_)
123-
| RigidName::FnPtr(_)
124-
| RigidName::FnDef(_) => false,
125-
}
126-
}
127-
12836
pub fn impl_decls<'s>(&'s self, trait_id: &'s TraitId) -> impl Iterator<Item = &'s ImplDecl> {
12937
self.impl_decls
13038
.iter()

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: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ impl Env {
2121
self.variables.iter().all(|v| v.is_universal())
2222
}
2323

24-
pub fn is_coherence_mode(&self) -> bool {
24+
pub fn is_in_coherence_mode(&self) -> bool {
2525
self.coherence_mode
2626
}
2727

28-
pub fn coherence_mode(self) -> Env {
28+
pub fn with_coherence_mode(&self, b: bool) -> Env {
2929
Env {
30-
coherence_mode: true,
31-
..self
30+
coherence_mode: b,
31+
..self.clone()
3232
}
3333
}
3434
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
use formality_types::{
2+
collections::Set,
3+
grammar::{Lt, Parameter, RigidName, RigidTy, TraitRef, TyData, Variable, Wcs},
4+
judgment_fn, set,
5+
};
6+
7+
use crate::{
8+
decls::Decls,
9+
prove::{combinators::for_all, prove_normalize::prove_normalize, Constraints},
10+
Env,
11+
};
12+
13+
// From https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html:
14+
//
15+
// Given `impl<P1..=Pn> Trait<T1..=Tn> for T0`, an impl is valid only if at least one of the following is true:
16+
//
17+
// - `Trait` is a local trait
18+
// - All of
19+
// - At least one of the types `T0..=Tn` must be a local type. Let `Ti` be the
20+
// first such type.
21+
// - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti` (excluding
22+
// `Ti`)
23+
//
24+
// Given the following definitions:
25+
//
26+
// Covered Type: A type which appears as a parameter to another type. For example,
27+
// `T` is uncovered, but the `T` in `Vec<T>` is covered. This is only relevant for
28+
// type parameters.
29+
//
30+
// Fundamental Type: A type for which you cannot add a blanket impl backwards
31+
// compatibly. This includes `&`, `&mut`, and `Box`. Any time a type `T` is
32+
// considered local, `&T`, `&mut T`, and `Box<T>` are also considered local.
33+
// Fundamental types cannot cover other types. Any time the term "covered type" is
34+
// used, `&T`, `&mut T`, and `Box<T>` are not considered covered.
35+
//
36+
// Local Type: A struct, enum, or union which was defined in the current crate.
37+
// This is not affected by type parameters. `struct Foo` is considered local, but
38+
// `Vec<Foo>` is not. `LocalType<ForeignType>` is local. Type aliases and trait
39+
// aliases do not affect locality.
40+
41+
/// True if `goal` may be remote. This is
42+
pub fn may_be_remote(decls: Decls, env: Env, assumptions: Wcs, goal: TraitRef) -> Set<Constraints> {
43+
assert!(env.is_in_coherence_mode());
44+
45+
let c = is_local_trait_ref(decls, &env, assumptions, goal);
46+
47+
if c.is_empty() {
48+
// Cannot possibly be local, so always remote.
49+
return set![Constraints::none(env)];
50+
}
51+
52+
if c.iter().any(Constraints::unconditionally_true) {
53+
// If this is unconditionally known to be local, then it is never remote.
54+
return set![];
55+
}
56+
57+
// Otherwise it is ambiguous
58+
set![Constraints::none(env).ambiguous()]
59+
}
60+
61+
judgment_fn! {
62+
pub fn is_local_trait_ref(
63+
decls: Decls,
64+
env: Env,
65+
assumptions: Wcs,
66+
goal: TraitRef,
67+
) => Constraints {
68+
debug(goal, assumptions, env, decls)
69+
70+
(
71+
(if decls.is_local_trait_id(&goal.trait_id))
72+
--- ("local trait")
73+
(is_local_trait_ref(decls, env, _assumptions, goal) => Constraints::none(env))
74+
)
75+
76+
(
77+
(0 .. goal.parameters.len() => i)
78+
(is_local_parameter(&decls, &env, &assumptions, &goal.parameters[i]) => c1)
79+
(let assumptions = c1.substitution().apply(&assumptions))
80+
(let goal = c1.substitution().apply(&goal))
81+
(for_all(&decls, &env, &assumptions, &goal.parameters[..i], &not_downstream) => c2)
82+
--- ("local parameter")
83+
(is_local_trait_ref(decls, env, assumptions, goal) => c1.seq(c2))
84+
)
85+
}
86+
}
87+
88+
judgment_fn! {
89+
fn not_downstream(
90+
decls: Decls,
91+
env: Env,
92+
assumptions: Wcs,
93+
parameter: Parameter,
94+
) => Constraints {
95+
debug(parameter, assumptions, env, decls)
96+
97+
(
98+
(for_all(&decls, &env, &assumptions, &parameters, &not_downstream) => c)
99+
--- ("rigid")
100+
(not_downstream(decls, env, assumptions, RigidTy { name: _, parameters }) => c)
101+
)
102+
103+
(
104+
--- ("lifetime")
105+
(not_downstream(_decls, env, _assumptions, _l: Lt) => Constraints::none(env))
106+
)
107+
108+
(
109+
--- ("type variable")
110+
(not_downstream(_decls, env, _assumptions, TyData::Variable(Variable::InferenceVar(_))) => Constraints::none(env).ambiguous())
111+
)
112+
}
113+
}
114+
115+
judgment_fn! {
116+
fn is_local_parameter(
117+
decls: Decls,
118+
env: Env,
119+
assumptions: Wcs,
120+
goal: Parameter,
121+
) => Constraints {
122+
debug(goal, assumptions, env, decls)
123+
124+
assert(env.is_in_coherence_mode())
125+
126+
// If we can normalize `goal` to something else, check if that normalized form is local.
127+
(
128+
(prove_normalize(&decls, env.with_coherence_mode(false), &assumptions, goal) => (c1, p))
129+
(let c1 = c1.with_coherence_mode(true))
130+
(let assumptions = c1.substitution().apply(&assumptions))
131+
(is_local_parameter(&decls, c1.env(), assumptions, p) => c2)
132+
--- ("local parameter")
133+
(is_local_parameter(decls, env, assumptions, goal) => c1.seq(c2))
134+
)
135+
136+
// Fundamental types are local if all their arguments are local.
137+
(
138+
(if is_fundamental(&decls, &name))
139+
(for_all(&decls, &env, &assumptions, &parameters, &is_local_parameter) => c) // FIXME: should be `is_local_parameter`
140+
--- ("fundamental rigid type")
141+
(is_local_parameter(decls, env, assumptions, RigidTy { name, parameters }) => c)
142+
)
143+
144+
// ADTs are local if they were declared in this crate.
145+
(
146+
(if decls.is_local_adt_id(&a))
147+
--- ("local rigid type")
148+
(is_local_parameter(decls, env, _assumptions, RigidTy { name: RigidName::AdtId(a), parameters: _ }) => Constraints::none(env))
149+
)
150+
151+
// Inference variables might or might not be local, depending on how they are instantiated.
152+
(
153+
--- ("existential variable")
154+
(is_local_parameter(_decls, env, _assumptions, TyData::Variable(Variable::InferenceVar(_))) => Constraints::none(env).ambiguous())
155+
)
156+
}
157+
}
158+
159+
fn is_fundamental(_decls: &Decls, name: &RigidName) -> bool {
160+
// From https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html:
161+
//
162+
// Fundamental Type: A type for which you cannot add a blanket impl backwards
163+
// compatibly. This includes `&`, `&mut`, and `Box`. Any time a type `T` is
164+
// considered local, `&T`, `&mut T`, and `Box<T>` are also considered local.
165+
// Fundamental types cannot cover other types. Any time the term "covered type" is
166+
// used, `&T`, `&mut T`, and `Box<T>` are not considered covered.
167+
168+
match name {
169+
RigidName::AdtId(_) => false, // FIXME
170+
171+
RigidName::Ref(_) => true,
172+
173+
RigidName::ScalarId(_)
174+
| RigidName::Tuple(_)
175+
| RigidName::FnPtr(_)
176+
| RigidName::FnDef(_) => false,
177+
}
178+
}

0 commit comments

Comments
 (0)