Skip to content

Commit b344839

Browse files
committed
Auto merge of #681 - detrumi:opaque-super-bounds, r=jackh726
Generate clauses for super traits of opaque bounds Implements #677 Turns out that push_dyn_ty_impl_clauses can also be used for opaque types.
2 parents d6a4ce3 + 29ba9ee commit b344839

File tree

6 files changed

+162
-121
lines changed

6 files changed

+162
-121
lines changed

chalk-ir/src/could_match.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ where
3333
struct MatchZipper<'i, I> {
3434
interner: &'i I,
3535
db: &'i dyn UnificationDatabase<I>,
36-
};
36+
}
3737

3838
impl<'i, I: Interner> Zipper<'i, I> for MatchZipper<'i, I> {
3939
fn zip_tys(&mut self, variance: Variance, a: &Ty<I>, b: &Ty<I>) -> Fallible<()> {

chalk-solve/src/clauses.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod dyn_ty;
1919
mod env_elaborator;
2020
mod generalize;
2121
pub mod program_clauses;
22+
mod super_traits;
2223

2324
// yields the types "contained" in `app_ty`
2425
fn constituent_types<I: Interner>(db: &dyn RustIrDatabase<I>, ty: &TyKind<I>) -> Vec<Ty<I>> {

chalk-solve/src/clauses/dyn_ty.rs

Lines changed: 9 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
use rustc_hash::FxHashSet;
2-
31
use super::{builder::ClauseBuilder, generalize};
42
use crate::RustIrDatabase;
5-
use chalk_ir::{
6-
cast::Cast, fold::shift::Shift, interner::Interner, Binders, BoundVar, DebruijnIndex, TraitId,
7-
TraitRef, Ty, TyKind, WhereClause,
8-
};
3+
use chalk_ir::{cast::Cast, interner::Interner, Ty, TyKind, WhereClause};
94

105
/// If the self type `S` of an `Implemented` goal is a `dyn trait` type, we wish
116
/// to generate program-clauses that indicates that it implements its own
@@ -67,123 +62,20 @@ pub(super) fn build_dyn_self_ty_clauses<I: Interner>(
6762
.cloned()
6863
.substitute(interner, &[self_ty.clone().cast(interner)]);
6964

70-
builder.push_binders(qwc, |builder, wc| match &wc {
65+
builder.push_binders(qwc, |builder, bound| match &bound {
7166
// For the implemented traits, we need to elaborate super traits and add where clauses from the trait
7267
WhereClause::Implemented(trait_ref) => {
73-
push_dyn_ty_impl_clauses(db, builder, trait_ref.clone())
68+
super::super_traits::push_trait_super_clauses(
69+
builder.db,
70+
builder,
71+
trait_ref.clone(),
72+
)
7473
}
75-
// Associated item bindings are just taken as facts (?)
76-
WhereClause::AliasEq(_) => builder.push_fact(wc),
74+
// FIXME: Associated item bindings are just taken as facts (?)
75+
WhereClause::AliasEq(_) => builder.push_fact(bound),
7776
WhereClause::LifetimeOutlives(..) => {}
7877
WhereClause::TypeOutlives(..) => {}
7978
});
8079
}
8180
});
8281
}
83-
84-
/// Generate `Implemented` clauses for a `dyn Trait` type. We need to generate
85-
/// `Implemented` clauses for all super traits, and for each trait we require
86-
/// its where clauses. (See #203.)
87-
fn push_dyn_ty_impl_clauses<I: Interner>(
88-
db: &dyn RustIrDatabase<I>,
89-
builder: &mut ClauseBuilder<'_, I>,
90-
trait_ref: TraitRef<I>,
91-
) {
92-
let interner = db.interner();
93-
// We have some `dyn Trait`, and some `trait SuperTrait: WC`
94-
// which is a super trait of `Trait` (including actually
95-
// just being the same trait); then we want to push
96-
// `Implemented(dyn Trait: SuperTrait) :- WC`.
97-
98-
let super_trait_refs =
99-
super_traits(db, trait_ref.trait_id).substitute(interner, &trait_ref.substitution);
100-
101-
for q_super_trait_ref in super_trait_refs {
102-
builder.push_binders(q_super_trait_ref.clone(), |builder, super_trait_ref| {
103-
let trait_datum = db.trait_datum(super_trait_ref.trait_id);
104-
let wc = trait_datum
105-
.where_clauses()
106-
.cloned()
107-
.substitute(interner, &super_trait_ref.substitution);
108-
builder.push_clause(super_trait_ref, wc);
109-
});
110-
}
111-
}
112-
113-
pub fn super_traits<I: Interner>(
114-
db: &dyn RustIrDatabase<I>,
115-
trait_id: TraitId<I>,
116-
) -> Binders<Vec<Binders<TraitRef<I>>>> {
117-
let interner = db.interner();
118-
let mut seen_traits = FxHashSet::default();
119-
let trait_datum = db.trait_datum(trait_id);
120-
let trait_ref = Binders::empty(
121-
db.interner(),
122-
TraitRef {
123-
trait_id,
124-
substitution: trait_datum
125-
.binders
126-
.identity_substitution(interner)
127-
.shifted_in(interner),
128-
},
129-
);
130-
let mut trait_refs = Vec::new();
131-
go(db, trait_ref, &mut seen_traits, &mut trait_refs);
132-
133-
fn go<I: Interner>(
134-
db: &dyn RustIrDatabase<I>,
135-
trait_ref: Binders<TraitRef<I>>,
136-
seen_traits: &mut FxHashSet<TraitId<I>>,
137-
trait_refs: &mut Vec<Binders<TraitRef<I>>>,
138-
) {
139-
let interner = db.interner();
140-
let trait_id = trait_ref.skip_binders().trait_id;
141-
// Avoid cycles
142-
if !seen_traits.insert(trait_id) {
143-
return;
144-
}
145-
trait_refs.push(trait_ref.clone());
146-
let trait_datum = db.trait_datum(trait_id);
147-
let super_trait_refs = trait_datum
148-
.binders
149-
.map_ref(|td| {
150-
td.where_clauses
151-
.iter()
152-
.filter_map(|qwc| {
153-
qwc.as_ref().filter_map(|wc| match wc {
154-
WhereClause::Implemented(tr) => {
155-
let self_ty = tr.self_type_parameter(db.interner());
156-
157-
// We're looking for where clauses
158-
// of the form `Self: Trait`. That's
159-
// ^1.0 because we're one binder in.
160-
if self_ty.bound_var(db.interner())
161-
!= Some(BoundVar::new(DebruijnIndex::ONE, 0))
162-
{
163-
return None;
164-
}
165-
Some(tr.clone())
166-
}
167-
WhereClause::AliasEq(_) => None,
168-
WhereClause::LifetimeOutlives(..) => None,
169-
WhereClause::TypeOutlives(..) => None,
170-
})
171-
})
172-
.collect::<Vec<_>>()
173-
})
174-
// we skip binders on the trait_ref here and add them to the binders
175-
// on the trait ref in the loop below. We could probably avoid this if
176-
// we could turn the `Binders<Vec<>>` into a `Vec<Binders<>>` easily.
177-
.substitute(db.interner(), &trait_ref.skip_binders().substitution);
178-
for q_super_trait_ref in super_trait_refs {
179-
// So now we need to combine the binders of trait_ref with the
180-
// binders of super_trait_ref.
181-
let actual_binders = Binders::new(trait_ref.binders.clone(), q_super_trait_ref);
182-
let q_super_trait_ref = actual_binders.fuse_binders(interner);
183-
go(db, q_super_trait_ref, seen_traits, trait_refs);
184-
}
185-
seen_traits.remove(&trait_id);
186-
}
187-
188-
Binders::new(trait_datum.binders.binders.clone(), trait_refs)
189-
}

chalk-solve/src/clauses/program_clauses.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,20 @@ impl<I: Interner> ToProgramClauses<I> for OpaqueTyDatum<I> {
195195

196196
let substitution = Substitution::from1(interner, alias_placeholder_ty);
197197
for bound in opaque_ty_bound.bounds {
198-
// Implemented(!T<..>: Bound).
199198
let bound_with_placeholder_ty = bound.substitute(interner, &substitution);
200-
builder.push_binders(bound_with_placeholder_ty, |builder, bound| {
201-
builder.push_fact(bound);
199+
builder.push_binders(bound_with_placeholder_ty, |builder, bound| match &bound {
200+
// For the implemented traits, we need to elaborate super traits and add where clauses from the trait
201+
WhereClause::Implemented(trait_ref) => {
202+
super::super_traits::push_trait_super_clauses(
203+
builder.db,
204+
builder,
205+
trait_ref.clone(),
206+
)
207+
}
208+
// FIXME: Associated item bindings are just taken as facts (?)
209+
WhereClause::AliasEq(_) => builder.push_fact(bound),
210+
WhereClause::LifetimeOutlives(..) => {}
211+
WhereClause::TypeOutlives(..) => {}
202212
});
203213
}
204214
});
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use rustc_hash::FxHashSet;
2+
3+
use super::builder::ClauseBuilder;
4+
use crate::RustIrDatabase;
5+
use chalk_ir::{
6+
fold::shift::Shift, interner::Interner, Binders, BoundVar, DebruijnIndex, TraitId, TraitRef,
7+
WhereClause,
8+
};
9+
10+
/// Generate `Implemented` clauses for `dyn Trait` and opaque types. We need to generate
11+
/// `Implemented` clauses for all super traits, and for each trait we require
12+
/// its where clauses. (See #203.)
13+
pub(super) fn push_trait_super_clauses<I: Interner>(
14+
db: &dyn RustIrDatabase<I>,
15+
builder: &mut ClauseBuilder<'_, I>,
16+
trait_ref: TraitRef<I>,
17+
) {
18+
let interner = db.interner();
19+
// Given`trait SuperTrait: WC`, which is a super trait
20+
// of `Trait` (including actually just being the same trait);
21+
// then we want to push
22+
// - for `dyn Trait`:
23+
// `Implemented(dyn Trait: SuperTrait) :- WC`.
24+
// - for placeholder `!T` of `opaque type T: Trait = HiddenTy`:
25+
// `Implemented(!T: SuperTrait) :- WC`
26+
27+
let super_trait_refs =
28+
super_traits(db, trait_ref.trait_id).substitute(interner, &trait_ref.substitution);
29+
30+
for q_super_trait_ref in super_trait_refs {
31+
builder.push_binders(q_super_trait_ref.clone(), |builder, super_trait_ref| {
32+
let trait_datum = db.trait_datum(super_trait_ref.trait_id);
33+
let wc = trait_datum
34+
.where_clauses()
35+
.cloned()
36+
.substitute(interner, &super_trait_ref.substitution);
37+
builder.push_clause(super_trait_ref, wc);
38+
});
39+
}
40+
}
41+
42+
pub fn super_traits<I: Interner>(
43+
db: &dyn RustIrDatabase<I>,
44+
trait_id: TraitId<I>,
45+
) -> Binders<Vec<Binders<TraitRef<I>>>> {
46+
let interner = db.interner();
47+
let mut seen_traits = FxHashSet::default();
48+
let trait_datum = db.trait_datum(trait_id);
49+
let trait_ref = Binders::empty(
50+
db.interner(),
51+
TraitRef {
52+
trait_id,
53+
substitution: trait_datum
54+
.binders
55+
.identity_substitution(interner)
56+
.shifted_in(interner),
57+
},
58+
);
59+
let mut trait_refs = Vec::new();
60+
go(db, trait_ref, &mut seen_traits, &mut trait_refs);
61+
62+
fn go<I: Interner>(
63+
db: &dyn RustIrDatabase<I>,
64+
trait_ref: Binders<TraitRef<I>>,
65+
seen_traits: &mut FxHashSet<TraitId<I>>,
66+
trait_refs: &mut Vec<Binders<TraitRef<I>>>,
67+
) {
68+
let interner = db.interner();
69+
let trait_id = trait_ref.skip_binders().trait_id;
70+
// Avoid cycles
71+
if !seen_traits.insert(trait_id) {
72+
return;
73+
}
74+
trait_refs.push(trait_ref.clone());
75+
let trait_datum = db.trait_datum(trait_id);
76+
let super_trait_refs = trait_datum
77+
.binders
78+
.map_ref(|td| {
79+
td.where_clauses
80+
.iter()
81+
.filter_map(|qwc| {
82+
qwc.as_ref().filter_map(|wc| match wc {
83+
WhereClause::Implemented(tr) => {
84+
let self_ty = tr.self_type_parameter(db.interner());
85+
86+
// We're looking for where clauses
87+
// of the form `Self: Trait`. That's
88+
// ^1.0 because we're one binder in.
89+
if self_ty.bound_var(db.interner())
90+
!= Some(BoundVar::new(DebruijnIndex::ONE, 0))
91+
{
92+
return None;
93+
}
94+
Some(tr.clone())
95+
}
96+
WhereClause::AliasEq(_) => None,
97+
WhereClause::LifetimeOutlives(..) => None,
98+
WhereClause::TypeOutlives(..) => None,
99+
})
100+
})
101+
.collect::<Vec<_>>()
102+
})
103+
// we skip binders on the trait_ref here and add them to the binders
104+
// on the trait ref in the loop below. We could probably avoid this if
105+
// we could turn the `Binders<Vec<>>` into a `Vec<Binders<>>` easily.
106+
.substitute(db.interner(), &trait_ref.skip_binders().substitution);
107+
for q_super_trait_ref in super_trait_refs {
108+
// So now we need to combine the binders of trait_ref with the
109+
// binders of super_trait_ref.
110+
let actual_binders = Binders::new(trait_ref.binders.clone(), q_super_trait_ref);
111+
let q_super_trait_ref = actual_binders.fuse_binders(interner);
112+
go(db, q_super_trait_ref, seen_traits, trait_refs);
113+
}
114+
seen_traits.remove(&trait_id);
115+
}
116+
117+
Binders::new(trait_datum.binders.binders.clone(), trait_refs)
118+
}

tests/test/opaque_types.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,23 @@ fn opaque_auto_traits_indirect() {
263263
}
264264
}
265265
}
266+
267+
#[test]
268+
fn opaque_super_trait() {
269+
test! {
270+
program {
271+
trait Base {}
272+
trait Super where Self: Base {}
273+
impl Base for () {}
274+
impl Super for () {}
275+
276+
opaque type Opaque: Super = ();
277+
}
278+
279+
goal {
280+
Opaque: Base
281+
} yields {
282+
"Unique"
283+
}
284+
}
285+
}

0 commit comments

Comments
 (0)