Skip to content

Commit f319d6d

Browse files
committed
make associated type value goals standalone
For a GAT like ``` impl<T> Foo<T> { type Bar<'a> = ..} ``` Instead of constructing nested quantifiers like ``` forall<T> { if (..) { stuff && forall<'a> { more_stuff } } } ``` we will now build separate goals ``` forall<T> { if (...) { stuff } } && forall<T, 'a> { if (...) { more_stuff } } ``` This both removes the need for the 'partial' forall methods in GoalBuilder butalso means we would be able to give better error messages in rustc at somepoint, since we can test "is this impl header WF" separately from "is this associated type value WF" etc.
1 parent f099845 commit f319d6d

File tree

2 files changed

+157
-139
lines changed

2 files changed

+157
-139
lines changed

chalk-rust-ir/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl<I: Interner> ImplDatum<I> {
4040
}
4141
}
4242

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

chalk-solve/src/wf.rs

Lines changed: 156 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -230,62 +230,22 @@ where
230230
let interner = self.db.interner();
231231
let impl_datum = self.db.impl_datum(impl_id);
232232

233-
if !impl_datum.is_positive() {
234-
return Ok(());
235-
}
236-
237-
let impl_fields = impl_datum
238-
.binders
239-
.map_ref(|v| (&v.trait_ref, &v.where_clauses));
240-
241-
let mut gb = GoalBuilder::new(self.db);
242-
// forall<P0...Pn> {...}
243-
let goal = gb.forall(
244-
&impl_fields,
245-
&impl_datum.associated_ty_value_ids,
246-
|gb, impl_substitution, (trait_ref, where_clauses), associated_ty_value_ids| {
247-
let interner = gb.interner();
248-
249-
// if (WC && input types are well formed) { ... }
250-
let impl_wf = Self::impl_wf_environment(interner, &where_clauses, &trait_ref);
251-
gb.implies(impl_wf, |gb| {
252-
let db = gb.db();
253-
254-
let assoc_ty_goals = associated_ty_value_ids
255-
.iter()
256-
.filter_map(|&id| Self::compute_assoc_ty_goal(db, &impl_substitution, id));
257-
258-
// We retrieve all the input types of the where clauses appearing on the trait impl,
259-
// e.g. in:
260-
// ```
261-
// impl<T, K> Foo for (T, K) where T: Iterator<Item = (HashSet<K>, Vec<Box<T>>)> { ... }
262-
// ```
263-
// we would retrieve `HashSet<K>`, `Box<T>`, `Vec<Box<T>>`, `(HashSet<K>, Vec<Box<T>>)`.
264-
// We will have to prove that these types are well-formed (e.g. an additional `K: Hash`
265-
// bound would be needed here).
266-
let mut input_types = Vec::new();
267-
where_clauses.fold(interner, &mut input_types);
268-
269-
// Things to prove well-formed: input types of the where-clauses, projection types
270-
// appearing in the header, associated type values, and of course the trait ref.
271-
debug!("verify_trait_impl: input_types={:?}", input_types);
272-
let goals = input_types
273-
.into_iter()
274-
.map(|ty| ty.well_formed().cast(interner))
275-
.chain(assoc_ty_goals)
276-
.chain(Some(trait_ref.clone().well_formed().cast(interner)));
277-
278-
gb.all(goals)
279-
})
280-
},
233+
let impl_goal = Goal::all(
234+
interner,
235+
impl_header_wf_goal(self.db, impl_id).into_iter().chain(
236+
impl_datum
237+
.associated_ty_value_ids
238+
.iter()
239+
.filter_map(|&id| compute_assoc_ty_goal(self.db, id)),
240+
),
281241
);
282242

283-
debug!("WF trait goal: {:?}", goal);
243+
debug!("WF trait goal: {:?}", impl_goal);
284244

285245
let is_legal = match self
286246
.solver_choice
287247
.into_solver()
288-
.solve(self.db, &goal.into_closed_goal(interner))
248+
.solve(self.db, &impl_goal.into_closed_goal(interner))
289249
{
290250
Some(sol) => sol.is_unique(),
291251
None => false,
@@ -298,99 +258,157 @@ where
298258
Err(WfError::IllFormedTraitImpl(trait_ref.trait_id))
299259
}
300260
}
261+
}
301262

