From ac961b261458bfeb23f7d4e896d5f957b0854a3a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 6 Dec 2019 12:45:00 +0100 Subject: Add test for unifying impl Trait --- crates/ra_hir_ty/src/tests/traits.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'crates/ra_hir_ty/src') diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 6139adb72..a926d01e5 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1,4 +1,4 @@ -use super::{infer, type_at, type_at_pos}; +use super::{infer, infer_with_mismatches, type_at, type_at_pos}; use crate::test_db::TestDB; use insta::assert_snapshot; use ra_db::fixture::WithFixture; @@ -1486,3 +1486,29 @@ fn test() where T: Trait, U: Trait { // this is a legitimate cycle assert_eq!(t, "{unknown}"); } + +#[test] +fn unify_impl_trait() { + assert_snapshot!( + infer_with_mismatches(r#" +trait Trait {} + +fn foo(x: impl Trait) { loop {} } +fn bar(x: impl Trait) -> T { loop {} } + +struct S(T); +impl Trait for S {} + +fn default() -> T { loop {} } + +fn test() -> impl Trait { + let s1 = S(default()); + foo(s1); + let x: i32 = bar(S(default())); + S(default()) +} +"#, true), + @r###" + "### + ); +} -- cgit v1.2.3 From 6e1c2d0df89a390be33c81b6e03a5ad352763593 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 15 Dec 2019 18:56:38 +0100 Subject: Handle impl Trait more correctly When calling a function, argument-position impl Trait is transparent; same for return-position impl Trait when inside the function. So in these cases, we need to represent that type not by `Ty::Opaque`, but by a type variable that can be unified with whatever flows into there. --- crates/ra_hir_ty/src/infer.rs | 25 ++++++++++++++++++++++++- crates/ra_hir_ty/src/infer/expr.rs | 1 + crates/ra_hir_ty/src/tests/traits.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) (limited to 'crates/ra_hir_ty/src') diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index af42854cc..1aa1330a6 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -274,6 +274,28 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.normalize_associated_types_in(ty) } + /// Replaces `impl Trait` in `ty` by type variables and obligations for + /// those variables. This is done for function arguments when calling a + /// function, and for return types when inside the function body, i.e. in + /// the cases where the `impl Trait` is 'transparent'. In other cases, `impl + /// Trait` is represented by `Ty::Opaque`. + fn insert_vars_for_impl_trait(&mut self, ty: Ty) -> Ty { + ty.fold(&mut |ty| match ty { + Ty::Opaque(preds) => { + let var = self.table.new_type_var(); + let var_subst = Substs::builder(1).push(var.clone()).build(); + self.obligations.extend( + preds + .iter() + .map(|pred| pred.clone().subst_bound_vars(&var_subst)) + .filter_map(Obligation::from_predicate), + ); + var + } + _ => ty, + }) + } + /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { match ty { @@ -414,7 +436,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.infer_pat(*pat, &ty, BindingMode::default()); } - self.return_ty = self.make_ty(&data.ret_type); + let return_ty = self.make_ty(&data.ret_type); + self.return_ty = self.insert_vars_for_impl_trait(return_ty); } fn infer_body(&mut self) { diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 2e3cdd53a..924ad3e81 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -613,6 +613,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { continue; } + let param_ty = self.insert_vars_for_impl_trait(param_ty); let param_ty = self.normalize_associated_types_in(param_ty); self.infer_expr_coerce(arg, &Expectation::has_type(param_ty.clone())); } diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index a926d01e5..d8673c90d 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1509,6 +1509,37 @@ fn test() -> impl Trait { } "#, true), @r###" + [27; 28) 'x': impl Trait + [47; 58) '{ loop {} }': () + [49; 56) 'loop {}': ! + [54; 56) '{}': () + [69; 70) 'x': impl Trait + [92; 103) '{ loop {} }': T + [94; 101) 'loop {}': ! + [99; 101) '{}': () + [172; 183) '{ loop {} }': T + [174; 181) 'loop {}': ! + [179; 181) '{}': () + [214; 310) '{ ...t()) }': S + [224; 226) 's1': S + [229; 230) 'S': S(T) -> S + [229; 241) 'S(default())': S + [231; 238) 'default': fn default() -> T + [231; 240) 'default()': u32 + [247; 250) 'foo': fn foo(impl Trait) -> () + [247; 254) 'foo(s1)': () + [251; 253) 's1': S + [264; 265) 'x': i32 + [273; 276) 'bar': fn bar(impl Trait) -> T + [273; 290) 'bar(S(...lt()))': i32 + [277; 278) 'S': S(T) -> S + [277; 289) 'S(default())': S + [279; 286) 'default': fn default() -> T + [279; 288) 'default()': i32 + [296; 297) 'S': S(T) -> S + [296; 308) 'S(default())': S + [298; 305) 'default': fn default() -> T + [298; 307) 'default()': i32 "### ); } -- cgit v1.2.3 From 91853590a9ee78406e892ca92305edef3a5b9213 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 15 Dec 2019 21:06:08 +0100 Subject: Add test mark --- crates/ra_hir_ty/src/infer.rs | 2 ++ crates/ra_hir_ty/src/marks.rs | 1 + crates/ra_hir_ty/src/tests/traits.rs | 8 ++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'crates/ra_hir_ty/src') diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 1aa1330a6..98ba05fc2 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -32,6 +32,7 @@ use hir_def::{ use hir_expand::{diagnostics::DiagnosticSink, name::name}; use ra_arena::map::ArenaMap; use ra_prof::profile; +use test_utils::tested_by; use super::{ primitive::{FloatTy, IntTy}, @@ -282,6 +283,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn insert_vars_for_impl_trait(&mut self, ty: Ty) -> Ty { ty.fold(&mut |ty| match ty { Ty::Opaque(preds) => { + tested_by!(insert_vars_for_impl_trait); let var = self.table.new_type_var(); let var_subst = Substs::builder(1).push(var.clone()).build(); self.obligations.extend( diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs index 0f754eb9c..fe74acf11 100644 --- a/crates/ra_hir_ty/src/marks.rs +++ b/crates/ra_hir_ty/src/marks.rs @@ -6,4 +6,5 @@ test_utils::marks!( type_var_resolves_to_int_var match_ergonomics_ref coerce_merge_fail_fallback + insert_vars_for_impl_trait ); diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index d8673c90d..802937cb0 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1,7 +1,10 @@ -use super::{infer, infer_with_mismatches, type_at, type_at_pos}; -use crate::test_db::TestDB; use insta::assert_snapshot; + use ra_db::fixture::WithFixture; +use test_utils::covers; + +use super::{infer, infer_with_mismatches, type_at, type_at_pos}; +use crate::test_db::TestDB; #[test] fn infer_await() { @@ -1489,6 +1492,7 @@ fn test() where T: Trait, U: Trait { #[test] fn unify_impl_trait() { + covers!(insert_vars_for_impl_trait); assert_snapshot!( infer_with_mismatches(r#" trait Trait {} -- cgit v1.2.3