Skip to content

Commit 363f7ad

Browse files
authored
Merge pull request #527 from nathanwhit/add-region-constraints
Refactor `LifetimeOutlives` goals to produce `AddRegionConstraint` subgoals
2 parents b643b21 + 05f8225 commit 363f7ad

File tree

12 files changed

+74
-89
lines changed

12 files changed

+74
-89
lines changed

book/src/clauses/type_equality.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,16 +145,15 @@ with unification. As described in the
145145
section, unification is basically a procedure with a signature like this:
146146

147147
```text
148-
Unify(A, B) = Result<(Subgoals, RegionConstraints), NoSolution>
148+
Unify(A, B) = Result<Subgoals, NoSolution>
149149
```
150150

151151
In other words, we try to unify two things A and B. That procedure
152152
might just fail, in which case we get back `Err(NoSolution)`. This
153153
would happen, for example, if we tried to unify `u32` and `i32`.
154154

155155
The key point is that, on success, unification can also give back to
156-
us a set of subgoals that still remain to be proven. (It can also give
157-
back region constraints, but those are not relevant here).
156+
us a set of subgoals that still remain to be proven.
158157

159158
Whenever unification encounters a non-placeholder associated type
160159
projection P being equated with some other type T, it always succeeds,

chalk-engine/src/logic.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::{
1212

1313
use chalk_ir::interner::Interner;
1414
use chalk_ir::{
15-
Canonical, ConstrainedSubst, DomainGoal, Floundered, Goal, GoalData, InEnvironment, NoSolution,
16-
Substitution, UCanonical, UniverseMap, WhereClause,
15+
Canonical, ConstrainedSubst, Floundered, Goal, GoalData, InEnvironment, NoSolution,
16+
Substitution, UCanonical, UniverseMap,
1717
};
1818
use tracing::{debug, debug_span, info, instrument};
1919

@@ -251,16 +251,8 @@ impl<I: Interner, C: Context<I>> Forest<I, C> {
251251
let (mut infer, subst, environment, goal) = context.instantiate_ucanonical_goal(&goal);
252252
let goal_data = goal.data(context.interner());
253253

254-
let is_outlives_goal = |dg: &DomainGoal<I>| {
255-
if let DomainGoal::Holds(WhereClause::LifetimeOutlives(_)) = dg {
256-
true
257-
} else {
258-
false
259-
}
260-
};
261-
262254
match goal_data {
263-
GoalData::DomainGoal(domain_goal) if !is_outlives_goal(domain_goal) => {
255+
GoalData::DomainGoal(domain_goal) => {
264256
match context.program_clauses(&environment, &domain_goal, &mut infer) {
265257
Ok(clauses) => {
266258
for clause in clauses {

chalk-engine/src/simplify.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ use crate::{ExClause, Literal, TimeStamp};
44

55
use chalk_ir::interner::Interner;
66
use chalk_ir::{
7-
Constraint, DomainGoal, Environment, Fallible, Goal, GoalData, InEnvironment, LifetimeOutlives,
8-
QuantifierKind, Substitution, WhereClause,
7+
Constraint, Environment, Fallible, Goal, GoalData, InEnvironment, QuantifierKind, Substitution,
98
};
109
use tracing::debug;
1110

@@ -69,22 +68,17 @@ impl<I: Interner, C: Context<I>> Forest<I, C> {
6968
&goal.b,
7069
&mut ex_clause,
7170
)?,
72-
GoalData::DomainGoal(domain_goal) => match domain_goal {
73-
DomainGoal::Holds(WhereClause::LifetimeOutlives(LifetimeOutlives { a, b })) => {
74-
ex_clause.constraints.push(InEnvironment::new(
71+
GoalData::AddRegionConstraint(a, b) => ex_clause.constraints.push(
72+
InEnvironment::new(&environment, Constraint::Outlives(a.clone(), b.clone())),
73+
),
74+
GoalData::DomainGoal(domain_goal) => {
75+
ex_clause
76+
.subgoals
77+
.push(Literal::Positive(InEnvironment::new(
7578
&environment,
76-
Constraint::Outlives(a.clone(), b.clone()),
77-
));
78-
}
79-
_ => {
80-
ex_clause
81-
.subgoals
82-
.push(Literal::Positive(InEnvironment::new(
83-
&environment,
84-
context.into_goal(domain_goal.clone()),
85-
)));
86-
}
87-
},
79+
context.into_goal(domain_goal.clone()),
80+
)));
81+
}
8882
GoalData::CannotProve(()) => {
8983
debug!("Marking Strand as ambiguous because of a `CannotProve` subgoal");
9084
ex_clause.ambiguous = true;

chalk-ir/src/debug.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ impl<I: Interner> Debug for GoalData<I> {
326326
GoalData::Not(ref g) => write!(fmt, "not {{ {:?} }}", g),
327327
GoalData::EqGoal(ref wc) => write!(fmt, "{:?}", wc),
328328
GoalData::DomainGoal(ref wc) => write!(fmt, "{:?}", wc),
329+
GoalData::AddRegionConstraint(ref a, ref b) => {
330+
write!(fmt, "AddRegionConstraint({:?}: {:?})", a, b)
331+
}
329332
GoalData::CannotProve(()) => write!(fmt, r"¯\_(ツ)_/¯"),
330333
}
331334
}

chalk-ir/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2445,6 +2445,9 @@ pub enum GoalData<I: Interner> {
24452445
/// proven via program clauses
24462446
DomainGoal(DomainGoal<I>),
24472447

2448+
/// Adds a region constraint requiring `'a : 'b`, given two lifetimes `'a, 'b`
2449+
AddRegionConstraint(Lifetime<I>, Lifetime<I>),
2450+
24482451
/// Indicates something that cannot be proven to be true or false
24492452
/// definitively. This can occur with overflow but also with
24502453
/// unifications of skolemized variables like `forall<X,Y> { X = Y

chalk-solve/src/clauses.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,15 @@ fn program_clauses_that_could_match<I: Interner>(
312312
.opaque_ty_data(opaque_ty.opaque_ty_id)
313313
.to_program_clauses(builder),
314314
},
315-
DomainGoal::Holds(WhereClause::LifetimeOutlives(_)) => {}
315+
DomainGoal::Holds(WhereClause::LifetimeOutlives(LifetimeOutlives { a, b })) => {
316+
builder.push_clause(
317+
WhereClause::LifetimeOutlives(LifetimeOutlives {
318+
a: a.clone(),
319+
b: b.clone(),
320+
}),
321+
Some(GoalData::AddRegionConstraint(a.clone(), b.clone()).intern(interner)),
322+
);
323+
}
316324
DomainGoal::WellFormed(WellFormed::Trait(trait_ref))
317325
| DomainGoal::LocalImplAllowed(trait_ref) => {
318326
db.trait_datum(trait_ref.trait_id)

chalk-solve/src/infer/test.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -292,27 +292,26 @@ fn lifetime_constraint_indirect() {
292292
// '!1.
293293
let t_a = ty!(apply (item 0) (lifetime (placeholder 1)));
294294
let t_b = ty!(apply (item 0) (lifetime (infer 1)));
295-
let UnificationResult { goals, constraints } =
296-
table.unify(interner, &environment0, &t_a, &t_b).unwrap();
295+
let UnificationResult { goals } = table.unify(interner, &environment0, &t_a, &t_b).unwrap();
297296
assert!(goals.is_empty());
298-
assert!(constraints.is_empty());
299297

300298
// Here, we try to unify `?0` (the type variable in universe 0)
301299
// with something that involves `'?1`. Since `'?1` has been
302300
// unified with `'!1`, and `'!1` is not visible from universe 0,
303301
// we will replace `'!1` with a new variable `'?2` and introduce a
304302
// (likely unsatisfiable) constraint relating them.
305303
let t_c = ty!(infer 0);
306-
let UnificationResult { goals, constraints } =
307-
table.unify(interner, &environment0, &t_c, &t_b).unwrap();
308-
assert!(goals.is_empty());
309-
assert_eq!(constraints.len(), 2);
304+
let goals = table
305+
.unify(interner, &environment0, &t_c, &t_b)
306+
.unwrap()
307+
.goals;
308+
assert_eq!(goals.len(), 2);
310309
assert_eq!(
311-
format!("{:?}", constraints[0]),
312-
"InEnvironment { environment: Env([]), goal: \'?2: \'!1_0 }",
310+
format!("{:?}", goals[0]),
311+
"InEnvironment { environment: Env([]), goal: AddRegionConstraint(\'!1_0: \'?2) }",
313312
);
314313
assert_eq!(
315-
format!("{:?}", constraints[1]),
316-
"InEnvironment { environment: Env([]), goal: \'!1_0: \'?2 }",
314+
format!("{:?}", goals[1]),
315+
"InEnvironment { environment: Env([]), goal: AddRegionConstraint(\'?2: \'!1_0) }",
317316
);
318317
}

chalk-solve/src/infer/unify.rs

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,13 @@ impl<I: Interner> InferenceTable<I> {
3737
struct Unifier<'t, I: Interner> {
3838
table: &'t mut InferenceTable<I>,
3939
environment: &'t Environment<I>,
40-
goals: Vec<InEnvironment<DomainGoal<I>>>,
41-
constraints: Vec<InEnvironment<Constraint<I>>>,
40+
goals: Vec<InEnvironment<Goal<I>>>,
4241
interner: &'t I,
4342
}
4443

4544
#[derive(Debug)]
4645
pub(crate) struct UnificationResult<I: Interner> {
47-
pub(crate) goals: Vec<InEnvironment<DomainGoal<I>>>,
48-
pub(crate) constraints: Vec<InEnvironment<Constraint<I>>>,
46+
pub(crate) goals: Vec<InEnvironment<Goal<I>>>,
4947
}
5048

5149
impl<'t, I: Interner> Unifier<'t, I> {
@@ -58,7 +56,6 @@ impl<'t, I: Interner> Unifier<'t, I> {
5856
environment: environment,
5957
table: table,
6058
goals: vec![],
61-
constraints: vec![],
6259
interner,
6360
}
6461
}
@@ -71,10 +68,7 @@ impl<'t, I: Interner> Unifier<'t, I> {
7168
T: ?Sized + Zip<I>,
7269
{
7370
Zip::zip_with(&mut self, a, b)?;
74-
Ok(UnificationResult {
75-
goals: self.goals,
76-
constraints: self.constraints,
77-
})
71+
Ok(UnificationResult { goals: self.goals })
7872
}
7973

8074
fn unify_ty_ty(&mut self, a: &Ty<I>, b: &Ty<I>) -> Fallible<()> {
@@ -344,7 +338,7 @@ impl<'t, I: Interner> Unifier<'t, I> {
344338

345339
(&LifetimeData::Placeholder(_), &LifetimeData::Placeholder(_)) => {
346340
if a != b {
347-
Ok(self.push_lifetime_eq_constraint(a.clone(), b.clone()))
341+
Ok(self.push_lifetime_eq_subgoal(a.clone(), b.clone()))
348342
} else {
349343
Ok(())
350344
}
@@ -388,7 +382,7 @@ impl<'t, I: Interner> Unifier<'t, I> {
388382
"unify_lifetime_var: {:?} in {:?} cannot see {:?}; pushing constraint",
389383
var, var_ui, value_ui
390384
);
391-
Ok(self.push_lifetime_eq_constraint(a.clone(), b.clone()))
385+
Ok(self.push_lifetime_eq_subgoal(a.clone(), b.clone()))
392386
}
393387
}
394388

@@ -478,15 +472,14 @@ impl<'t, I: Interner> Unifier<'t, I> {
478472
Ok(())
479473
}
480474

481-
fn push_lifetime_eq_constraint(&mut self, a: Lifetime<I>, b: Lifetime<I>) {
482-
self.constraints.push(InEnvironment::new(
483-
self.environment,
484-
Constraint::Outlives(a.clone(), b.clone()),
485-
));
486-
self.constraints.push(InEnvironment::new(
487-
self.environment,
488-
Constraint::Outlives(b, a),
489-
));
475+
fn push_lifetime_eq_subgoal(&mut self, a: Lifetime<I>, b: Lifetime<I>) {
476+
let interner = self.interner;
477+
let b_outlives_a = GoalData::AddRegionConstraint(b.clone(), a.clone()).intern(interner);
478+
self.goals
479+
.push(InEnvironment::new(self.environment, b_outlives_a));
480+
let a_outlives_b = GoalData::AddRegionConstraint(a, b).intern(interner);
481+
self.goals
482+
.push(InEnvironment::new(self.environment, a_outlives_b));
490483
}
491484
}
492485

@@ -588,10 +581,8 @@ where
588581
// exists<'x> forall<'b> ?T = Foo<'x>, where 'x = 'b
589582

590583
let tick_x = self.unifier.table.new_variable(self.universe_index);
591-
self.unifier.push_lifetime_eq_constraint(
592-
tick_x.to_lifetime(interner),
593-
ui.to_lifetime(interner),
594-
);
584+
self.unifier
585+
.push_lifetime_eq_subgoal(tick_x.to_lifetime(interner), ui.to_lifetime(interner));
595586
Ok(tick_x.to_lifetime(interner))
596587
} else {
597588
// If the `ui` is higher than `self.universe_index`, then we can name

chalk-solve/src/recursive/fulfill.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,7 @@ pub(super) trait RecursiveInferenceTable<I: Interner> {
9999
environment: &Environment<I>,
100100
a: &T,
101101
b: &T,
102-
) -> Fallible<(
103-
Vec<InEnvironment<DomainGoal<I>>>,
104-
Vec<InEnvironment<Constraint<I>>>,
105-
)>
102+
) -> Fallible<Vec<InEnvironment<Goal<I>>>>
106103
where
107104
T: ?Sized + Zip<I>;
108105

@@ -259,13 +256,11 @@ impl<'s, I: Interner, Solver: SolveDatabase<I>, Infer: RecursiveInferenceTable<I
259256
where
260257
T: ?Sized + Zip<I> + Debug,
261258
{
262-
let (goals, constraints) = self
259+
let goals = self
263260
.infer
264261
.unify(self.solver.interner(), environment, a, b)?;
265262
debug!("unify({:?}, {:?}) succeeded", a, b);
266263
debug!("unify: goals={:?}", goals);
267-
debug!("unify: constraints={:?}", constraints);
268-
self.constraints.extend(constraints);
269264
for goal in goals {
270265
let goal = goal.cast(self.solver.interner());
271266
self.push_obligation(Obligation::Prove(goal));
@@ -309,12 +304,17 @@ impl<'s, I: Interner, Solver: SolveDatabase<I>, Infer: RecursiveInferenceTable<I
309304
let in_env = InEnvironment::new(environment, subgoal.clone());
310305
self.push_obligation(Obligation::Refute(in_env));
311306
}
307+
GoalData::AddRegionConstraint(a, b) => {
308+
self.constraints.insert(InEnvironment::new(
309+
&environment,
310+
Constraint::Outlives(a.clone(), b.clone()),
311+
));
312+
}
312313
GoalData::DomainGoal(domain_goal) => match domain_goal {
313314
DomainGoal::Holds(WhereClause::LifetimeOutlives(LifetimeOutlives { a, b })) => {
314-
self.constraints.insert(InEnvironment::new(
315-
&environment,
316-
Constraint::Outlives(a.clone(), b.clone()),
317-
));
315+
let add_constraint =
316+
GoalData::AddRegionConstraint(a.clone(), b.clone()).intern(interner);
317+
self.push_goal(environment, add_constraint)?
318318
}
319319
_ => {
320320
let in_env = InEnvironment::new(environment, goal);

chalk-solve/src/recursive/solve.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use chalk_ir::interner::{HasInterner, Interner};
1010
use chalk_ir::visit::Visit;
1111
use chalk_ir::zip::Zip;
1212
use chalk_ir::{
13-
Binders, Canonical, ClausePriority, Constraint, DomainGoal, Environment, Fallible, Floundered,
14-
GenericArg, Goal, GoalData, InEnvironment, NoSolution, ProgramClause, ProgramClauseData,
13+
Binders, Canonical, ClausePriority, DomainGoal, Environment, Fallible, Floundered, GenericArg,
14+
Goal, GoalData, InEnvironment, NoSolution, ProgramClause, ProgramClauseData,
1515
ProgramClauseImplication, Substitution, UCanonical, UniverseMap,
1616
};
1717
use std::fmt::Debug;
@@ -279,15 +279,12 @@ impl<I: Interner> RecursiveInferenceTable<I> for RecursiveInferenceTableImpl<I>
279279
environment: &Environment<I>,
280280
a: &T,
281281
b: &T,
282-
) -> Fallible<(
283-
Vec<InEnvironment<DomainGoal<I>>>,
284-
Vec<InEnvironment<Constraint<I>>>,
285-
)>
282+
) -> Fallible<Vec<InEnvironment<Goal<I>>>>
286283
where
287284
T: ?Sized + Zip<I>,
288285
{
289286
let res = self.infer.unify(interner, environment, a, b)?;
290-
Ok((res.goals, res.constraints))
287+
Ok(res.goals)
291288
}
292289

293290
fn instantiate_canonical<T>(&mut self, interner: &I, bound: &Canonical<T>) -> T::Result

0 commit comments

Comments
 (0)