Skip to content

Commit 3a074e4

Browse files
committed
Move floundering logic to program_clauses
1 parent b1453e5 commit 3a074e4

File tree

3 files changed

+54
-76
lines changed

3 files changed

+54
-76
lines changed

chalk-solve/src/clauses.rs

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ pub fn push_auto_trait_impls<I: Interner>(
102102
});
103103
}
104104

105+
// TODO add Floundered error instead of using Option
106+
105107
/// Given some goal `goal` that must be proven, along with
106108
/// its `environment`, figures out the program clauses that apply
107109
/// to this goal from the Rust program. So for example if the goal
@@ -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+
) -> Option<Vec<ProgramClause<I>>> {
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+
Some(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+
) -> Option<()> {
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 None;
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 None;
173184
}
174185
_ => {}
175186
}
@@ -262,12 +273,12 @@ fn program_clauses_that_could_match<I: Interner>(
262273
}
263274
DomainGoal::WellFormed(WellFormed::Ty(ty))
264275
| DomainGoal::IsUpstream(ty)
265-
| DomainGoal::DownstreamType(ty) => match_ty(builder, environment, ty),
276+
| DomainGoal::DownstreamType(ty) => match_ty(builder, environment, ty)?,
266277
DomainGoal::IsFullyVisible(ty) | DomainGoal::IsLocal(ty) => {
267-
match_ty(builder, environment, ty)
278+
match_ty(builder, environment, ty)?
268279
}
269280
DomainGoal::FromEnv(_) => (), // Computed in the environment
270-
DomainGoal::Normalize(Normalize { alias, ty: _ }) => {
281+
DomainGoal::Normalize(Normalize { alias, ty }) => {
271282
// Normalize goals derive from `AssociatedTyValue` datums,
272283
// which are found in impls. That is, if we are
273284
// normalizing (e.g.) `<T as Iterator>::Item>`, then
@@ -282,6 +293,26 @@ fn program_clauses_that_could_match<I: Interner>(
282293
let associated_ty_datum = db.associated_ty_data(alias.associated_ty_id);
283294
let trait_id = associated_ty_datum.trait_id;
284295
let trait_parameters = db.trait_parameters_from_projection(alias);
296+
297+
if let TyData::Apply(ApplicationTy {
298+
name: TypeName::AssociatedType(_),
299+
..
300+
}) = ty.data(interner)
301+
{
302+
// TODO this is probably wrong
303+
// Associated types will never *normalize* to an associated type placeholder.
304+
// It's important that we return early here so that we don't flounder in this case.
305+
return Some(());
306+
}
307+
308+
let trait_datum = builder.db.trait_datum(trait_id);
309+
if trait_datum.is_non_enumerable_trait() || trait_datum.is_auto_trait() {
310+
let self_ty = alias.self_type_parameter(interner);
311+
if self_ty.bound(interner).is_some() || self_ty.inference_var(interner).is_some() {
312+
return None;
313+
}
314+
}
315+
285316
push_program_clauses_for_associated_type_values_in_impls_of(
286317
builder,
287318
trait_id,
@@ -293,6 +324,8 @@ fn program_clauses_that_could_match<I: Interner>(
293324
.to_program_clauses(builder),
294325
DomainGoal::Compatible(()) => (),
295326
};
327+
328+
Some(())
296329
}
297330

298331
/// Generate program clauses from the associated-type values
@@ -353,9 +386,9 @@ fn match_ty<I: Interner>(
353386
builder: &mut ClauseBuilder<'_, I>,
354387
environment: &Environment<I>,
355388
ty: &Ty<I>,
356-
) {
389+
) -> Option<()> {
357390
let interner = builder.interner();
358-
match ty.data(interner) {
391+
Some(match ty.data(interner) {
359392
TyData::Apply(application_ty) => match_type_name(builder, application_ty.name),
360393
TyData::Placeholder(_) => {
361394
builder.push_clause(WellFormed::Ty(ty.clone()), Some(FromEnv::Ty(ty.clone())));
@@ -370,12 +403,12 @@ fn match_ty<I: Interner>(
370403
.substitution
371404
.iter(interner)
372405
.map(|p| p.assert_ty_ref(interner))
373-
.for_each(|ty| match_ty(builder, environment, &ty))
406+
.map(|ty| match_ty(builder, environment, &ty))
407+
.collect::<Option<_>>()?;
374408
}
375-
TyData::BoundVar(_) => {}
376-
TyData::InferenceVar(_) => panic!("should have floundered"),
409+
TyData::BoundVar(_) | TyData::InferenceVar(_) => return None,
377410
TyData::Dyn(_) => {}
378-
}
411+
})
379412
}
380413

381414
fn match_type_name<I: Interner>(builder: &mut ClauseBuilder<'_, I>, name: TypeName<I>) {

chalk-solve/src/recursive/mod.rs

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -541,35 +541,7 @@ impl<'me, I: Interner> Solver<'me, I> {
541541
environment: &Environment<I>,
542542
goal: &DomainGoal<I>,
543543
) -> Result<Vec<ProgramClause<I>>, Floundered> {
544-
// TODO this is currently duplicated with the SLG solver, extract it somewhere?
545-
// Look for floundering goals:
546-
let interner = self.program.interner();
547-
match goal {
548-
// Check for a goal like `?T: Foo` where `Foo` is not enumerable.
549-
DomainGoal::Holds(WhereClause::Implemented(trait_ref)) => {
550-
let trait_datum = self.program.trait_datum(trait_ref.trait_id);
551-
if trait_datum.is_non_enumerable_trait() || trait_datum.is_auto_trait() {
552-
let self_ty = trait_ref.self_type_parameter(interner);
553-
if let Some(_) = self_ty.bound(interner) {
554-
return Err(Floundered);
555-
}
556-
}
557-
}
558-
559-
DomainGoal::WellFormed(WellFormed::Ty(ty))
560-
| DomainGoal::IsUpstream(ty)
561-
| DomainGoal::DownstreamType(ty)
562-
| DomainGoal::IsFullyVisible(ty)
563-
| DomainGoal::IsLocal(ty) => match ty.data(interner) {
564-
TyData::BoundVar(_) => return Err(Floundered),
565-
_ => {}
566-
},
567-
568-
_ => {}
569-
}
570-
571-
// TODO this also includes clauses from env
572-
Ok(program_clauses_for_goal(self.program, environment, goal))
544+
program_clauses_for_goal(self.program, environment, goal).ok_or(Floundered)
573545
}
574546
}
575547

chalk-solve/src/solve/slg.rs

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -175,39 +175,12 @@ impl<'me, I: Interner> context::ContextOps<SlgContext<I>> for SlgContextOps<'me,
175175
&self,
176176
environment: &Environment<I>,
177177
goal: &DomainGoal<I>,
178-
infer: &mut TruncatingInferenceTable<I>,
178+
_infer: &mut TruncatingInferenceTable<I>,
179179
) -> Result<Vec<ProgramClause<I>>, Floundered> {
180-
// Look for floundering goals:
181-
let interner = self.interner();
182-
match goal {
183-
// Check for a goal like `?T: Foo` where `Foo` is not enumerable.
184-
DomainGoal::Holds(WhereClause::Implemented(trait_ref)) => {
185-
let trait_datum = self.program.trait_datum(trait_ref.trait_id);
186-
if trait_datum.is_non_enumerable_trait() || trait_datum.is_auto_trait() {
187-
let self_ty = trait_ref.self_type_parameter(interner);
188-
if let Some(v) = self_ty.inference_var(interner) {
189-
if !infer.infer.var_is_bound(v) {
190-
return Err(Floundered);
191-
}
192-
}
193-
}
194-
}
195-
196-
DomainGoal::WellFormed(WellFormed::Ty(ty))
197-
| DomainGoal::IsUpstream(ty)
198-
| DomainGoal::DownstreamType(ty)
199-
| DomainGoal::IsFullyVisible(ty)
200-
| DomainGoal::IsLocal(ty) => match ty.data(interner) {
201-
TyData::InferenceVar(_) => return Err(Floundered),
202-
_ => {}
203-
},
204-
205-
_ => {}
206-
}
207-
208-
let mut clauses: Vec<_> = program_clauses_for_goal(self.program, environment, goal);
180+
let interner = self.program.interner();
181+
let mut clauses: Vec<_> =
182+
program_clauses_for_goal(self.program, environment, goal).ok_or(Floundered)?;
209183

210-
// TODO this is redundant, I think
211184
clauses.extend(
212185
environment
213186
.clauses

0 commit comments

Comments
 (0)