Skip to content

Commit 28cef6f

Browse files
authored
Merge pull request #372 from flodiebold/recursive-solver
Recursive solver
2 parents 2bd2e03 + 2aa90be commit 28cef6f

File tree

26 files changed

+2041
-109
lines changed

26 files changed

+2041
-109
lines changed

chalk-integration/src/lowering.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use chalk_ir::cast::{Cast, Caster};
22
use chalk_ir::interner::ChalkIr;
33
use chalk_ir::{
4-
self, AssocTypeId, BoundVar, DebruijnIndex, ImplId, QuantifiedWhereClauses, StructId,
5-
Substitution, TraitId,
4+
self, AssocTypeId, BoundVar, ClausePriority, DebruijnIndex, ImplId, QuantifiedWhereClauses,
5+
StructId, Substitution, TraitId,
66
};
77
use chalk_parse::ast::*;
88
use chalk_rust_ir as rust_ir;
@@ -1193,6 +1193,7 @@ impl LowerClause for Clause {
11931193
.map(|consequence| chalk_ir::ProgramClauseImplication {
11941194
consequence,
11951195
conditions: conditions.clone(),
1196+
priority: ClausePriority::High,
11961197
})
11971198
.collect::<Vec<_>>();
11981199
Ok(implications)

chalk-ir/src/cast.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ where
187187
ProgramClauseData::Implies(ProgramClauseImplication {
188188
consequence: self.cast(interner),
189189
conditions: Goals::new(interner),
190+
priority: ClausePriority::High,
190191
})
191192
.intern(interner)
192193
}
@@ -201,6 +202,7 @@ where
201202
ProgramClauseData::ForAll(self.map(|bound| ProgramClauseImplication {
202203
consequence: bound.cast(interner),
203204
conditions: Goals::new(interner),
205+
priority: ClausePriority::High,
204206
}))
205207
.intern(interner)
206208
}

chalk-ir/src/fold/boring_impls.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ copy_fold!(DebruijnIndex);
243243
copy_fold!(chalk_engine::TableIndex);
244244
copy_fold!(chalk_engine::TimeStamp);
245245
copy_fold!(());
246+
copy_fold!(ClausePriority);
246247

247248
#[macro_export]
248249
macro_rules! id_fold {

chalk-ir/src/lib.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,14 @@ impl<I: Interner> Ty<I> {
229229
}
230230
}
231231

