mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
An attempt to add the coercion logic for Never
This commit is contained in:
parent
89f3cc587d
commit
44386d5373
2 changed files with 98 additions and 51 deletions
|
@ -296,9 +296,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
| (Ty::Infer(InferTy::IntVar(tv)), other)
|
||||
| (other, Ty::Infer(InferTy::IntVar(tv)))
|
||||
| (Ty::Infer(InferTy::FloatVar(tv)), other)
|
||||
| (other, Ty::Infer(InferTy::FloatVar(tv)))
|
||||
if !is_never(other) =>
|
||||
{
|
||||
| (other, Ty::Infer(InferTy::FloatVar(tv))) => {
|
||||
// the type var is unknown since we tried to resolve it
|
||||
self.var_unification_table.union_value(*tv, TypeVarValue::Known(other.clone()));
|
||||
true
|
||||
|
@ -977,27 +975,56 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
ret_ty
|
||||
}
|
||||
|
||||
fn coerce(&mut self, tgt_expr: ExprId, ty1: Ty, ty2: Ty) -> Ty {
|
||||
if is_never(&ty1) {
|
||||
ty2
|
||||
} else {
|
||||
self.unify(&ty1, &ty2);
|
||||
// TODO Fugly and looks like we need more, `infer_adt_pattern` and other fails
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty1);
|
||||
self.write_expr_ty(tgt_expr, ty.clone());
|
||||
ty
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
let ty = self.infer_expr_inner(tgt_expr, expected);
|
||||
// use a new type variable if we got Ty::Unknown here
|
||||
let ty = self.insert_type_vars_shallow(ty);
|
||||
let could_unify = self.unify(&ty, &expected.ty);
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
|
||||
self.write_expr_ty(tgt_expr, ty.clone());
|
||||
if !could_unify {
|
||||
self.result.type_mismatches.insert(
|
||||
tgt_expr,
|
||||
TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
|
||||
);
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
||||
fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
let body = Arc::clone(&self.body); // avoid borrow checker problem
|
||||
let ty = match &body[tgt_expr] {
|
||||
match &body[tgt_expr] {
|
||||
Expr::Missing => Ty::Unknown,
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
// if let is desugared to match, so this is always simple if
|
||||
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
|
||||
|
||||
let mut branch_tys = Vec::with_capacity(2);
|
||||
let then_ty = self.infer_expr(*then_branch, &expected);
|
||||
let then_ty = self.infer_expr_inner(*then_branch, &expected);
|
||||
self.coerce(*then_branch, then_ty.clone(), expected.ty.clone());
|
||||
match else_branch {
|
||||
Some(else_branch) => {
|
||||
branch_tys.push(self.infer_expr(*else_branch, &expected));
|
||||
let else_ty = self.infer_expr_inner(*else_branch, &expected);
|
||||
self.coerce(*else_branch, else_ty, expected.ty.clone());
|
||||
}
|
||||
None => {
|
||||
// no else branch -> unit
|
||||
self.unify(&then_ty, &Ty::unit()); // actually coerce
|
||||
}
|
||||
};
|
||||
branch_tys.push(then_ty);
|
||||
calculate_least_upper_bound(expected.ty.clone(), branch_tys)
|
||||
|
||||
expected.ty.clone()
|
||||
}
|
||||
Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected),
|
||||
Expr::TryBlock { body } => {
|
||||
|
@ -1084,8 +1111,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
expected.clone()
|
||||
};
|
||||
|
||||
let mut arm_tys = Vec::with_capacity(arms.len());
|
||||
|
||||
for arm in arms {
|
||||
for &pat in &arm.pats {
|
||||
let _pat_ty = self.infer_pat(pat, &input_ty, BindingMode::default());
|
||||
|
@ -1096,9 +1121,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
&Expectation::has_type(Ty::simple(TypeCtor::Bool)),
|
||||
);
|
||||
}
|
||||
arm_tys.push(self.infer_expr(arm.expr, &expected));
|
||||
let match_arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
||||
self.coerce(arm.expr, match_arm_ty, expected.ty.clone());
|
||||
}
|
||||
calculate_least_upper_bound(expected.ty.clone(), arm_tys)
|
||||
|
||||
expected.ty
|
||||
}
|
||||
Expr::Path(p) => {
|
||||
// FIXME this could be more efficient...
|
||||
|
@ -1358,19 +1385,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int(*ty)),
|
||||
Literal::Float(_v, ty) => Ty::simple(TypeCtor::Float(*ty)),
|
||||
},
|
||||
};
|
||||
// use a new type variable if we got Ty::Unknown here
|
||||
let ty = self.insert_type_vars_shallow(ty);
|
||||
let could_unify = self.unify(&ty, &expected.ty);
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
|
||||
self.write_expr_ty(tgt_expr, ty.clone());
|
||||
if !could_unify {
|
||||
self.result.type_mismatches.insert(
|
||||
tgt_expr,
|
||||
TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
|
||||
);
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
||||
fn infer_block(
|
||||
|
@ -1629,29 +1644,3 @@ fn is_never(ty: &Ty) -> bool {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_least_upper_bound(expected_ty: Ty, actual_tys: Vec<Ty>) -> Ty {
|
||||
let mut all_never = true;
|
||||
let mut last_never_ty = None;
|
||||
let mut least_upper_bound = expected_ty;
|
||||
|
||||
for actual_ty in actual_tys {
|
||||
if is_never(&actual_ty) {
|
||||
last_never_ty = Some(actual_ty);
|
||||
} else {
|
||||
all_never = false;
|
||||
least_upper_bound = match (&actual_ty, &least_upper_bound) {
|
||||
(_, Ty::Unknown)
|
||||
| (Ty::Infer(_), Ty::Infer(InferTy::TypeVar(_)))
|
||||
| (Ty::Apply(_), _) => actual_ty,
|
||||
_ => least_upper_bound,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if all_never && last_never_ty.is_some() {
|
||||
last_never_ty.unwrap()
|
||||
} else {
|
||||
least_upper_bound
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3618,6 +3618,26 @@ fn test(a: i32) {
|
|||
assert_eq!(t, "f64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_second_block_arm_never() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
fn test(a: i32) {
|
||||
let i = match a {
|
||||
1 => { 3.0 },
|
||||
2 => { loop {} },
|
||||
3 => { 3.0 },
|
||||
_ => { return },
|
||||
};
|
||||
i<|>
|
||||
()
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "f64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_never() {
|
||||
let t = type_at(
|
||||
|
@ -3656,6 +3676,26 @@ fn test(input: bool) {
|
|||
assert_eq!(t, "f64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_first_block_arm_never() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
fn test(a: i32) {
|
||||
let i = match a {
|
||||
1 => { return },
|
||||
2 => { 2.0 },
|
||||
3 => { loop {} },
|
||||
_ => { 3.0 },
|
||||
};
|
||||
i<|>
|
||||
()
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "f64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_second_arm_never() {
|
||||
let t = type_at(
|
||||
|
@ -3694,6 +3734,24 @@ fn test(a: i32) {
|
|||
assert_eq!(t, "!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_all_block_arms_never() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
fn test(a: i32) {
|
||||
let i = match a {
|
||||
2 => { return },
|
||||
_ => { loop {} },
|
||||
};
|
||||
i<|>
|
||||
()
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_no_never_arms() {
|
||||
let t = type_at(
|
||||
|
|
Loading…
Reference in a new issue