blob: eac42c1a9f7ed43f627dc615750564ea8dad6ff5 [file] [log] [blame]
#![cfg(test)]
use super::unify::UnificationResult;
use super::*;
use chalk_integration::interner::ChalkIr;
#[test]
fn infer() {
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let environment0 = Environment::new(interner);
let a = table.new_variable(U0).to_ty(interner);
let b = table.new_variable(U0).to_ty(interner);
table
.unify(interner, &environment0, &a, &ty!(apply (item 0) (expr b)))
.unwrap();
assert_eq!(
table.normalize_deep(interner, &a),
ty!(apply (item 0) (expr b))
);
table
.unify(interner, &environment0, &b, &ty!(apply (item 1)))
.unwrap();
assert_eq!(
table.normalize_deep(interner, &a),
ty!(apply (item 0) (apply (item 1)))
);
}
#[test]
fn universe_error() {
// exists(A -> forall(X -> A = X)) ---> error
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let environment0 = Environment::new(interner);
let a = table.new_variable(U0).to_ty(interner);
table
.unify(interner, &environment0, &a, &ty!(placeholder 1))
.unwrap_err();
}
#[test]
fn cycle_error() {
// exists(A -> A = foo A) ---> error
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let environment0 = Environment::new(interner);
let a = table.new_variable(U0).to_ty(interner);
table
.unify(interner, &environment0, &a, &ty!(apply (item 0) (expr a)))
.unwrap_err();
// exists(A -> A = for<'a> A)
table
.unify(interner, &environment0, &a, &ty!(function 1 (infer 0)))
.unwrap_err();
}
#[test]
fn cycle_indirect() {
// exists(A -> A = foo B, A = B) ---> error
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let environment0 = Environment::new(interner);
let a = table.new_variable(U0).to_ty(interner);
let b = table.new_variable(U0).to_ty(interner);
table
.unify(interner, &environment0, &a, &ty!(apply (item 0) (expr b)))
.unwrap();
table.unify(interner, &environment0, &a, &b).unwrap_err();
}
#[test]
fn universe_error_indirect_1() {
// exists(A -> forall(X -> exists(B -> B = X, A = B))) ---> error
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let environment0 = Environment::new(interner);
let a = table.new_variable(U0).to_ty(interner);
let b = table.new_variable(U1).to_ty(interner);
table
.unify(interner, &environment0, &b, &ty!(placeholder 1))
.unwrap();
table.unify(interner, &environment0, &a, &b).unwrap_err();
}
#[test]
fn universe_error_indirect_2() {
// exists(A -> forall(X -> exists(B -> B = A, B = X))) ---> error
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let environment0 = Environment::new(interner);
let a = table.new_variable(U0).to_ty(interner);
let b = table.new_variable(U1).to_ty(interner);
table.unify(interner, &environment0, &a, &b).unwrap();
table
.unify(interner, &environment0, &b, &ty!(placeholder 1))
.unwrap_err();
}
#[test]
fn universe_promote() {
// exists(A -> forall(X -> exists(B -> A = foo(B), A = foo(i32)))) ---> OK
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let environment0 = Environment::new(interner);
let a = table.new_variable(U0).to_ty(interner);
let b = table.new_variable(U1).to_ty(interner);
table
.unify(interner, &environment0, &a, &ty!(apply (item 0) (expr b)))
.unwrap();
table
.unify(
interner,
&environment0,
&a,
&ty!(apply (item 0) (apply (item 1))),
)
.unwrap();
}
#[test]
fn universe_promote_bad() {
// exists(A -> forall(X -> exists(B -> A = foo(B), B = X))) ---> error
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let environment0 = Environment::new(interner);
let a = table.new_variable(U0).to_ty(interner);
let b = table.new_variable(U1).to_ty(interner);
table
.unify(interner, &environment0, &a, &ty!(apply (item 0) (expr b)))
.unwrap();
table
.unify(interner, &environment0, &b, &ty!(placeholder 1))
.unwrap_err();
}
#[test]
fn projection_eq() {
// exists(A -> A = Item0<<A as Item1>::foo>)
// ^^^^^^^^^^^^ Can A repeat here? For now,
// we say no, but it's an interesting question.
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let environment0 = Environment::new(interner);
let a = table.new_variable(U0).to_ty(interner);
// expect an error ("cycle during unification")
table
.unify(
interner,
&environment0,
&a,
&ty!(apply (item 0) (projection (item 1) (expr a))),
)
.unwrap_err();
}
const U0: UniverseIndex = UniverseIndex { counter: 0 };
const U1: UniverseIndex = UniverseIndex { counter: 1 };
const U2: UniverseIndex = UniverseIndex { counter: 2 };
fn make_table() -> InferenceTable<ChalkIr> {
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let _ = table.new_universe(); // U1
let _ = table.new_universe(); // U2
table
}
#[test]
fn quantify_simple() {
let interner = &ChalkIr;
let mut table = make_table();
let _ = table.new_variable(U0);
let _ = table.new_variable(U1);
let _ = table.new_variable(U2);
assert_eq!(
table
.canonicalize(interner, &ty!(apply (item 0) (infer 2) (infer 1) (infer 0)))
.quantified,
Canonical {
value: ty!(apply (item 0) (bound 0) (bound 1) (bound 2)),
binders: CanonicalVarKinds::from(
interner,
vec![
CanonicalVarKind::new(VariableKind::Ty(TyKind::General), U2),
CanonicalVarKind::new(VariableKind::Ty(TyKind::General), U1),
CanonicalVarKind::new(VariableKind::Ty(TyKind::General), U0),
]
),
}
);
}
#[test]
fn quantify_bound() {
let interner = &ChalkIr;
let mut table = make_table();
let environment0 = Environment::new(interner);
let v0 = table.new_variable(U0).to_ty(interner);
let v1 = table.new_variable(U1).to_ty(interner);
let v2a = table.new_variable(U2).to_ty(interner);
let v2b = table.new_variable(U2).to_ty(interner);
table
.unify(
interner,
&environment0,
&v2b,
&ty!(apply (item 1) (expr v1) (expr v0)),
)
.unwrap();
assert_eq!(
table
.canonicalize(
interner,
&ty!(apply (item 0) (expr v2b) (expr v2a) (expr v1) (expr v0))
)
.quantified,
Canonical {
value: ty!(apply (item 0) (apply (item 1) (bound 0) (bound 1)) (bound 2) (bound 0) (bound 1)),
binders: CanonicalVarKinds::from(
interner,
vec![
CanonicalVarKind::new(VariableKind::Ty(TyKind::General), U1),
CanonicalVarKind::new(VariableKind::Ty(TyKind::General), U0),
CanonicalVarKind::new(VariableKind::Ty(TyKind::General), U2),
]
),
}
);
}
#[test]
fn quantify_ty_under_binder() {
let interner = &ChalkIr;
let mut table = make_table();
let v0 = table.new_variable(U0);
let v1 = table.new_variable(U0);
let _r2 = table.new_variable(U0);
// Unify v0 and v1.
let environment0 = Environment::new(interner);
table
.unify(
interner,
&environment0,
&v0.to_ty(interner),
&v1.to_ty(interner),
)
.unwrap();
// Here: the `function` introduces 3 binders, so in the result,
// `(bound 3)` references the first canonicalized inference
// variable. -- note that `infer 0` and `infer 1` have been
// unified above, as well.
assert_eq!(
table
.canonicalize(
interner,
&ty!(function 3 (apply (item 0) (bound 1) (infer 0) (infer 1) (lifetime (infer 2))))
)
.quantified,
Canonical {
value: ty!(function 3 (apply (item 0) (bound 1) (bound 1 0) (bound 1 0) (lifetime (bound 1 1)))),
binders: CanonicalVarKinds::from(
interner,
vec![
CanonicalVarKind::new(VariableKind::Ty(TyKind::General), U0),
CanonicalVarKind::new(VariableKind::Lifetime, U0)
]
),
}
);
}
#[test]
fn lifetime_constraint_indirect() {
let interner = &ChalkIr;
let mut table: InferenceTable<ChalkIr> = InferenceTable::new();
let _ = table.new_universe(); // U1
let _t_0 = table.new_variable(U0);
let _l_1 = table.new_variable(U1);
let environment0 = Environment::new(interner);
// Here, we unify '?1 (the lifetime variable in universe 1) with
// '!1.
let t_a = ty!(apply (item 0) (lifetime (placeholder 1)));
let t_b = ty!(apply (item 0) (lifetime (infer 1)));
let UnificationResult { goals, constraints } =
table.unify(interner, &environment0, &t_a, &t_b).unwrap();
assert!(goals.is_empty());
assert!(constraints.is_empty());
// Here, we try to unify `?0` (the type variable in universe 0)
// with something that involves `'?1`. Since `'?1` has been
// unified with `'!1`, and `'!1` is not visible from universe 0,
// we will replace `'!1` with a new variable `'?2` and introduce a
// (likely unsatisfiable) constraint relating them.
let t_c = ty!(infer 0);
let UnificationResult { goals, constraints } =
table.unify(interner, &environment0, &t_c, &t_b).unwrap();
assert!(goals.is_empty());
assert_eq!(constraints.len(), 2);
assert_eq!(
format!("{:?}", constraints[0]),
"InEnvironment { environment: Env([]), goal: \'?2: \'!1_0 }",
);
assert_eq!(
format!("{:?}", constraints[1]),
"InEnvironment { environment: Env([]), goal: \'!1_0: \'?2 }",
);
}