Properly coerce never types

This commit is contained in:
Kirill Bulatov 2019-08-15 23:53:42 +03:00
parent 8b612251fd
commit 89f3cc587d
3 changed files with 46 additions and 57 deletions

View file

@ -11,10 +11,4 @@ test_utils::marks!(
match_ergonomics_ref
trait_resolution_on_fn_type
infer_while_let
match_first_arm_never
match_second_arm_never
match_all_arms_never
match_no_never_arms
if_never
if_else_never
);

View file

@ -297,7 +297,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
| (other, Ty::Infer(InferTy::IntVar(tv)))
| (Ty::Infer(InferTy::FloatVar(tv)), other)
| (other, Ty::Infer(InferTy::FloatVar(tv)))
if !Self::is_never(other) =>
if !is_never(other) =>
{
// the type var is unknown since we tried to resolve it
self.var_unification_table.union_value(*tv, TypeVarValue::Known(other.clone()));
@ -984,24 +984,20 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
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 then_ty = self.infer_expr(*then_branch, expected);
let mut branch_tys = Vec::with_capacity(2);
let then_ty = self.infer_expr(*then_branch, &expected);
match else_branch {
Some(else_branch) => {
let else_ty = self.infer_expr(*else_branch, expected);
if Self::is_never(&then_ty) {
tested_by!(if_never);
else_ty
} else {
tested_by!(if_else_never);
then_ty
}
branch_tys.push(self.infer_expr(*else_branch, &expected));
}
None => {
// no else branch -> unit
self.unify(&then_ty, &Ty::unit()); // actually coerce
then_ty
}
}
};
branch_tys.push(then_ty);
calculate_least_upper_bound(expected.ty.clone(), branch_tys)
}
Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected),
Expr::TryBlock { body } => {
@ -1081,15 +1077,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
Expr::MethodCall { receiver, args, method_name, generic_args } => self
.infer_method_call(tgt_expr, *receiver, &args, &method_name, generic_args.as_ref()),
Expr::Match { expr, arms } => {
let input_ty = self.infer_expr(*expr, &Expectation::none());
let expected = if expected.ty == Ty::Unknown {
Expectation::has_type(self.new_type_var())
} else {
expected.clone()
};
let input_ty = self.infer_expr(*expr, &Expectation::none());
let mut resulting_match_ty = None;
let mut all_arms_never = !arms.is_empty();
let mut arm_tys = Vec::with_capacity(arms.len());
for arm in arms {
for &pat in &arm.pats {
@ -1101,28 +1096,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
&Expectation::has_type(Ty::simple(TypeCtor::Bool)),
);
}
let arm_ty = self.infer_expr(arm.expr, &expected);
if all_arms_never && Self::is_never(&arm_ty) {
tested_by!(match_first_arm_never);
resulting_match_ty = Some(arm_ty);
} else {
tested_by!(match_second_arm_never);
all_arms_never = false;
resulting_match_ty = None;
}
}
if let (Ty::Infer(expected_tv), Some(match_ty)) =
(&expected.ty, &resulting_match_ty)
{
tested_by!(match_all_arms_never);
self.var_unification_table
.union_value(expected_tv.to_inner(), TypeVarValue::Known(match_ty.clone()));
match_ty.clone()
} else {
tested_by!(match_no_never_arms);
expected.ty
arm_tys.push(self.infer_expr(arm.expr, &expected));
}
calculate_least_upper_bound(expected.ty.clone(), arm_tys)
}
Expr::Path(p) => {
// FIXME this could be more efficient...
@ -1397,14 +1373,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
ty
}
fn is_never(ty: &Ty) -> bool {
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) = ty {
true
} else {
false
}
}
fn infer_block(
&mut self,
statements: &[Statement],
@ -1653,3 +1621,37 @@ mod diagnostics {
}
}
}
fn is_never(ty: &Ty) -> bool {
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) = ty {
true
} else {
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
}
}

View file

@ -3597,11 +3597,9 @@ fn no_such_field_diagnostics() {
mod branching_with_never_tests {
use super::type_at;
use test_utils::covers;
#[test]
fn match_first_arm_never() {
covers!(match_first_arm_never);
let t = type_at(
r#"
//- /main.rs
@ -3622,7 +3620,6 @@ fn test(a: i32) {
#[test]
fn if_never() {
covers!(if_never);
let t = type_at(
r#"
//- /main.rs
@ -3642,7 +3639,6 @@ fn test() {
#[test]
fn if_else_never() {
covers!(if_else_never);
let t = type_at(
r#"
//- /main.rs
@ -3662,7 +3658,6 @@ fn test(input: bool) {
#[test]
fn match_second_arm_never() {
covers!(match_second_arm_never);
let t = type_at(
r#"
//- /main.rs
@ -3683,7 +3678,6 @@ fn test(a: i32) {
#[test]
fn match_all_arms_never() {
covers!(match_all_arms_never);
let t = type_at(
r#"
//- /main.rs
@ -3702,7 +3696,6 @@ fn test(a: i32) {
#[test]
fn match_no_never_arms() {
covers!(match_no_never_arms);
let t = type_at(
r#"
//- /main.rs