From f63cfd5fca6180b51d3b00896b119def32314b71 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 12 Aug 2019 00:11:15 +0300 Subject: Tests --- crates/ra_hir/src/ty/tests.rs | 104 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index c5818b738..0f53f156e 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3594,3 +3594,107 @@ fn no_such_field_diagnostics() { "### ); } + +#[cfg(test)] +mod match_with_never_tests { + use super::type_at; + + #[test] + fn match_compex_arm_ty() { + let t = type_at( + r#" +//- /main.rs +enum Option { + Some(T), + None +} + +fn test(a: i32) { + let i = match a { + 2 => Option::Some(2.0), + _ => loop {}, + }; + i<|> + () +} +"#, + ); + assert_eq!(t, "Option"); + } + + #[test] + fn match_first_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( + 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 match_all_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( + r#" +//- /main.rs +fn test(a: i32) { + let i = match a { + 2 => 2.0, + _ => 3.0, + }; + i<|> + () +} +"#, + ); + assert_eq!(t, "f64"); + } +} -- cgit v1.2.3 From 0ce05633a170076a3624a79ed96a40c1730a8164 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 12 Aug 2019 01:01:15 +0300 Subject: Fix match type inference for Never match arms --- crates/ra_hir/src/ty/infer.rs | 36 +++++++++++++++++++++++++++++++----- crates/ra_hir/src/ty/tests.rs | 2 +- 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index d94e8154b..dc7101075 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -280,8 +280,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty1 = self.resolve_ty_shallow(ty1); let ty2 = self.resolve_ty_shallow(ty2); match (&*ty1, &*ty2) { - (Ty::Unknown, ..) => true, - (.., Ty::Unknown) => true, + (Ty::Unknown, _) | (_, Ty::Unknown) => true, (Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => { self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1) } @@ -297,7 +296,9 @@ 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))) => { + | (other, Ty::Infer(InferTy::FloatVar(tv))) + if !Self::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())); true @@ -1080,6 +1081,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; let input_ty = self.infer_expr(*expr, &Expectation::none()); + let mut resulting_match_ty = None; + let mut all_arms_never = !arms.is_empty(); + for arm in arms { for &pat in &arm.pats { let _pat_ty = self.infer_pat(pat, &input_ty, BindingMode::default()); @@ -1090,10 +1094,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { &Expectation::has_type(Ty::simple(TypeCtor::Bool)), ); } - self.infer_expr(arm.expr, &expected); + let arm_ty = self.infer_expr(arm.expr, &expected); + if all_arms_never && Self::is_never(&arm_ty) { + resulting_match_ty = Some(arm_ty); + } else { + all_arms_never = false; + resulting_match_ty = None; + } } - expected.ty + if let (Ty::Infer(expected_tv), Some(match_ty)) = + (&expected.ty, &resulting_match_ty) + { + self.var_unification_table + .union_value(expected_tv.to_inner(), TypeVarValue::Known(match_ty.clone())); + match_ty.clone() + } else { + expected.ty + } } Expr::Path(p) => { // FIXME this could be more efficient... @@ -1368,6 +1386,14 @@ 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], diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 0f53f156e..6bf56959d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3600,7 +3600,7 @@ mod match_with_never_tests { use super::type_at; #[test] - fn match_compex_arm_ty() { + fn match_complex_arm_ty() { let t = type_at( r#" //- /main.rs -- cgit v1.2.3 From c1f47c37886b5cba116ba99fa37977d51871eba4 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 12 Aug 2019 23:06:32 +0300 Subject: Add test marks --- crates/ra_hir/src/marks.rs | 5 +++++ crates/ra_hir/src/ty/infer.rs | 5 +++++ crates/ra_hir/src/ty/tests.rs | 7 ++++++- 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index 5b15eee90..3795debc1 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs @@ -11,4 +11,9 @@ test_utils::marks!( match_ergonomics_ref trait_resolution_on_fn_type infer_while_let + match_complex_arm_ty + match_first_arm_never + match_second_arm_never + match_all_arms_never + match_no_never_arms ); diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index dc7101075..b310bf6bd 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -1096,8 +1096,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } 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; } @@ -1106,10 +1108,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { 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); + tested_by!(match_complex_arm_ty); expected.ty } } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 6bf56959d..7ec834836 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3595,12 +3595,13 @@ fn no_such_field_diagnostics() { ); } -#[cfg(test)] mod match_with_never_tests { use super::type_at; + use test_utils::covers; #[test] fn match_complex_arm_ty() { + covers!(match_complex_arm_ty); let t = type_at( r#" //- /main.rs @@ -3624,6 +3625,7 @@ fn test(a: i32) { #[test] fn match_first_arm_never() { + covers!(match_first_arm_never); let t = type_at( r#" //- /main.rs @@ -3644,6 +3646,7 @@ fn test(a: i32) { #[test] fn match_second_arm_never() { + covers!(match_second_arm_never); let t = type_at( r#" //- /main.rs @@ -3664,6 +3667,7 @@ fn test(a: i32) { #[test] fn match_all_arms_never() { + covers!(match_all_arms_never); let t = type_at( r#" //- /main.rs @@ -3682,6 +3686,7 @@ 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 From 44cf7b34fe1a486168590f7fead442f12602c419 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 14 Aug 2019 22:20:18 +0300 Subject: Fix never in if expressions --- crates/ra_hir/src/marks.rs | 2 ++ crates/ra_hir/src/ty/infer.rs | 13 ++++++++++--- crates/ra_hir/src/ty/tests.rs | 42 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 4 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index 3795debc1..1adf5cd53 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs @@ -16,4 +16,6 @@ test_utils::marks!( 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 b310bf6bd..d01063766 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -987,14 +987,21 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let then_ty = self.infer_expr(*then_branch, expected); match else_branch { Some(else_branch) => { - self.infer_expr(*else_branch, expected); + 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 + } } None => { // no else branch -> unit self.unify(&then_ty, &Ty::unit()); // actually coerce + then_ty } - }; - then_ty + } } Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected), Expr::TryBlock { body } => { diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 7ec834836..94b0fe3b3 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3595,7 +3595,7 @@ fn no_such_field_diagnostics() { ); } -mod match_with_never_tests { +mod branching_with_never_tests { use super::type_at; use test_utils::covers; @@ -3644,6 +3644,46 @@ fn test(a: i32) { assert_eq!(t, "f64"); } + #[test] + fn if_never() { + covers!(if_never); + let t = type_at( + r#" +//- /main.rs +fn test() { + let i = if true { + loop {} + } else { + 3.0 + }; + i<|> + () +} +"#, + ); + assert_eq!(t, "f64"); + } + + #[test] + fn if_else_never() { + covers!(if_else_never); + let t = type_at( + r#" +//- /main.rs +fn test(input: bool) { + let i = if input { + 2.0 + } else { + return + }; + i<|> + () +} +"#, + ); + assert_eq!(t, "f64"); + } + #[test] fn match_second_arm_never() { covers!(match_second_arm_never); -- cgit v1.2.3 From 8b612251fd8c741416d2fb320bd908b76134fde5 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 14 Aug 2019 22:58:17 +0300 Subject: Remove extra inference test --- crates/ra_hir/src/marks.rs | 1 - crates/ra_hir/src/ty/infer.rs | 1 - crates/ra_hir/src/ty/tests.rs | 24 ------------------------ 3 files changed, 26 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index 1adf5cd53..f450aef9f 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs @@ -11,7 +11,6 @@ test_utils::marks!( match_ergonomics_ref trait_resolution_on_fn_type infer_while_let - match_complex_arm_ty match_first_arm_never match_second_arm_never match_all_arms_never diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index d01063766..7b59ebfe7 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -1121,7 +1121,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match_ty.clone() } else { tested_by!(match_no_never_arms); - tested_by!(match_complex_arm_ty); expected.ty } } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 94b0fe3b3..195514f10 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3599,30 +3599,6 @@ mod branching_with_never_tests { use super::type_at; use test_utils::covers; - #[test] - fn match_complex_arm_ty() { - covers!(match_complex_arm_ty); - let t = type_at( - r#" -//- /main.rs -enum Option { - Some(T), - None -} - -fn test(a: i32) { - let i = match a { - 2 => Option::Some(2.0), - _ => loop {}, - }; - i<|> - () -} -"#, - ); - assert_eq!(t, "Option"); - } - #[test] fn match_first_arm_never() { covers!(match_first_arm_never); -- cgit v1.2.3 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') 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 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 From e37b6c58378355347898cbdf1eef278137f6b461 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 26 Aug 2019 20:12:41 +0200 Subject: Make infer_block not unify; add back calculate_least_upper_bound --- crates/ra_hir/src/ty/infer.rs | 74 +++++++++++++++++++++++++++++++------------ crates/ra_hir/src/ty/tests.rs | 22 +++++++------ 2 files changed, 66 insertions(+), 30 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 074baa8ef..0aa993067 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -975,48 +975,44 @@ 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 + /// This is similar to unify, but it makes the first type coerce to the + /// second one. + fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { + if is_never(from_ty) { + // ! coerces to any type + true } 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 + self.unify(from_ty, to_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() }, ); } + let ty = self.resolve_ty_as_possible(&mut vec![], ty); ty } fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { let body = Arc::clone(&self.body); // avoid borrow checker problem - match &body[tgt_expr] { + let ty = 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 then_ty = self.infer_expr_inner(*then_branch, &expected); - self.coerce(*then_branch, then_ty.clone(), expected.ty.clone()); + self.coerce(&then_ty, &expected.ty); match else_branch { Some(else_branch) => { let else_ty = self.infer_expr_inner(*else_branch, &expected); - self.coerce(*else_branch, else_ty, expected.ty.clone()); + self.coerce(&else_ty, &expected.ty); } None => { // no else branch -> unit @@ -1111,6 +1107,8 @@ 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()); @@ -1121,11 +1119,16 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { &Expectation::has_type(Ty::simple(TypeCtor::Bool)), ); } - let match_arm_ty = self.infer_expr_inner(arm.expr, &expected); - self.coerce(arm.expr, match_arm_ty, expected.ty.clone()); + arm_tys.push(self.infer_expr_inner(arm.expr, &expected)); } - expected.ty + let lub_ty = calculate_least_upper_bound(expected.ty.clone(), &arm_tys); + + for arm_ty in &arm_tys { + self.coerce(arm_ty, &lub_ty); + } + + lub_ty } Expr::Path(p) => { // FIXME this could be more efficient... @@ -1385,7 +1388,12 @@ 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 ty = self.resolve_ty_as_possible(&mut vec![], ty); + self.write_expr_ty(tgt_expr, ty.clone()); + ty } fn infer_block( @@ -1414,7 +1422,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } } - let ty = if let Some(expr) = tail { self.infer_expr(expr, expected) } else { Ty::unit() }; + let ty = if let Some(expr) = tail { self.infer_expr_inner(expr, expected) } else { Ty::unit() }; ty } @@ -1644,3 +1652,29 @@ fn is_never(ty: &Ty) -> bool { false } } + +fn calculate_least_upper_bound(expected_ty: Ty, actual_tys: &[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.clone()); + } 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.clone(), + _ => 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 4fa9d131d..7fc9fbb63 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -718,16 +718,18 @@ fn main(foo: Foo) { } "#), @r###" -[35; 38) 'foo': Foo -[45; 109) '{ ... } }': () -[51; 107) 'if tru... }': () -[54; 58) 'true': bool -[59; 67) '{ }': () -[73; 107) 'if fal... }': i32 -[76; 81) 'false': bool -[82; 107) '{ ... }': i32 -[92; 95) 'foo': Foo -[92; 101) 'foo.field': i32"### + ⋮ + ⋮[35; 38) 'foo': Foo + ⋮[45; 109) '{ ... } }': () + ⋮[51; 107) 'if tru... }': () + ⋮[54; 58) 'true': bool + ⋮[59; 67) '{ }': () + ⋮[73; 107) 'if fal... }': () + ⋮[76; 81) 'false': bool + ⋮[82; 107) '{ ... }': i32 + ⋮[92; 95) 'foo': Foo + ⋮[92; 101) 'foo.field': i32 + "### ) } -- cgit v1.2.3 From c256a72b039e849414245c563692feb41d1b051c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 26 Aug 2019 20:41:31 +0200 Subject: Fix 'missing Ok in tail expr' validation Because of the coercion change, the type mismatch now only happens on the block expression. --- crates/ra_hir/src/expr/validation.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs index 5d9d59ff8..c99c9c869 100644 --- a/crates/ra_hir/src/expr/validation.rs +++ b/crates/ra_hir/src/expr/validation.rs @@ -41,7 +41,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { let body_expr = &body[body.body_expr()]; if let Expr::Block { statements: _, tail: Some(t) } = body_expr { - self.validate_results_in_tail_expr(*t, db); + self.validate_results_in_tail_expr(body.body_expr(), *t, db); } } @@ -97,8 +97,9 @@ impl<'a, 'b> ExprValidator<'a, 'b> { } } - fn validate_results_in_tail_expr(&mut self, id: ExprId, db: &impl HirDatabase) { - let mismatch = match self.infer.type_mismatch_for_expr(id) { + fn validate_results_in_tail_expr(&mut self, body_id: ExprId, id: ExprId, db: &impl HirDatabase) { + // the mismatch will be on the whole block currently + let mismatch = match self.infer.type_mismatch_for_expr(body_id) { Some(m) => m, None => return, }; -- cgit v1.2.3 From 590aed2eec869a1b64c5d3c07a26dd59c669004b Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 26 Aug 2019 23:00:27 +0300 Subject: Remove redundant tests --- crates/ra_hir/src/ty/tests.rs | 68 ++++--------------------------------------- 1 file changed, 5 insertions(+), 63 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 7fc9fbb63..e3eb0c3fa 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3600,46 +3600,6 @@ fn no_such_field_diagnostics() { mod branching_with_never_tests { use super::type_at; - #[test] - fn match_first_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_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( @@ -3679,16 +3639,16 @@ fn test(input: bool) { } #[test] - fn match_first_block_arm_never() { + fn match_first_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 }, + 1 => return, + 2 => 2.0, + 3 => loop {}, + _ => 3.0, }; i<|> () @@ -3736,24 +3696,6 @@ 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 From 4adfdea1ad5aca393fa5bb9ff40fdc05827fcd56 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 26 Aug 2019 23:00:35 +0300 Subject: Small fixes --- crates/ra_hir/src/expr/validation.rs | 7 ++++++- crates/ra_hir/src/ty/infer.rs | 21 +++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs index c99c9c869..c8ae19869 100644 --- a/crates/ra_hir/src/expr/validation.rs +++ b/crates/ra_hir/src/expr/validation.rs @@ -97,7 +97,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> { } } - fn validate_results_in_tail_expr(&mut self, body_id: ExprId, id: ExprId, db: &impl HirDatabase) { + fn validate_results_in_tail_expr( + &mut self, + body_id: ExprId, + id: ExprId, + db: &impl HirDatabase, + ) { // the mismatch will be on the whole block currently let mismatch = match self.infer.type_mismatch_for_expr(body_id) { Some(m) => m, diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 0aa993067..812990426 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -1009,16 +1009,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let then_ty = self.infer_expr_inner(*then_branch, &expected); self.coerce(&then_ty, &expected.ty); - match else_branch { - Some(else_branch) => { - let else_ty = self.infer_expr_inner(*else_branch, &expected); - self.coerce(&else_ty, &expected.ty); - } - None => { - // no else branch -> unit - self.unify(&then_ty, &Ty::unit()); // actually coerce - } + + let else_ty = match else_branch { + Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), + None => Ty::unit(), }; + self.coerce(&else_ty, &expected.ty); expected.ty.clone() } @@ -1422,7 +1418,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } } - let ty = if let Some(expr) = tail { self.infer_expr_inner(expr, expected) } else { Ty::unit() }; + let ty = + if let Some(expr) = tail { self.infer_expr_inner(expr, expected) } else { Ty::unit() }; ty } @@ -1665,8 +1662,8 @@ fn calculate_least_upper_bound(expected_ty: Ty, actual_tys: &[Ty]) -> Ty { all_never = false; least_upper_bound = match (actual_ty, &least_upper_bound) { (_, Ty::Unknown) - | (Ty::Infer(_), Ty::Infer(InferTy::TypeVar(_))) - | (Ty::Apply(_), _) => actual_ty.clone(), + | (Ty::Infer(_), Ty::Infer(InferTy::TypeVar(_))) + | (Ty::Apply(_), _) => actual_ty.clone(), _ => least_upper_bound, } } -- cgit v1.2.3