Skip to content

Commit acad3a4

Browse files
committed
extend goal builder for partial quantification
1 parent 413e582 commit acad3a4

File tree

2 files changed

+127
-13
lines changed

2 files changed

+127
-13
lines changed

chalk-solve/src/goal_builder.rs

Lines changed: 124 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use cast::CastTo;
55
use chalk_ir::cast::Cast;
66
use chalk_ir::cast::Caster;
77
use chalk_ir::*;
8+
use chalk_rust_ir::ToParameter;
89
use fold::shift::Shift;
910
use fold::Fold;
1011
use interner::{HasInterner, Interner};
@@ -18,14 +19,18 @@ impl<'i, I: Interner> GoalBuilder<'i, I> {
1819
GoalBuilder { db }
1920
}
2021

22+
/// Returns the database within the goal builder.
2123
pub(crate) fn db(&self) -> &'i dyn RustIrDatabase<I> {
2224
self.db
2325
}
2426

27+
/// Returns the interner within the goal builder.
2528
pub(crate) fn interner(&self) -> &'i I {
2629
self.db.interner()
2730
}
2831

32+
/// Creates a goal that ensures all of the goals from the `goals`
33+
/// iterator are met (e.g., `goals[0] && ... && goals[N]`).
2934
pub(crate) fn all<GS, G>(&mut self, goals: GS) -> Goal<I>
3035
where
3136
GS: IntoIterator<Item = G>,
@@ -34,6 +39,8 @@ impl<'i, I: Interner> GoalBuilder<'i, I> {
3439
Goal::all(self.interner(), goals.into_iter().casted(self.interner()))
3540
}
3641

