From 89f3cc587d07a3cdcebf84cc4b99fe42636e66f0 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 15 Aug 2019 23:53:42 +0300 Subject: Properly coerce never types --- crates/ra_hir/src/marks.rs | 6 --- crates/ra_hir/src/ty/infer.rs | 90 ++++++++++++++++++++++--------------------- crates/ra_hir/src/ty/tests.rs | 7 ---- 3 files changed, 46 insertions(+), 57 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index f450aef9f..5b15eee90 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs @@ -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 ); diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 7b59ebfe7..3911695df 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -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 { + 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 + } +} diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 195514f10..a30a645eb 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -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 -- cgit v1.2.3