mirror of https://github.com/rust-lang/rust.git
Merge #491
491: Fix assertion error in unification (hopefully) r=flodiebold a=flodiebold Currently, all types that we handle during inference need to be resolved as far as possible at the time. It's maybe too brittle of an invariant; I need to think how we can do this better. This should fix #484 though, I hope (if it's the same case as I managed to reproduce). Co-authored-by: Florian Diebold <florian.diebold@freiheit.com>
This commit is contained in:
commit
cca39ebc24
|
@ -18,6 +18,7 @@ mod primitive;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Index;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem};
|
||||
|
@ -671,7 +672,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
|
||||
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||
match (ty1, ty2) {
|
||||
// try to resolve type vars first
|
||||
let ty1 = self.resolve_ty_shallow(ty1);
|
||||
let ty2 = self.resolve_ty_shallow(ty2);
|
||||
match (&*ty1, &*ty2) {
|
||||
(Ty::Unknown, ..) => true,
|
||||
(.., Ty::Unknown) => true,
|
||||
(Ty::Bool, _)
|
||||
|
@ -698,10 +702,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
.zip(ts2.iter())
|
||||
.all(|(t1, t2)| self.unify(t1, t2)),
|
||||
(Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) => {
|
||||
// both type vars are unknown since we tried to resolve them
|
||||
self.var_unification_table.union(*tv1, *tv2);
|
||||
true
|
||||
}
|
||||
(Ty::Infer(InferTy::TypeVar(tv)), other) | (other, Ty::Infer(InferTy::TypeVar(tv))) => {
|
||||
// the type var is unknown since we tried to resolve it
|
||||
self.var_unification_table
|
||||
.union_value(*tv, TypeVarValue::Known(other.clone()));
|
||||
true
|
||||
|
@ -746,6 +752,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
})
|
||||
}
|
||||
|
||||
/// If `ty` is a type variable with known type, returns that type;
|
||||
/// otherwise, return ty.
|
||||
fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
|
||||
match ty {
|
||||
Ty::Infer(InferTy::TypeVar(tv)) => {
|
||||
match self.var_unification_table.probe_value(*tv).known() {
|
||||
Some(known_ty) => {
|
||||
// The known_ty can't be a type var itself
|
||||
Cow::Owned(known_ty.clone())
|
||||
}
|
||||
_ => Cow::Borrowed(ty),
|
||||
}
|
||||
}
|
||||
_ => Cow::Borrowed(ty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the type completely; type variables without known type are
|
||||
/// replaced by Ty::Unknown.
|
||||
fn resolve_ty_completely(&mut self, ty: Ty) -> Ty {
|
||||
|
@ -816,12 +839,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
// if let is desugared to match, so this is always simple if
|
||||
self.infer_expr(*condition, &Expectation::has_type(Ty::Bool))?;
|
||||
let then_ty = self.infer_expr(*then_branch, expected)?;
|
||||
if let Some(else_branch) = else_branch {
|
||||
match else_branch {
|
||||
Some(else_branch) => {
|
||||
self.infer_expr(*else_branch, expected)?;
|
||||
} else {
|
||||
// no else branch -> unit
|
||||
self.unify(&expected.ty, &Ty::unit()); // actually coerce
|
||||
}
|
||||
None => {
|
||||
// no else branch -> unit
|
||||
self.unify(&then_ty, &Ty::unit()); // actually coerce
|
||||
}
|
||||
};
|
||||
then_ty
|
||||
}
|
||||
Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected)?,
|
||||
|
|
|
@ -230,6 +230,18 @@ fn test2(a1: *const A, a2: *mut A) {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_bug_484() {
|
||||
check_inference(
|
||||
r#"
|
||||
fn test() {
|
||||
let x = if true {};
|
||||
}
|
||||
"#,
|
||||
"bug_484.txt",
|
||||
);
|
||||
}
|
||||
|
||||
fn infer(content: &str) -> String {
|
||||
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
||||
let source_file = db.source_file(file_id);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
[11; 37) '{ l... {}; }': ()
|
||||
[20; 21) 'x': ()
|
||||
[24; 34) 'if true {}': ()
|
||||
[27; 31) 'true': bool
|
||||
[32; 34) '{}': ()
|
Loading…
Reference in New Issue