From 44386d5373114ffc88ef6bd182fb3b58a7c27e69 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 22 Aug 2019 00:34:50 +0300 Subject: An attempt to add the coercion logic for Never --- crates/ra_hir/src/ty/infer.rs | 91 +++++++++++++++++++------------------------ crates/ra_hir/src/ty/tests.rs | 58 +++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 51 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 3911695df..074baa8ef 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -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 { - 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 a30a645eb..4fa9d131d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -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( -- cgit v1.2.3