302-
/// Creates the conditions that an impl (and its contents of an impl)
303-
/// can assume to be true when proving that it is well-formed.
304-
fn impl_wf_environment<'i>(
305-
interner: &'i I,
306-
where_clauses: &'i [QuantifiedWhereClause<I>],
307-
trait_ref: &'i TraitRef<I>,
308-
) -> impl Iterator<Item = ProgramClause<I>> + 'i {
309-
// if (WC) { ... }
310-
let wc = where_clauses
311-
.iter()
312-
.cloned()
313-
.map(move |qwc| qwc.into_from_env_goal(interner).cast(interner));
314-
315-
// We retrieve all the input types of the type on which we implement the trait: we will
316-
// *assume* that these types are well-formed, e.g. we will be able to derive that
317-
// `K: Hash` holds without writing any where clause.
318-
//
319-
// Example:
320-
// ```
321-
// struct HashSet<K> where K: Hash { ... }
322-
//
323-
// impl<K> Foo for HashSet<K> {
324-
// // Inside here, we can rely on the fact that `K: Hash` holds
325-
// }
326-
// ```
327-
let mut header_input_types = Vec::new();
328-
trait_ref.fold(interner, &mut header_input_types);
329-
330-
let types_wf = header_input_types
331-
.into_iter()
332-
.map(move |ty| ty.into_from_env_goal(interner).cast(interner));
263+
fn impl_header_wf_goal<I: Interner>(
264+
db: &dyn RustIrDatabase<I>,
265+
impl_id: ImplId<I>,
266+
) -> Option<Goal<I>> {
267+
let impl_datum = db.impl_datum(impl_id);
333268

334-
wc.chain(types_wf)
269+
if !impl_datum.is_positive() {
270+
return None;
335271
}
336272

337-
/// Associated type values are special because they can be parametric (independently of
338-
/// the impl), so we issue a special goal which is quantified using the binders of the
339-
/// associated type value, for example in:
340-
///
341-
/// ```ignore
342-
/// trait Foo {
343-
/// type Item<'a>: Clone where Self: 'a
344-
/// }
345-
///
346-
/// impl<T> Foo for Box<T> {
347-
/// type Item<'a> = Box<&'a T>;
348-
/// }
349-
/// ```
350-
///
351-
/// we would issue the following subgoal: `forall<'a> { WellFormed(Box<&'a T>) }`.
352-
///
353-
/// Note that there is no binder for `T` in the above: the goal we
354-
/// generate is expected to be exected in the context of the
355-
/// larger WF goal for the impl, which already has such a
356-
/// binder. So the entire goal for the impl might be:
357-
///
358-
/// ```ignore
359-
/// forall<T> {
360-
/// WellFormed(Box<T>) /* this comes from the impl, not this routine */,
361-
/// forall<'a> { WellFormed(Box<&'a T>) },
362-
/// }
363-
/// ```
364-
fn compute_assoc_ty_goal(
365-
db: &dyn RustIrDatabase<I>,
366-
impl_substitution: &Substitution<I>,
367-
assoc_ty_id: AssociatedTyValueId<I>,
368-
) -> Option<Goal<I>> {
369-
let mut gb = GoalBuilder::new(db);
370-
let assoc_ty = &db.associated_ty_value(assoc_ty_id);
371-
372-
// Create `forall<'a> { .. }`
373-
Some(gb.partially_forall(
374-
&assoc_ty.value.map_ref(|v| &v.ty),
375-
impl_substitution,
376-
assoc_ty_id,
377-
|gb, assoc_ty_substitution, value_ty, assoc_ty_id| {
378-
let interner = gb.interner();
379-
let db = gb.db();
380-
381-
// Hmm, because `Arc<AssociatedTyValue>` does not implement `Fold`, we can't pass this value through,
382-
// just the id, so we have to fetch `assoc_ty` from the database again.
383-
// Implementing `Fold` for `AssociatedTyValue` doesn't *quite* seem right though, as that
384-
// would result in a deep clone, and the value is inert. We could do some more refatoring
385-
// (move the `Arc` behind a newtype, for example) to fix this, but for now doesn't
386-
// seem worth it.
387-
let assoc_ty = &db.associated_ty_value(assoc_ty_id);
388-
389-
let (_, projection) = db.impl_parameters_and_projection_from_associated_ty_value(
273+
let impl_fields = impl_datum
274+
.binders
275+
.map_ref(|v| (&v.trait_ref, &v.where_clauses));
276+
277+
let mut gb = GoalBuilder::new(db);
278+
// forall<P0...Pn> {...}
279+
Some(
280+
gb.forall(&impl_fields, (), |gb, _, (trait_ref, where_clauses), ()| {
281+
let interner = gb.interner();
282+
283+
// if (WC && input types are well formed) { ... }
284+
let impl_wf = impl_wf_environment(interner, &where_clauses, &trait_ref);
285+
gb.implies(impl_wf, |gb| {
286+
// We retrieve all the input types of the where clauses appearing on the trait impl,
287+
// e.g. in:
288+
// ```
289+
// impl<T, K> Foo for (T, K) where T: Iterator<Item = (HashSet<K>, Vec<Box<T>>)> { ... }
290+
// ```
291+
// we would retrieve `HashSet<K>`, `Box<T>`, `Vec<Box<T>>`, `(HashSet<K>, Vec<Box<T>>)`.
292+
// We will have to prove that these types are well-formed (e.g. an additional `K: Hash`
293+
// bound would be needed here).
294+
let mut input_types = Vec::new();
295+
where_clauses.fold(interner, &mut input_types);
296+
297+
// Things to prove well-formed: input types of the where-clauses, projection types
298+
// appearing in the header, associated type values, and of course the trait ref.
299+
debug!("verify_trait_impl: input_types={:?}", input_types);
300+
let goals = input_types
301+
.into_iter()
302+
.map(|ty| ty.well_formed().cast(interner))
303+
.chain(Some(trait_ref.clone().well_formed().cast(interner)));
304+
305+
gb.all::<_, Goal<I>>(goals)
306+
})
307+
}),
308+
)
309+
}
310+
311+
/// Creates the conditions that an impl (and its contents of an impl)
312+
/// can assume to be true when proving that it is well-formed.
313+
fn impl_wf_environment<'i, I: Interner>(
314+
interner: &'i I,
315+
where_clauses: &'i [QuantifiedWhereClause<I>],
316+
trait_ref: &'i TraitRef<I>,
317+
) -> impl Iterator<Item = ProgramClause<I>> + 'i {
318+
// if (WC) { ... }
319+
let wc = where_clauses
320+
.iter()
321+
.cloned()
322+
.map(move |qwc| qwc.into_from_env_goal(interner).cast(interner));
323+
324+
// We retrieve all the input types of the type on which we implement the trait: we will
325+
// *assume* that these types are well-formed, e.g. we will be able to derive that
326+
// `K: Hash` holds without writing any where clause.
327+
//
328+
// Example:
329+
// ```
330+
// struct HashSet<K> where K: Hash { ... }
331+
//
332+
// impl<K> Foo for HashSet<K> {
333+
// // Inside here, we can rely on the fact that `K: Hash` holds
334+
// }
335+
// ```
336+
let mut header_input_types = Vec::new();
337+
trait_ref.fold(interner, &mut header_input_types);
338+
339+
let types_wf = header_input_types
340+
.into_iter()
341+
.map(move |ty| ty.into_from_env_goal(interner).cast(interner));
342+
343+
wc.chain(types_wf)
344+
}
345+
346+
/// Associated type values are special because they can be parametric (independently of
347+
/// the impl), so we issue a special goal which is quantified using the binders of the
348+
/// associated type value, for example in:
349+
///
350+
/// ```ignore
351+
/// trait Foo {
352+
/// type Item<'a>: Clone where Self: 'a
353+
/// }
354+
///
355+
/// impl<T> Foo for Box<T> {
356+
/// type Item<'a> = Box<&'a T>;
357+
/// }
358+
/// ```
359+
///
360+
/// we would issue the following subgoal: `forall<'a> { WellFormed(Box<&'a T>) }`.
361+
///
362+
/// Note that there is no binder for `T` in the above: the goal we
363+
/// generate is expected to be exected in the context of the
364+
/// larger WF goal for the impl, which already has such a
365+
/// binder. So the entire goal for the impl might be:
366+
///
367+
/// ```ignore
368+
/// forall<T> {
369+
/// WellFormed(Box<T>) /* this comes from the impl, not this routine */,
370+
/// forall<'a> { WellFormed(Box<&'a T>) },
371+
/// }
372+
/// ```
373+
fn compute_assoc_ty_goal<I: Interner>(
374+
db: &dyn RustIrDatabase<I>,
375+
assoc_ty_id: AssociatedTyValueId<I>,
376+
) -> Option<Goal<I>> {
377+
let mut gb = GoalBuilder::new(db);
378+
let assoc_ty = &db.associated_ty_value(assoc_ty_id);
379+
380+
// Create `forall<T, 'a> { .. }`
381+
Some(gb.forall(
382+
&assoc_ty.value.map_ref(|v| &v.ty),
383+
assoc_ty_id,
384+
|gb, assoc_ty_substitution, value_ty, assoc_ty_id| {
385+
let interner = gb.interner();
386+
let db = gb.db();
387+
388+
// Hmm, because `Arc<AssociatedTyValue>` does not implement `Fold`, we can't pass this value through,
389+
// just the id, so we have to fetch `assoc_ty` from the database again.
390+
// Implementing `Fold` for `AssociatedTyValue` doesn't *quite* seem right though, as that
391+
// would result in a deep clone, and the value is inert. We could do some more refatoring
392+
// (move the `Arc` behind a newtype, for example) to fix this, but for now doesn't
393+
// seem worth it.
394+
let assoc_ty = &db.associated_ty_value(assoc_ty_id);
395+
396+
let (impl_parameters, projection) = db
397+
.impl_parameters_and_projection_from_associated_ty_value(
390398
&assoc_ty_substitution.parameters(interner),
391399
assoc_ty,
392400
);
393401

402+
// If (/* impl WF environment */) { ... }
403+
let impl_id = assoc_ty.impl_id;
404+
let impl_datum = &db.impl_datum(impl_id);
405+
let ImplDatumBound {
406+
trait_ref: impl_trait_ref,
407+
where_clauses: impl_where_clauses,
408+
} = impl_datum.binders.substitute(interner, impl_parameters);
409+
let impl_wf_clauses =
410+
impl_wf_environment(interner, &impl_where_clauses, &impl_trait_ref);
411+
gb.implies(impl_wf_clauses, |gb| {
394412
// Get the bounds and where clauses from the trait
395413
// declaration, substituted appropriately.
396414
//
@@ -446,7 +464,7 @@ where
446464
gb.all::<_, Goal<I>>(wf_goals.chain(bound_goals))
447465
},
448466
)
449-
},
450-
))
451-
}
467+
})
468+
},
469+
))
452470
}

0 commit comments

Comments
 (0)