Skip to content

Commit 039fc90

Browse files
authored
Merge pull request #361 from nikomatsakis/goal-builder
Goal builder
2 parents 0b3f90d + 32f4dc8 commit 039fc90

File tree

8 files changed

+503
-320
lines changed

8 files changed

+503
-320
lines changed

chalk-ir/src/cast.rs

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ reflexive_impl!(for(I: Interner) TraitRef<I>);
8080
reflexive_impl!(for(I: Interner) DomainGoal<I>);
8181
reflexive_impl!(for(I: Interner) Goal<I>);
8282
reflexive_impl!(for(I: Interner) WhereClause<I>);
83+
reflexive_impl!(for(I: Interner) ProgramClause<I>);
8384

8485
impl<I: Interner> CastTo<WhereClause<I>> for TraitRef<I> {
8586
fn cast_to(self, _interner: &I) -> WhereClause<I> {

chalk-ir/src/fold/shift.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub trait Shift<I: Interner>: Fold<I, I> {
2121
fn shifted_out_to(&self, interner: &I, target_binder: DebruijnIndex) -> Fallible<Self::Result>;
2222
}
2323

24-
impl<T: Fold<I, I> + Eq, I: Interner> Shift<I> for T {
24+
impl<T: Fold<I, I>, I: Interner> Shift<I> for T {
2525
fn shifted_in(&self, interner: &I) -> Self::Result {
2626
self.shifted_in_from(interner, DebruijnIndex::ONE)
2727
}

chalk-ir/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ impl<I: Interner> Ty<I> {
196196
WellFormed::Ty(self.clone())
197197
}
198198

199+
/// Creates a domain goal `FromEnv(T)` where `T` is this type.
200+
pub fn into_from_env_goal(self, interner: &I) -> DomainGoal<I> {
201+
self.from_env().cast(interner)
202+
}
203+
199204
/// If this is a `TyData::BoundVar(d)`, returns `Some(d)` else `None`.
200205
pub fn bound(&self, interner: &I) -> Option<BoundVar> {
201206
if let TyData::BoundVar(bv) = self.data(interner) {

chalk-rust-ir/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ pub enum LangItem {}
2222
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2323
pub struct AssociatedTyValueId<I: Interner>(pub I::DefId);
2424

25+
chalk_ir::id_fold!(AssociatedTyValueId);
26+
2527
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2628
pub struct ImplDatum<I: Interner> {
2729
pub polarity: Polarity,
@@ -40,7 +42,7 @@ impl<I: Interner> ImplDatum<I> {
4042
}
4143
}
4244

43-
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
45+
#[derive(Clone, Debug, PartialEq, Eq, Hash, HasInterner, Fold)]
4446
pub struct ImplDatumBound<I: Interner> {
4547
pub trait_ref: TraitRef<I>,
4648
pub where_clauses: Vec<QuantifiedWhereClause<I>>,

chalk-solve/src/coherence/solve.rs

+85-64
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::coherence::{CoherenceError, CoherenceSolver};
22
use crate::ext::*;
3-
use crate::Solution;
3+
use crate::{goal_builder::GoalBuilder, Solution};
44
use chalk_ir::cast::*;
55
use chalk_ir::fold::shift::Shift;
66
use chalk_ir::interner::Interner;
@@ -34,7 +34,7 @@ impl<I: Interner> CoherenceSolver<'_, I> {
3434
// the other. Note that specialization can only run one way - if both
3535
// specialization checks return *either* true or false, that's an error.
3636
if !self.disjoint(lhs, rhs) {
37-
match (self.specializes(lhs, rhs), self.specializes(rhs, lhs)) {
37+
match (self.specializes(l_id, r_id), self.specializes(r_id, l_id)) {
3838
(true, false) => record_specialization(l_id, r_id),
3939
(false, true) => record_specialization(r_id, l_id),
4040
(_, _) => {
@@ -145,23 +145,56 @@ impl<I: Interner> CoherenceSolver<'_, I> {
145145
result
146146
}
147147

148-
// Test for specialization.
148+
// Creates a goal which, if provable, means "more special" impl specializes the "less special" one.
149149
//
150-
// If this test succeeds, the second impl specializes the first.
150+
// # General rule
151151
//
152-
// Example lowering:
152+
// Given the more special impl:
153153
//
154-
// more: impl<T: Clone> Foo for Vec<T>
155-
// less: impl<U: Clone> Foo for U
154+
// ```ignore
155+
// impl<P0..Pn> SomeTrait<T1..Tm> for T0 where WC_more
156+
// ```
156157
//
158+
// and less special impl
159+
//
160+
// ```ignore
161+
// impl<Q0..Qo> SomeTrait<U1..Um> for U0 where WC_less
162+
// ```
163+
//
164+
// create the goal:
165+
//
166+
// ```ignore
167+
// forall<P0..Pn> {
168+
// if (WC_more) {}
169+
// exists<Q0..Qo> {
170+
// T0 = U0, ..., Tm = Um,
171+
// WC_less
172+
// }
173+
// }
174+
// }
175+
// ```
176+
//
177+
// # Example
178+
//
179+
// Given:
180+
//
181+
// * more: `impl<T: Clone> Foo for Vec<T>`
182+
// * less: `impl<U: Clone> Foo for U`
183+
//
184+
// Resulting goal:
185+
//
186+
// ```ignore
157187
// forall<T> {
158188
// if (T: Clone) {
159189
// exists<U> {
160190
// Vec<T> = U, U: Clone
161191
// }
162192
// }
163193
// }
164-
fn specializes(&self, less_special: &ImplDatum<I>, more_special: &ImplDatum<I>) -> bool {
194+
// ```
195+
fn specializes(&self, less_special_id: ImplId<I>, more_special_id: ImplId<I>) -> bool {
196+
let more_special = &self.db.impl_datum(more_special_id);
197+
let less_special = &self.db.impl_datum(less_special_id);
165198
debug_heading!(
166199
"specializes(less_special={:#?}, more_special={:#?})",
167200
less_special,
@@ -170,66 +203,54 @@ impl<I: Interner> CoherenceSolver<'_, I> {
170203

171204
let interner = self.db.interner();
172205

173-
// Negative impls cannot specialize.
174-
if !less_special.is_positive() || !more_special.is_positive() {
175-
return false;
176-
}
206+
let gb = &mut GoalBuilder::new(self.db);
177207

178-
// Create parameter equality goals. Note that parameters from
179-
// the "more special" goal have to be "shifted in" across the
180-
// binder for the "less special" impl.
181-
let more_special_params = params(interner, more_special)
182-
.iter()
183-
.map(|p| p.shifted_in(interner));
184-
let less_special_params = params(interner, less_special).iter().cloned();
185-
let params_goals = more_special_params
186-
.zip(less_special_params)
187-
.map(|(a, b)| GoalData::EqGoal(EqGoal { a, b }).intern(interner));
208+
// forall<P0..Pn> { ... }
209+
let goal = gb.forall(
210+
&more_special.binders,
211+
less_special_id,
212+
|gb, _, more_special_impl, less_special_id| {
213+
// if (WC_more) { ... }
214+
gb.implies(more_special_impl.where_clauses.iter().cloned(), |gb| {
215+
let less_special = &gb.db().impl_datum(less_special_id);
188216

189-
// Create less special where clause goals. These reference the parameters
190-
// from the "less special" impl, which are at the same debruijn depth here.
191-
let less_special_wc = less_special
192-
.binders
193-
.value
194-
.where_clauses
195-
.iter()
196-
.cloned()
197-
.casted(interner);
217+
// exists<Q0..Qn> { ... }
218+
gb.exists(
219+
&less_special.binders,
220+
&more_special_impl.trait_ref,
221+
|gb, _, less_special_impl, more_special_trait_ref| {
222+
let interner = gb.interner();
198223

199-
// Create the "more special" where clause goals. These will be
200-
// added as an implication without the "less special" goals in
201-
// scope, no shift required.
202-
let more_special_wc = more_special
203-
.binders
204-
.value
205-
.where_clauses
206-
.iter()
207-
.cloned()
208-
.casted(interner)
209-
.collect();
224+
// T0 = U0, ..., Tm = Um
225+
let params_goals = more_special_trait_ref
226+
.substitution
227+
.parameters(interner)
228+
.iter()
229+
.cloned()
230+
.zip(
231+
less_special_impl
232+
.trait_ref
233+
.substitution
234+
.parameters(interner)
235+
.iter()
236+
.cloned(),
237+
)
238+
.map(|(a, b)| GoalData::EqGoal(EqGoal { a, b }).intern(interner));
210239

211-
// Join all of the goals together:
212-
//
213-
// forall<..more special..> {
214-
// if (<more special wc>) {
215-
// exists<..less special..> {
216-
// ..less special goals..
217-
// ..equality goals..
218-
// }
219-
// }
220-
// }
221-
let goal = Box::new(Goal::all(interner, params_goals.chain(less_special_wc)))
222-
.quantify(
223-
interner,
224-
QuantifierKind::Exists,
225-
less_special.binders.binders.clone(),
226-
)
227-
.implied_by(interner, more_special_wc)
228-
.quantify(
229-
interner,
230-
QuantifierKind::ForAll,
231-
more_special.binders.binders.clone(),
232-
);
240+
// <less_special_wc_goals> = where clauses from the less special impl
241+
let less_special_wc_goals = less_special_impl
242+
.where_clauses
243+
.iter()
244+
.cloned()
245+
.casted(interner);
246+
247+
// <equality_goals> && WC_less
248+
gb.all(params_goals.chain(less_special_wc_goals))
249+
},
250+
)
251+
})
252+
},
253+
);
233254

234255
let canonical_goal = &goal.into_closed_goal(interner);
235256
let result = match self

chalk-solve/src/goal_builder.rs

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
use crate::RustIrDatabase;
2+
use cast::CastTo;
3+
use chalk_ir::cast::Cast;
4+
use chalk_ir::cast::Caster;
5+
use chalk_ir::*;
6+
use chalk_rust_ir::ToParameter;
7+
use fold::shift::Shift;
8+
use fold::Fold;
9+
use interner::{HasInterner, Interner};
10+
11+
pub struct GoalBuilder<'i, I: Interner> {
12+
db: &'i dyn RustIrDatabase<I>,
13+
}
14+
15+
impl<'i, I: Interner> GoalBuilder<'i, I> {
16+
pub fn new(db: &'i dyn RustIrDatabase<I>) -> Self {
17+
GoalBuilder { db }
18+
}
19+
20+
/// Returns the database within the goal builder.
21+
pub fn db(&self) -> &'i dyn RustIrDatabase<I> {
22+
self.db
23+
}
24+
25+
/// Returns the interner within the goal builder.
26+
pub fn interner(&self) -> &'i I {
27+
self.db.interner()
28+
}
29+
30+
/// Creates a goal that ensures all of the goals from the `goals`
31+
/// iterator are met (e.g., `goals[0] && ... && goals[N]`).
32+
pub fn all<GS, G>(&mut self, goals: GS) -> Goal<I>
33+
where
34+
GS: IntoIterator<Item = G>,
35+
G: CastTo<Goal<I>>,
36+
{
37+
Goal::all(self.interner(), goals.into_iter().casted(self.interner()))
38+
}
39+
40+
/// Creates a goal `clauses => goal`. The clauses are given as an iterator
41+
/// and the goal is returned via the contained closure.
42+
pub fn implies<CS, C, G>(&mut self, clauses: CS, goal: impl FnOnce(&mut Self) -> G) -> Goal<I>
43+
where
44+
CS: IntoIterator<Item = C>,
45+
C: CastTo<ProgramClause<I>>,
46+
G: CastTo<Goal<I>>,
47+
{
48+
GoalData::Implies(
49+
clauses.into_iter().casted(self.interner()).collect(),
50+
goal(self).cast(self.interner()),
51+
)
52+
.intern(self.interner())
53+
}
54+
55+
/// Given a bound value `binders` like `<P0..Pn> V`,
56+
/// creates a goal `forall<Q0..Qn> { G }` where
57+
/// the goal `G` is created by invoking a helper
58+
/// function `body`.
59+
///
60+
/// # Parameters to `body`
61+
///
62+
/// `body` will be invoked with:
63+
///
64+
/// * the goal builder `self`
65+
/// * the substitution `Q0..Qn`
66+
/// * the bound value `[P0..Pn => Q0..Qn] V` instantiated
67+
/// with the substitution
68+
/// * the value `passthru`, appropriately shifted so that
69+
/// any debruijn indices within account for the new binder
70+
///
71+
/// # Why is `body` a function and not a closure?
72+
///
73+
/// This is to ensure that `body` doesn't accidentally reference
74+
/// values from the environment whose debruijn indices do not
75+
/// account for the new binder being created.
76+
pub fn forall<G, B, P>(
77+
&mut self,
78+
binders: &Binders<B>,
79+
passthru: P,
80+
body: fn(&mut Self, Substitution<I>, &B, P::Result) -> G,
81+
) -> Goal<I>
82+
where
83+
B: Fold<I> + HasInterner<Interner = I>,
84+
P: Fold<I>,
85+
B::Result: std::fmt::Debug,
86+
G: CastTo<Goal<I>>,
87+
{
88+
self.quantified(QuantifierKind::ForAll, binders, passthru, body)
89+
}
90+
91+
/// Like [`GoalBuilder::forall`], but for a `exists<Q0..Qn> { G }` goal.
92+
pub fn exists<G, B, P>(
93+
&mut self,
94+
binders: &Binders<B>,
95+
passthru: P,
96+
body: fn(&mut Self, Substitution<I>, &B, P::Result) -> G,
97+
) -> Goal<I>
98+
where
99+
B: Fold<I> + HasInterner<Interner = I>,
100+
P: Fold<I>,
101+
B::Result: std::fmt::Debug,
102+
G: CastTo<Goal<I>>,
103+
{
104+
self.quantified(QuantifierKind::Exists, binders, passthru, body)
105+
}
106+
107+
/// A combined helper functon for the various methods
108+
/// to create `forall` and `exists` goals. See:
109+
///
110+
/// * [`GoalBuilder::forall`]
111+
/// * [`GoalBuilder::exists`]
112+
///
113+
/// for details.
114+
fn quantified<G, B, P>(
115+
&mut self,
116+
quantifier_kind: QuantifierKind,
117+
binders: &Binders<B>,
118+
passthru: P,
119+
body: fn(&mut Self, Substitution<I>, &B, P::Result) -> G,
120+
) -> Goal<I>
121+
where
122+
B: Fold<I> + HasInterner<Interner = I>,
123+
P: Fold<I>,
124+
B::Result: std::fmt::Debug,
125+
G: CastTo<Goal<I>>,
126+
{
127+
let interner = self.interner();
128+
129+
// Make an identity mapping `[0 => ^0.0, 1 => ^0.1, ..]`
130+
// and so forth. This substitution is mapping from the `<P0..Pn>` variables
131+
// in `binders` to the corresponding `P0..Pn` variables we're about to
132+
// introduce in the form of a `forall<P0..Pn>` goal. Of course, it's
133+
// actually an identity mapping, since this `forall` will be the innermost
134+
// debruijn binder and so forth, so there's no actual reason to
135+
// *do* the substitution, since it would effectively just be a clone.
136+
let substitution: Substitution<I> = Substitution::from(
137+
interner,
138+
binders
139+
.binders
140+
.iter()
141+
.zip(0..)
142+
.map(|p| p.to_parameter(interner)),
143+
);
144+
145+
// Shift passthru into one level of binder, to account for the `forall<P0..Pn>`
146+
// we are about to introduce.
147+
let passthru_shifted = passthru.shifted_in(self.interner());
148+
149+
// Invoke `body` function, which returns a goal, and wrap that goal in the binders
150+
// from `binders`, and finally a `forall` or `exists` goal.
151+
let bound_goal = binders.map_ref(|bound_value| {
152+
body(self, substitution, bound_value, passthru_shifted).cast(interner)
153+
});
154+
GoalData::Quantified(quantifier_kind, bound_goal).intern(interner)
155+
}
156+
}

0 commit comments

Comments
 (0)