232+
/// Returns true if this is a `BoundVar` or `InferenceVar`.
233+
pub fn is_var(&self, interner: &I) -> bool {
234+
match self.data(interner) {
235+
TyData::BoundVar(_) | TyData::InferenceVar(_) => true,
236+
_ => false,
237+
}
238+
}
239+
232240
pub fn is_alias(&self, interner: &I) -> bool {
233241
match self.data(interner) {
234242
TyData::Alias(..) => true,
@@ -547,7 +555,7 @@ impl DebruijnIndex {
547555
/// known. It is referenced within the type using `^1`, indicating
548556
/// a bound type with debruijn index 1 (i.e., skipping through one
549557
/// level of binder).
550-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
558+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
551559
pub struct DynTy<I: Interner> {
552560
pub bounds: Binders<QuantifiedWhereClauses<I>>,
553561
}
@@ -839,6 +847,14 @@ impl<I: Interner> AliasTy<I> {
839847
pub fn intern(self, interner: &I) -> Ty<I> {
840848
Ty::new(interner, self)
841849
}
850+
851+
pub fn self_type_parameter(&self, interner: &I) -> Ty<I> {
852+
self.substitution
853+
.iter(interner)
854+
.find_map(move |p| p.ty(interner))
855+
.unwrap()
856+
.clone()
857+
}
842858
}
843859

844860
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
@@ -1106,6 +1122,15 @@ impl<I: Interner> DomainGoal<I> {
11061122
goal => goal,
11071123
}
11081124
}
1125+
1126+
pub fn inputs(&self, interner: &I) -> Vec<Parameter<I>> {
1127+
match self {
1128+
DomainGoal::Holds(WhereClause::AliasEq(alias_eq)) => {
1129+
vec![ParameterKind::Ty(alias_eq.alias.clone().intern(interner)).intern(interner)]
1130+
}
1131+
_ => Vec::new(),
1132+
}
1133+
}
11091134
}
11101135

11111136
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
@@ -1304,6 +1329,23 @@ impl<V: IntoIterator> Iterator for BindersIntoIterator<V> {
13041329
pub struct ProgramClauseImplication<I: Interner> {
13051330
pub consequence: DomainGoal<I>,
13061331
pub conditions: Goals<I>,
1332+
pub priority: ClausePriority,
1333+
}
1334+
1335+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
1336+
pub enum ClausePriority {
1337+
High,
1338+
Low,
1339+
}
1340+
1341+
impl std::ops::BitAnd for ClausePriority {
1342+
type Output = ClausePriority;
1343+
fn bitand(self, rhs: ClausePriority) -> Self::Output {
1344+
match (self, rhs) {
1345+
(ClausePriority::High, ClausePriority::High) => ClausePriority::High,
1346+
_ => ClausePriority::Low,
1347+
}
1348+
}
13071349
}
13081350

13091351
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
@@ -1318,6 +1360,7 @@ impl<I: Interner> ProgramClauseImplication<I> {
13181360
ProgramClauseImplication {
13191361
consequence: self.consequence.into_from_env_goal(interner),
13201362
conditions: self.conditions.clone(),
1363+
priority: self.priority,
13211364
}
13221365
} else {
13231366
self
@@ -1466,6 +1509,30 @@ impl<T> UCanonical<T> {
14661509
);
14671510
subst.is_identity_subst(interner)
14681511
}
1512+
1513+
pub fn trivial_substitution<I: Interner>(&self, interner: &I) -> Substitution<I> {
1514+
let binders = &self.canonical.binders;
1515+
Substitution::from(
1516+
interner,
1517+
binders
1518+
.iter()
1519+
.enumerate()
1520+
.map(|(index, pk)| {
1521+
let bound_var = BoundVar::new(DebruijnIndex::INNERMOST, index);
1522+
match pk {
1523+
ParameterKind::Ty(_) => {
1524+
ParameterKind::Ty(TyData::BoundVar(bound_var).intern(interner))
1525+
.intern(interner)
1526+
}
1527+
ParameterKind::Lifetime(_) => ParameterKind::Lifetime(
1528+
LifetimeData::BoundVar(bound_var).intern(interner),
1529+
)
1530+
.intern(interner),
1531+
}
1532+
})
1533+
.collect::<Vec<_>>(),
1534+
)
1535+
}
14691536
}
14701537

14711538
#[derive(Clone, PartialEq, Eq, Hash, HasInterner)]

chalk-ir/src/visit/boring_impls.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! The more interesting impls of `Visit` remain in the `visit` module.
66
77
use crate::{
8-
AssocTypeId, DebruijnIndex, Goals, ImplId, Interner, Parameter, ParameterKind,
8+
AssocTypeId, ClausePriority, DebruijnIndex, Goals, ImplId, Interner, Parameter, ParameterKind,
99
PlaceholderIndex, ProgramClause, ProgramClauseData, ProgramClauses, QuantifiedWhereClauses,
1010
QuantifierKind, StructId, Substitution, SuperVisit, TraitId, UniverseIndex, Visit, VisitResult,
1111
Visitor,
@@ -205,6 +205,7 @@ const_visit!(QuantifierKind);
205205
const_visit!(DebruijnIndex);
206206
const_visit!(chalk_engine::TableIndex);
207207
const_visit!(chalk_engine::TimeStamp);
208+
const_visit!(ClausePriority);
208209
const_visit!(());
209210

210211
#[macro_export]

chalk-ir/src/zip.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ eq_zip!(I => TypeName<I>);
200200
eq_zip!(I => QuantifierKind);
201201
eq_zip!(I => PhantomData<I>);
202202
eq_zip!(I => PlaceholderIndex);
203+
eq_zip!(I => ClausePriority);
203204

204205
/// Generates a Zip impl that zips each field of the struct in turn.
205206
macro_rules! struct_zip {
@@ -242,7 +243,8 @@ struct_zip!(impl[I: Interner] Zip<I> for AliasEq<I> { alias, ty });
242243
struct_zip!(impl[I: Interner] Zip<I> for EqGoal<I> { a, b });
243244
struct_zip!(impl[I: Interner] Zip<I> for ProgramClauseImplication<I> {
244245
consequence,
245-
conditions
246+
conditions,
247+
priority,
246248
});
247249

248250
impl<I: Interner> Zip<I> for Environment<I> {

chalk-solve/src/clauses.rs

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use self::env_elaborator::elaborate_env_clauses;
33
use self::program_clauses::ToProgramClauses;
44
use crate::split::Split;
55
use crate::RustIrDatabase;
6+
use chalk_engine::context::Floundered;
67
use chalk_ir::cast::Cast;
78
use chalk_ir::could_match::CouldMatch;
89
use chalk_ir::interner::Interner;
@@ -12,6 +13,7 @@ use rustc_hash::FxHashSet;
1213
pub mod builder;
1314
mod builtin_traits;
1415
mod env_elaborator;
16+
mod generalize;
1517
pub mod program_clauses;
1618

1719
/// For auto-traits, we generate a default rule for every struct,
@@ -111,7 +113,7 @@ pub(crate) fn program_clauses_for_goal<'db, I: Interner>(
111113
db: &'db dyn RustIrDatabase<I>,
112114
environment: &Environment<I>,
113115
goal: &DomainGoal<I>,
114-
) -> Vec<ProgramClause<I>> {
116+
) -> Result<Vec<ProgramClause<I>>, Floundered> {
115117
debug_heading!(
116118
"program_clauses_for_goal(goal={:?}, environment={:?})",
117119
goal,
@@ -121,13 +123,13 @@ pub(crate) fn program_clauses_for_goal<'db, I: Interner>(
121123

122124
let mut vec = vec![];
123125
vec.extend(db.custom_clauses());
124-
program_clauses_that_could_match(db, environment, goal, &mut vec);
126+
program_clauses_that_could_match(db, environment, goal, &mut vec)?;
125127
program_clauses_for_env(db, environment, &mut vec);
126128
vec.retain(|c| c.could_match(interner, goal));
127129

128130
debug!("vec = {:#?}", vec);
129131

130-
vec
132+
Ok(vec)
131133
}
132134

133135
/// Returns a set of program clauses that could possibly match
@@ -139,17 +141,26 @@ fn program_clauses_that_could_match<I: Interner>(
139141
environment: &Environment<I>,
140142
goal: &DomainGoal<I>,
141143
clauses: &mut Vec<ProgramClause<I>>,
142-
) {
144+
) -> Result<(), Floundered> {
143145
let interner = db.interner();
144146
let builder = &mut ClauseBuilder::new(db, clauses);
145147

146148
match goal {
147149
DomainGoal::Holds(WhereClause::Implemented(trait_ref)) => {
148150
let trait_id = trait_ref.trait_id;
149151

152+
let trait_datum = db.trait_datum(trait_id);
153+
154+
if trait_datum.is_non_enumerable_trait() || trait_datum.is_auto_trait() {
155+
let self_ty = trait_ref.self_type_parameter(interner);
156+
if self_ty.bound(interner).is_some() || self_ty.inference_var(interner).is_some() {
157+
return Err(Floundered);
158+
}
159+
}
160+
150161
// This is needed for the coherence related impls, as well
151162
// as for the `Implemented(Foo) :- FromEnv(Foo)` rule.
152-
db.trait_datum(trait_id).to_program_clauses(builder);
163+
trait_datum.to_program_clauses(builder);
153164

154165
for impl_id in db.impls_for_trait(
155166
trait_ref.trait_id,
@@ -168,8 +179,8 @@ fn program_clauses_that_could_match<I: Interner>(
168179
push_auto_trait_impls(builder, trait_id, struct_id);
169180
}
170181
}
171-
TyData::InferenceVar(_) => {
172-
panic!("auto-traits should flounder if nothing is known")
182+
TyData::InferenceVar(_) | TyData::BoundVar(_) => {
183+
return Err(Floundered);
173184
}
174185
_ => {}
175186
}
@@ -222,19 +233,29 @@ fn program_clauses_that_could_match<I: Interner>(
222233
// and `bounded_ty` is the `exists<T> { .. }`
223234
// clauses shown above.
224235

225-
for exists_qwc in dyn_ty.bounds.map_ref(|r| r.iter(interner)) {
226-
// Replace the `T` from `exists<T> { .. }` with `self_ty`,
227-
// yielding clases like
228-
//
229-
// ```
230-
// forall<'a> { Implemented(dyn Fn(&u8): Fn<(&'a u8)>) }
231-
// ```
232-
let qwc = exists_qwc.substitute(interner, &[self_ty.clone().cast(interner)]);
233-
234-
builder.push_binders(&qwc, |builder, wc| {
235-
builder.push_fact(wc);
236-
});
237-
}
236+
// Turn free BoundVars in the type into new existentials. E.g.
237+
// we might get some `dyn Foo<?X>`, and we don't want to return
238+
// a clause with a free variable. We can instead return a
239+
// slightly more general clause by basically turning this into
240+
// `exists<A> dyn Foo<A>`.
241+
let generalized_dyn_ty = generalize::Generalize::apply(db.interner(), dyn_ty);
242+
243+
builder.push_binders(&generalized_dyn_ty, |builder, dyn_ty| {
244+
for exists_qwc in dyn_ty.bounds.map_ref(|r| r.iter(interner)) {
245+
// Replace the `T` from `exists<T> { .. }` with `self_ty`,
246+
// yielding clases like
247+
//
248+
// ```
249+
// forall<'a> { Implemented(dyn Fn(&u8): Fn<(&'a u8)>) }
250+
// ```
251+
let qwc =
252+
exists_qwc.substitute(interner, &[self_ty.clone().cast(interner)]);
253+
254+
builder.push_binders(&qwc, |builder, wc| {
255+
builder.push_fact(wc);
256+
});
257+
}
258+
});
238259
}
239260

240261
if let Some(well_known) = trait_datum.well_known {
@@ -257,9 +278,9 @@ fn program_clauses_that_could_match<I: Interner>(
257278
}
258279
DomainGoal::WellFormed(WellFormed::Ty(ty))
259280
| DomainGoal::IsUpstream(ty)
260-
| DomainGoal::DownstreamType(ty) => match_ty(builder, environment, ty),
281+
| DomainGoal::DownstreamType(ty) => match_ty(builder, environment, ty)?,
261282
DomainGoal::IsFullyVisible(ty) | DomainGoal::IsLocal(ty) => {
262-
match_ty(builder, environment, ty)
283+
match_ty(builder, environment, ty)?
263284
}
264285
DomainGoal::FromEnv(_) => (), // Computed in the environment
265286
DomainGoal::Normalize(Normalize { alias, ty: _ }) => {
@@ -277,6 +298,18 @@ fn program_clauses_that_could_match<I: Interner>(
277298
let associated_ty_datum = db.associated_ty_data(alias.associated_ty_id);
278299
let trait_id = associated_ty_datum.trait_id;
279300
let trait_parameters = db.trait_parameters_from_projection(alias);
301+
302+
let trait_datum = db.trait_datum(trait_id);
303+
304+
// Flounder if the self-type is unknown and the trait is non-enumerable.
305+
//
306+
// e.g., Normalize(<?X as Iterator>::Item = u32)
307+
if (alias.self_type_parameter(interner).is_var(interner))
308+
&& trait_datum.is_non_enumerable_trait()
309+
{
310+
return Err(Floundered);
311+
}
312+
280313
push_program_clauses_for_associated_type_values_in_impls_of(
281314
builder,
282315
trait_id,
@@ -288,6 +321,8 @@ fn program_clauses_that_could_match<I: Interner>(
288321
.to_program_clauses(builder),
289322
DomainGoal::Compatible(()) => (),
290323
};
324+
325+
Ok(())
291326
}
292327

293328
/// Generate program clauses from the associated-type values
@@ -348,9 +383,9 @@ fn match_ty<I: Interner>(
348383
builder: &mut ClauseBuilder<'_, I>,
349384
environment: &Environment<I>,
350385
ty: &Ty<I>,
351-
) {
386+
) -> Result<(), Floundered> {
352387
let interner = builder.interner();
353-
match ty.data(interner) {
388+
Ok(match ty.data(interner) {
354389
TyData::Apply(application_ty) => match_type_name(builder, application_ty.name),
355390
TyData::Placeholder(_) => {
356391
builder.push_clause(WellFormed::Ty(ty.clone()), Some(FromEnv::Ty(ty.clone())));
@@ -365,12 +400,12 @@ fn match_ty<I: Interner>(
365400
.substitution
366401
.iter(interner)
367402
.map(|p| p.assert_ty_ref(interner))
368-
.for_each(|ty| match_ty(builder, environment, &ty))
403+
.map(|ty| match_ty(builder, environment, &ty))
404+
.collect::<Result<_, Floundered>>()?;
369405
}
370-
TyData::BoundVar(_) => {}
371-
TyData::InferenceVar(_) => panic!("should have floundered"),
406+
TyData::BoundVar(_) | TyData::InferenceVar(_) => return Err(Floundered),
372407
TyData::Dyn(_) => {}
373-
}
408+
})
374409
}
375410

376411
fn match_type_name<I: Interner>(builder: &mut ClauseBuilder<'_, I>, name: TypeName<I>) {
@@ -396,6 +431,8 @@ fn program_clauses_for_env<'db, I: Interner>(
396431
environment: &Environment<I>,
397432
clauses: &mut Vec<ProgramClause<I>>,
398433
) {
434+
clauses.extend(environment.clauses.iter(db.interner()).cloned());
435+
399436
let mut last_round = FxHashSet::default();
400437
elaborate_env_clauses(
401438
db,

0 commit comments

Comments
 (0)