42+
/// Creates a goal `clauses => goal`. The clauses are given as an iterator
43+
/// and the goal is returned via the contained closure.
3744
pub(crate) fn implies<CS, C, G>(
3845
&mut self,
3946
clauses: CS,
@@ -51,54 +58,161 @@ impl<'i, I: Interner> GoalBuilder<'i, I> {
5158
.intern(self.interner())
5259
}
5360

61+
/// Given a bound value `binders` like `<P0..Pn> V`,
62+
/// creates a goal `forall<Q0..Qn> { G }` where
63+
/// the goal `G` is created by invoking a helper
64+
/// function `body`.
65+
///
66+
/// # Parameters to `body`
67+
///
68+
/// `body` will be invoked with:
69+
///
70+
/// * the goal builder `self`
71+
/// * the substitution `Q0..Qn`
72+
/// * the bound value `[P0..Pn => Q0..Qn] V` instantiated
73+
/// with the substitution
74+
/// * the value `passthru`, appropriately shifted so that
75+
/// any debruijn indices within account for the new binder
76+
///
77+
/// # Why is `body` a function and not a closure?
78+
///
79+
/// This is to ensure that `body` doesn't accidentally reference
80+
/// values from the environment whose debruijn indices do not
81+
/// account for the new binder being created.
5482
pub(crate) fn forall<G, B, P>(
5583
&mut self,
5684
binders: &Binders<B>,
5785
passthru: P,
58-
body: fn(&mut Self, &B, P::Result) -> G,
86+
body: fn(&mut Self, Substitution<I>, B::Result, P::Result) -> G,
5987
) -> Goal<I>
6088
where
6189
B: Fold<I> + HasInterner<Interner = I>,
6290
P: Fold<I>,
6391
B::Result: std::fmt::Debug,
6492
G: CastTo<Goal<I>>,
6593
{
66-
self.quantified(QuantifierKind::ForAll, binders, passthru, body)
94+
self.partially_quantified(QuantifierKind::ForAll, binders, &[], passthru, body)
6795
}
6896

97+
/// Like [`GoalBuilder::forall`], but for a `exists<Q0..Qn> { G }` goal.
6998
pub(crate) fn exists<G, B, P>(
7099
&mut self,
71100
binders: &Binders<B>,
72101
passthru: P,
73-
body: fn(&mut Self, &B, P::Result) -> G,
102+
body: fn(&mut Self, Substitution<I>, B::Result, P::Result) -> G,
74103
) -> Goal<I>
75104
where
76105
B: Fold<I> + HasInterner<Interner = I>,
77106
P: Fold<I>,
78107
B::Result: std::fmt::Debug,
79108
G: CastTo<Goal<I>>,
80109
{
81-
self.quantified(QuantifierKind::Exists, binders, passthru, body)
110+
self.partially_quantified(QuantifierKind::Exists, binders, &[], passthru, body)
82111
}
83112

84-
pub(crate) fn quantified<G, B, P>(
113+
/// Like `[GoalBuilder::forall`], except that it also takes
114+
/// a (partial) substitution `S0..Sm` that provides some
115+
/// suffix of the values for the bound value `<P0..Pn> V`.
116+
///
117+
/// The resulting goal will be `forall<Q0..Q(n-m)> { G }`,
118+
/// and the resulting substitution for the bound value `V`
119+
/// will be `[Q0, .., Q(n-m), S0, .., Sm]`.
120+
///
121+
/// This is useful for associated items within traits and impls:
122+
/// the binders on such items contain both the binders from the trait
123+
/// and impl as well as from the associated item itself. Here, the
124+
/// "partial substitution" would be the values from the trait/impl.
125+
pub(crate) fn partially_forall<G, B, P>(
126+
&mut self,
127+
binders: &Binders<B>,
128+
partial_substitution: &Substitution<I>,
129+
passthru: P,
130+
body: fn(&mut Self, Substitution<I>, B::Result, P::Result) -> G,
131+
) -> Goal<I>
132+
where
133+
B: Fold<I> + HasInterner<Interner = I>,
134+
P: Fold<I>,
135+
B::Result: std::fmt::Debug,
136+
G: CastTo<Goal<I>>,
137+
{
138+
self.partially_quantified(
139+
QuantifierKind::ForAll,
140+
binders,
141+
partial_substitution.parameters(self.interner()),
142+
passthru,
143+
body,
144+
)
145+
}
146+
147+
/// Like [`GoalBuilder::partially_forall`], but for a `exists` goal.
148+
pub(crate) fn partially_exists<G, B, P>(
149+
&mut self,
150+
binders: &Binders<B>,
151+
partial_substitution: &Substitution<I>,
152+
passthru: P,
153+
body: fn(&mut Self, Substitution<I>, B::Result, P::Result) -> G,
154+
) -> Goal<I>
155+
where
156+
B: Fold<I> + HasInterner<Interner = I>,
157+
P: Fold<I>,
158+
B::Result: std::fmt::Debug,
159+
G: CastTo<Goal<I>>,
160+
{
161+
self.partially_quantified(
162+
QuantifierKind::Exists,
163+
binders,
164+
partial_substitution.parameters(self.interner()),
165+
passthru,
166+
body,
167+
)
168+
}
169+
170+
/// A combined helper functon for the various methods
171+
/// to create `forall` and `exists` goals. See:
172+
///
173+
/// * [`GoalBuilder::forall`]
174+
/// * [`GoalBuilder::partially_forall`]
175+
///
176+
/// for details.
177+
pub(crate) fn partially_quantified<G, B, P>(
85178
&mut self,
86179
quantifier_kind: QuantifierKind,
87180
binders: &Binders<B>,
181+
partial_substitution: &[Parameter<I>],
88182
passthru: P,
89-
body: fn(&mut Self, &B, P::Result) -> G,
183+
body: fn(&mut Self, Substitution<I>, B::Result, P::Result) -> G,
90184
) -> Goal<I>
91185
where
92186
B: Fold<I> + HasInterner<Interner = I>,
93187
P: Fold<I>,
94188
B::Result: std::fmt::Debug,
95189
G: CastTo<Goal<I>>,
96190
{
97-
let bound_goal = binders.map_ref(|bound_value| {
191+
let interner = self.interner();
192+
assert!(binders.binders.len() >= partial_substitution.len());
193+
let split_point = binders.binders.len() - partial_substitution.len();
194+
let (quantified_binders, _) = binders.binders.split_at(split_point);
195+
let combined_values: Substitution<I> = Substitution::from(
196+
interner,
197+
quantified_binders
198+
.iter()
199+
.zip(0..)
200+
.map(|p| p.to_parameter(interner))
201+
.chain(
202+
// Note that the values from the partial substitution must be shifted
203+
// in by one to account for the new binder we are introducing.
204+
partial_substitution.iter().map(|p| p.shifted_in(interner)),
205+
),
206+
);
207+
let bound_goal = {
208+
let bound_value = binders.substitute(interner, &combined_values);
98209
let passthru_shifted = passthru.shifted_in(self.interner());
99-
let result = body(self, bound_value, passthru_shifted);
100-
result.cast(self.interner())
101-
});
210+
let result = body(self, combined_values, bound_value, passthru_shifted);
211+
Binders {
212+
binders: quantified_binders.to_vec(),
213+
value: result.cast(self.interner()),
214+
}
215+
};
102216
GoalData::Quantified(quantifier_kind, bound_goal).intern(self.interner())
103217
}
104218
}

chalk-solve/src/wf.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ where
187187
// We make a goal like...
188188
//
189189
// forall<T> { ... }
190-
let wg_goal = gb.forall(&struct_data, (), |gb, (fields, where_clauses), ()| {
190+
let wg_goal = gb.forall(&struct_data, (), |gb, _, (fields, where_clauses), ()| {
191191
let interner = gb.interner();
192192

193193
// (FromEnv(T: Eq) => ...)
@@ -243,7 +243,7 @@ where
243243
let goal = gb.forall(
244244
&impl_fields,
245245
&impl_datum.associated_ty_value_ids,
246-
|gb, &(trait_ref, where_clauses), associated_ty_value_ids| {
246+
|gb, _, (trait_ref, where_clauses), associated_ty_value_ids| {
247247
let interner = gb.interner();
248248

249249
// if (WC) { ... }
@@ -297,7 +297,7 @@ where
297297
.into_iter()
298298
.map(|ty| ty.well_formed().cast(interner))
299299
.chain(assoc_ty_goals)
300-
.chain(Some(trait_ref.clone().well_formed().cast(interner)));
300+
.chain(Some(trait_ref.well_formed().cast(interner)));
301301

302302
gb.all(goals)
303303
},

0 commit comments

Comments
 (0)