Skip to content

Commit ee655db

Browse files
super-tupledaboross
authored andcommitted
Fix ignored var ty ordering in relate_ty_ty
relate_ty_ty was ignoring the order in which the var and ty were passed in when matching a var and a ty. This mean't if the var was passed in second, the variance would be incorrectly inverted because the parameters were swapped around. We fixed this by inverting the variance if the var is on the right side, and added a test case for the issue. We also had to switch the order we pass the value + generalized value into relate_ty_ty, since the previous incorrect ordering was hidden by the bug. Co-authored-by: David Ross <daboross@daboross.net>
1 parent 7fc9755 commit ee655db

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

chalk-solve/src/infer/unify.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ impl<'t, I: Interner> Unifier<'t, I> {
7878
Ok(RelationResult { goals: self.goals })
7979
}
8080

81+
/// Relate `a`, `b` with the variance such that if `variance = Covariant`, `a` is
82+
/// a subtype of `b`.
8183
fn relate_ty_ty(&mut self, variance: Variance, a: &Ty<I>, b: &Ty<I>) -> Fallible<()> {
8284
let interner = self.interner;
8385

@@ -158,17 +160,31 @@ impl<'t, I: Interner> Unifier<'t, I> {
158160

159161
// FIXME: needs to handle relating a var and ty; needs generalization
160162
// Relating an inference variable with a non-inference variable.
161-
(&TyKind::InferenceVar(var, kind), ty_data @ _)
162-
| (ty_data @ _, &TyKind::InferenceVar(var, kind)) => {
163-
let ty = ty_data.clone().intern(interner);
163+
(a_data @ &TyKind::InferenceVar(_, _), b_data @ _)
164+
| (a_data @ _, b_data @ &TyKind::InferenceVar(_, _)) => {
165+
// Correct variance when we flip a/b.
166+
let (new_variance, var, kind, ty_data) = match (a_data, b_data) {
167+
(&TyKind::InferenceVar(var, kind), ty_data @ _) => {
168+
(variance, var, kind, ty_data)
169+
}
170+
(ty_data @ _, &TyKind::InferenceVar(var, kind)) => {
171+
// var is grabbed from 'b', but given in as the left
172+
// parameter to relate_var_ty, so we need to flip variance.
173+
(variance.invert(), var, kind, ty_data)
174+
}
175+
_ => unreachable!(),
176+
};
164177

178+
let ty = ty_data.clone().intern(interner);
165179
match (kind, ty.is_integer(interner), ty.is_float(interner)) {
166180
// General inference variables can unify with any type
167181
(TyVariableKind::General, _, _)
168182
// Integer inference variables can only unify with integer types
169183
| (TyVariableKind::Integer, true, _)
170184
// Float inference variables can only unify with float types
171-
| (TyVariableKind::Float, _, true) => self.relate_var_ty(variance, var, &ty),
185+
| (TyVariableKind::Float, _, true) => {
186+
self.relate_var_ty(new_variance, var, &ty)
187+
},
172188
_ => Err(NoSolution),
173189
}
174190
}
@@ -799,7 +815,7 @@ impl<'t, I: Interner> Unifier<'t, I> {
799815
.unwrap();
800816
debug!("var {:?} set to {:?}", var, generalized_val);
801817

802-
self.relate_ty_ty(variance, &ty1, &generalized_val)?;
818+
self.relate_ty_ty(variance, &generalized_val, &ty1)?;
803819

804820
debug!(
805821
"generalized version {:?} related to original {:?}",
@@ -1026,6 +1042,8 @@ impl<'t, I: Interner> Unifier<'t, I> {
10261042
Ok(())
10271043
}
10281044

1045+
/// Relate `a`, `b` such that if `variance = Covariant`, `a` is a subtype of
1046+
/// `b` and thus `a` must outlive `b`.
10291047
fn push_lifetime_outlives_goals(&mut self, variance: Variance, a: Lifetime<I>, b: Lifetime<I>) {
10301048
debug!(
10311049
"pushing lifetime outlives goals for a={:?} b={:?} with variance {:?}",
@@ -1049,6 +1067,7 @@ impl<'t, I: Interner> Unifier<'t, I> {
10491067
}
10501068
}
10511069

1070+
/// Pushes a goal of `a` being a subtype of `b`.
10521071
fn push_subtype_goal(&mut self, a: Ty<I>, b: Ty<I>) {
10531072
let subtype_goal = GoalData::SubtypeGoal(SubtypeGoal { a, b }).intern(self.interner());
10541073
self.goals

tests/test/subtype.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,38 @@ fn multi_lifetime() {
169169
}
170170
}
171171

172+
/// Tests that the generalizer correctly generalizes lifetimes when given an
173+
/// inference var on the left hand side.
174+
#[test]
175+
fn multi_lifetime_inverted() {
176+
test! {
177+
program {}
178+
179+
goal {
180+
forall<'a, 'b> {
181+
exists<U> {
182+
Subtype(U, &'a u32),
183+
Subtype(U, &'b u32)
184+
}
185+
}
186+
} yields {
187+
// Without the generalizer, we would yield a result like this:
188+
//
189+
// "Unique; substitution [?0 := (&'!1_1 Uint(U32))], lifetime
190+
// constraints [InEnvironment { environment: Env([]), goal: '!1_1: '!1_0
191+
// }]"
192+
//
193+
// This is incorrect, as we shouldn't be requiring 'a and 'b to be
194+
// related to eachother. Instead, U should be &'?1 u32, with constraints
195+
// ?1 : 'a, ?1: 'b.
196+
"Unique; for<?U1> { substitution [?0 := (&'^0.0 Uint(U32))], lifetime constraints [\
197+
InEnvironment { environment: Env([]), goal: '^0.0: '!1_0 }, \
198+
InEnvironment { environment: Env([]), goal: '^0.0: '!1_1 } \
199+
]}"
200+
}
201+
}
202+
}
203+
172204
/// Tests that we handle variance for covariant structs correctly.
173205
#[test]
174206
fn multi_lifetime_covariant_struct() {

0 commit comments

Comments
 (0)