Skip to content

Commit 944fce6

Browse files
committed
Auto merge of #609 - daboross:subtype-variance-and-goal-writer, r=nikomatsakis
Variance This contains `@super-tuple` and my work on rebasing `@jackh726's` variance work from #520 and implementing the generalizer on top of it. As mentioned in Zulip, we've been trying to port rustc tests for the generalizer to chalk, in an effort to see what fails. Since we've been failing at this for so long, we decided to just try to write the generalizer instead! We don't fully understand it, but have enough of an idea to write something that hopefully works. This fails 5 of the existing tests, and that's part of the motivation for opening this now - we aren't sure how to proceed on those. We have not written any new tests for variance nor the generalizer. The last one we've debugged is `dyn_lifetime_bound`, which fails because when unifying our generalized value and the original ty value, the code tries to unify a inference var with universe 4, created freshly for our generalized value, with a placeholder with universe 6 - and that fails to pass the `OccursCheck`. Here's the debug logs we generated for that failed test: https://gist.github.com/daboross/24f1d2310bd86e1d542678c4ebd550e0
2 parents d112f2e + dfb510a commit 944fce6

File tree

41 files changed

+2901
-396
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2901
-396
lines changed

chalk-derive/src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ fn derive_zip(mut s: synstructure::Structure) -> TokenStream {
224224
let mut body = each_variant_pair(&mut a, &mut b, |v_a, v_b| {
225225
let mut t = TokenStream::new();
226226
for (b_a, b_b) in v_a.bindings().iter().zip(v_b.bindings().iter()) {
227-
quote!(chalk_ir::zip::Zip::zip_with(zipper, #b_a, #b_b)?;).to_tokens(&mut t);
227+
quote!(chalk_ir::zip::Zip::zip_with(zipper, variance, #b_a, #b_b)?;).to_tokens(&mut t);
228228
}
229229
quote!(Ok(())).to_tokens(&mut t);
230230
t
@@ -240,8 +240,9 @@ fn derive_zip(mut s: synstructure::Structure) -> TokenStream {
240240

241241
fn zip_with<'i, Z: ::chalk_ir::zip::Zipper<'i, #interner>>(
242242
zipper: &mut Z,
243-
a: &Self,
244-
b: &Self,
243+
variance: ::chalk_ir::Variance,
244+
a: &Self,
245+
b: &Self,
245246
) -> ::chalk_ir::Fallible<()>
246247
where
247248
#interner: 'i,

chalk-engine/src/logic.rs

Lines changed: 21 additions & 17 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-
AnswerSubst, Canonical, CanonicalVarKinds, ConstrainedSubst, Floundered, Goal, GoalData,
16-
InEnvironment, NoSolution, Substitution, UCanonical, UniverseMap,
15+
AnswerSubst, Canonical, CanonicalVarKinds, ConstrainedSubst, FallibleOrFloundered, Floundered,
16+
Goal, GoalData, InEnvironment, NoSolution, Substitution, UCanonical, UniverseMap,
1717
};
1818
use chalk_solve::clauses::program_clauses_for_goal;
1919
use chalk_solve::coinductive_goal::IsCoinductive;
@@ -278,6 +278,7 @@ impl<I: Interner> Forest<I> {
278278
info!("program clause = {:#?}", clause);
279279
let mut infer = infer.clone();
280280
if let Ok(resolvent) = infer.resolvent_clause(
281+
context.unification_database(),
281282
context.program().interner(),
282283
&environment,
283284
&domain_goal,
@@ -316,21 +317,23 @@ impl<I: Interner> Forest<I> {
316317
// simplified subgoals. You can think of this as
317318
// applying built-in "meta program clauses" that
318319
// reduce goals into Domain goals.
319-
if let Ok(ex_clause) =
320-
Self::simplify_goal(context, &mut infer, subst, environment, goal)
321-
{
322-
info!(
323-
ex_clause = ?infer.debug_ex_clause(context.program().interner(), &ex_clause),
324-
"pushing initial strand"
325-
);
326-
let strand = Strand {
327-
infer,
328-
ex_clause,
329-
selected_subgoal: None,
330-
last_pursued_time: TimeStamp::default(),
331-
};
332-
let canonical_strand = Self::canonicalize_strand(context, strand);
333-
table.enqueue_strand(canonical_strand);
320+
match Self::simplify_goal(context, &mut infer, subst, environment, goal) {
321+
FallibleOrFloundered::Ok(ex_clause) => {
322+
info!(
323+
ex_clause = ?infer.debug_ex_clause(context.program().interner(), &ex_clause),
324+
"pushing initial strand"
325+
);
326+
let strand = Strand {
327+
infer,
328+
ex_clause,
329+
selected_subgoal: None,
330+
last_pursued_time: TimeStamp::default(),
331+
};
332+
let canonical_strand = Self::canonicalize_strand(context, strand);
333+
table.enqueue_strand(canonical_strand);
334+
}
335+
FallibleOrFloundered::NoSolution => {}
336+
FallibleOrFloundered::Floundered => table.mark_floundered(),
334337
}
335338
}
336339
}
@@ -653,6 +656,7 @@ impl<'forest, I: Interner> SolveState<'forest, I> {
653656
);
654657
match strand.infer.apply_answer_subst(
655658
self.context.program().interner(),
659+
self.context.unification_database(),
656660
&mut strand.ex_clause,
657661
&subgoal,
658662
&table_goal,

chalk-engine/src/normalize_deep.rs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ where
5555
.assert_ty_ref(interner)
5656
.fold_with(self, DebruijnIndex::INNERMOST)?
5757
.shifted_in(interner)), // FIXME shift
58-
None => Ok(var.to_ty(interner, kind)),
58+
None => {
59+
// Normalize all inference vars which have been unified into a
60+
// single variable. Ena calls this the "root" variable.
61+
Ok(self.table.inference_var_root(var).to_ty(interner, kind))
62+
}
5963
}
6064
}
6165

@@ -107,6 +111,20 @@ mod test {
107111

108112
const U0: UniverseIndex = UniverseIndex { counter: 0 };
109113

114+
// We just use a vec of 20 `Invariant`, since this is zipped and no substs are
115+
// longer than this
116+
#[derive(Debug)]
117+
struct TestDatabase;
118+
impl UnificationDatabase<ChalkIr> for TestDatabase {
119+
fn fn_def_variance(&self, _fn_def_id: FnDefId<ChalkIr>) -> Variances<ChalkIr> {
120+
Variances::from(&ChalkIr, [Variance::Invariant; 20].iter().copied())
121+
}
122+
123+
fn adt_variance(&self, _adt_id: AdtId<ChalkIr>) -> Variances<ChalkIr> {
124+
Variances::from(&ChalkIr, [Variance::Invariant; 20].iter().copied())
125+
}
126+
}
127+
110128
#[test]
111129
fn infer() {
112130
let interner = &ChalkIr;
@@ -115,14 +133,34 @@ mod test {
115133
let a = table.new_variable(U0).to_ty(interner);
116134
let b = table.new_variable(U0).to_ty(interner);
117135
table
118-
.unify(interner, &environment0, &a, &ty!(apply (item 0) (expr b)))
136+
.relate(
137+
interner,
138+
&TestDatabase,
139+
&environment0,
140+
Variance::Invariant,
141+
&a,
142+
&ty!(apply (item 0) (expr b)),
143+
)
119144
.unwrap();
145+
// a is unified to Adt<#0>(c), where 'c' is a new inference var
146+
// created by the generalizer to generalize 'b'. It then unifies 'b'
147+
// and 'c', and when we normalize them, they'll both be output as
148+
// the same "root" variable. However, there are no guarantees for
149+
// _which_ of 'b' and 'c' becomes the root. We need to normalize
150+
// "b" too, then, to ensure we get a consistent result.
120151
assert_eq!(
121152
DeepNormalizer::normalize_deep(&mut table, interner, &a),
122-
ty!(apply (item 0) (expr b))
153+
ty!(apply (item 0) (expr DeepNormalizer::normalize_deep(&mut table, interner, &b))),
123154
);
124155
table
125-
.unify(interner, &environment0, &b, &ty!(apply (item 1)))
156+
.relate(
157+
interner,
158+
&TestDatabase,
159+
&environment0,
160+
Variance::Invariant,
161+
&b,
162+
&ty!(apply (item 1)),
163+
)
126164
.unwrap();
127165
assert_eq!(
128166
DeepNormalizer::normalize_deep(&mut table, interner, &a),

chalk-engine/src/simplify.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use crate::{ExClause, Literal, TimeStamp};
55
use chalk_ir::cast::Cast;
66
use chalk_ir::interner::Interner;
77
use chalk_ir::{
8-
Environment, Fallible, Goal, GoalData, InEnvironment, QuantifierKind, Substitution,
8+
Environment, FallibleOrFloundered, Goal, GoalData, InEnvironment, QuantifierKind, Substitution,
9+
Variance,
910
};
1011
use tracing::debug;
1112

@@ -19,7 +20,7 @@ impl<I: Interner> Forest<I> {
1920
subst: Substitution<I>,
2021
initial_environment: Environment<I>,
2122
initial_goal: Goal<I>,
22-
) -> Fallible<ExClause<I>> {
23+
) -> FallibleOrFloundered<ExClause<I>> {
2324
let mut ex_clause = ExClause {
2425
subst,
2526
ambiguous: false,
@@ -65,13 +66,37 @@ impl<I: Interner> Forest<I> {
6566
subgoal.clone(),
6667
)));
6768
}
68-
GoalData::EqGoal(goal) => infer.unify_generic_args_into_ex_clause(
69+
GoalData::EqGoal(goal) => match infer.relate_generic_args_into_ex_clause(
6970
context.program().interner(),
71+
context.unification_database(),
7072
&environment,
73+
Variance::Invariant,
7174
&goal.a,
7275
&goal.b,
7376
&mut ex_clause,
74-
)?,
77+
) {
78+
Ok(()) => {}
79+
Err(_) => return FallibleOrFloundered::NoSolution,
80+
},
81+
GoalData::SubtypeGoal(goal) => {
82+
if goal.a.inference_var(context.program().interner()).is_some()
83+
&& goal.b.inference_var(context.program().interner()).is_some()
84+
{
85+
return FallibleOrFloundered::Floundered;
86+
}
87+
match infer.relate_tys_into_ex_clause(
88+
context.program().interner(),
89+
context.unification_database(),
90+
&environment,
91+
Variance::Covariant,
92+
&goal.a,
93+
&goal.b,
94+
&mut ex_clause,
95+
) {
96+
Ok(()) => {}
97+
Err(_) => return FallibleOrFloundered::NoSolution,
98+
}
99+
}
75100
GoalData::DomainGoal(domain_goal) => {
76101
ex_clause
77102
.subgoals
@@ -87,6 +112,6 @@ impl<I: Interner> Forest<I> {
87112
}
88113
}
89114

90-
Ok(ex_clause)
115+
FallibleOrFloundered::Ok(ex_clause)
91116
}
92117
}

chalk-engine/src/slg.rs

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use chalk_ir::cast::Caster;
66
use chalk_ir::interner::Interner;
77
use chalk_ir::*;
88
use chalk_solve::infer::ucanonicalize::UCanonicalized;
9-
use chalk_solve::infer::unify::UnificationResult;
9+
use chalk_solve::infer::unify::RelationResult;
1010
use chalk_solve::infer::InferenceTable;
1111
use chalk_solve::solve::truncate;
1212
use chalk_solve::RustIrDatabase;
@@ -81,6 +81,10 @@ impl<I: Interner> SlgContextOps<'_, I> {
8181
pub(crate) fn max_size(&self) -> usize {
8282
self.max_size
8383
}
84+
85+
pub(crate) fn unification_database(&self) -> &dyn UnificationDatabase<I> {
86+
self.program.unification_database()
87+
}
8488
}
8589

8690
/// "Truncation" (called "abstraction" in the papers referenced below)
@@ -111,6 +115,7 @@ pub trait ResolventOps<I: Interner> {
111115
/// The bindings in `infer` are unaffected by this operation.
112116
fn resolvent_clause(
113117
&mut self,
118+
ops: &dyn UnificationDatabase<I>,
114119
interner: &I,
115120
environment: &Environment<I>,
116121
goal: &DomainGoal<I>,
@@ -121,6 +126,7 @@ pub trait ResolventOps<I: Interner> {
121126
fn apply_answer_subst(
122127
&mut self,
123128
interner: &I,
129+
unification_database: &dyn UnificationDatabase<I>,
124130
ex_clause: &mut ExClause<I>,
125131
selected_goal: &InEnvironment<Goal<I>>,
126132
answer_table_goal: &Canonical<InEnvironment<Goal<I>>>,
@@ -187,14 +193,27 @@ pub trait UnificationOps<I: Interner> {
187193
///
188194
/// If the parameters fail to unify, then `Error` is returned
189195
// Used by: simplify
190-
fn unify_generic_args_into_ex_clause(
196+
fn relate_generic_args_into_ex_clause(
191197
&mut self,
192198
interner: &I,
199+
db: &dyn UnificationDatabase<I>,
193200
environment: &Environment<I>,
201+
variance: Variance,
194202
a: &GenericArg<I>,
195203
b: &GenericArg<I>,
196204
ex_clause: &mut ExClause<I>,
197205
) -> Fallible<()>;
206+
207+
fn relate_tys_into_ex_clause(
208+
&mut self,
209+
interner: &I,
210+
db: &dyn UnificationDatabase<I>,
211+
environment: &Environment<I>,
212+
variance: Variance,
213+
a: &Ty<I>,
214+
b: &Ty<I>,
215+
ex_clause: &mut ExClause<I>,
216+
) -> Fallible<()>;
198217
}
199218

200219
#[derive(Clone)]
@@ -305,23 +324,43 @@ impl<I: Interner> UnificationOps<I> for TruncatingInferenceTable<I> {
305324
self.infer.invert(interner, value)
306325
}
307326

308-
fn unify_generic_args_into_ex_clause(
327+
fn relate_generic_args_into_ex_clause(
309328
&mut self,
310329
interner: &I,
330+
db: &dyn UnificationDatabase<I>,
311331
environment: &Environment<I>,
332+
variance: Variance,
312333
a: &GenericArg<I>,
313334
b: &GenericArg<I>,
314335
ex_clause: &mut ExClause<I>,
315336
) -> Fallible<()> {
316-
let result = self.infer.unify(interner, environment, a, b)?;
337+
let result = self
338+
.infer
339+
.relate(interner, db, environment, variance, a, b)?;
340+
Ok(into_ex_clause(interner, result, ex_clause))
341+
}
342+
343+
fn relate_tys_into_ex_clause(
344+
&mut self,
345+
interner: &I,
346+
db: &dyn UnificationDatabase<I>,
347+
environment: &Environment<I>,
348+
variance: Variance,
349+
a: &Ty<I>,
350+
b: &Ty<I>,
351+
ex_clause: &mut ExClause<I>,
352+
) -> Fallible<()> {
353+
let result = self
354+
.infer
355+
.relate(interner, db, environment, variance, a, b)?;
317356
Ok(into_ex_clause(interner, result, ex_clause))
318357
}
319358
}
320359

321360
/// Helper function
322361
fn into_ex_clause<I: Interner>(
323362
interner: &I,
324-
result: UnificationResult<I>,
363+
result: RelationResult<I>,
325364
ex_clause: &mut ExClause<I>,
326365
) {
327366
ex_clause.subgoals.extend(

0 commit comments

Comments
 (0)