From 5807e261c27e4964fc6a8d2b8cf02e548292b940 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Thu, 19 Sep 2019 00:36:12 +0800 Subject: Support custom `CoerceUnsized` Split out tests about coercion --- crates/ra_hir/src/ty/infer.rs | 207 ++++++++++++++++++++--- crates/ra_hir/src/ty/tests.rs | 202 +--------------------- crates/ra_hir/src/ty/tests/coercion.rs | 278 +++++++++++++++++++++++++++++++ crates/ra_hir/src/ty/tests/never_type.rs | 36 ++-- 4 files changed, 476 insertions(+), 247 deletions(-) create mode 100644 crates/ra_hir/src/ty/tests/coercion.rs (limited to 'crates') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index cbbba8b23..ba63050a9 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -42,6 +42,7 @@ use crate::{ RecordFieldPat, Statement, UnaryOp, }, generics::{GenericParams, HasGenericParams}, + lang_item::LangItemTarget, name, nameres::Namespace, path::{known, GenericArg, GenericArgs}, @@ -188,6 +189,12 @@ struct InferenceContext<'a, D: HirDatabase> { result: InferenceResult, /// The return type of the function being inferred. return_ty: Ty, + + /// Impls of `CoerceUnsized` used in coercion. + /// (from_ty_ctor, to_ty_ctor) => coerce_generic_index + // FIXME: Use trait solver for this. + // Chalk seems unable to work well with builtin impl of `Unsize` now. + coerce_unsized_map: FxHashMap<(TypeCtor, TypeCtor), usize>, } macro_rules! ty_app { @@ -207,12 +214,52 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { obligations: Vec::default(), return_ty: Ty::Unknown, // set in collect_fn_signature trait_env: lower::trait_env(db, &resolver), + coerce_unsized_map: Self::init_coerce_unsized_map(db, &resolver), db, body, resolver, } } + fn init_coerce_unsized_map( + db: &'a D, + resolver: &Resolver, + ) -> FxHashMap<(TypeCtor, TypeCtor), usize> { + let krate = resolver.krate().unwrap(); + let impls = match db.lang_item(krate, "coerce_unsized".into()) { + Some(LangItemTarget::Trait(trait_)) => db.impls_for_trait(krate, trait_), + _ => return FxHashMap::default(), + }; + + impls + .iter() + .filter_map(|impl_block| { + // `CoerseUnsized` has one generic parameter for the target type. + let trait_ref = impl_block.target_trait_ref(db)?; + let cur_from_ty = trait_ref.substs.0.get(0)?; + let cur_to_ty = trait_ref.substs.0.get(1)?; + + match (&cur_from_ty, cur_to_ty) { + (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) => { + // FIXME: We return the first non-equal bound as the type parameter to coerce to unsized type. + // This works for smart-pointer-like coercion, which covers all impls from std. + st1.iter().zip(st2.iter()).enumerate().find_map(|(i, (ty1, ty2))| { + match (ty1, ty2) { + (Ty::Param { idx: p1, .. }, Ty::Param { idx: p2, .. }) + if p1 != p2 => + { + Some(((*ctor1, *ctor2), i)) + } + _ => None, + } + }) + } + _ => None, + } + }) + .collect() + } + fn resolve_all(mut self) -> InferenceResult { // FIXME resolve obligations as well (use Guidance if necessary) let mut result = mem::replace(&mut self.result, InferenceResult::default()); @@ -919,16 +966,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { _ => {} } - // FIXME: Solve `FromTy: CoerceUnsized` instead of listing common impls here. - match (&from_ty, &to_ty) { - // Mutilibity is checked above - (ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) - | (ty_app!(TypeCtor::RawPtr(_), st1), ty_app!(TypeCtor::RawPtr(_), st2)) => { - if self.try_coerce_unsized(&st1[0], &st2[0], 0) { - return true; - } - } - _ => {} + if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { + return ret; } // Auto Deref if cannot coerce @@ -943,10 +982,43 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } - /// Coerce a type to a DST if `FromTy: Unsize` + /// Coerce a type using `from_ty: CoerceUnsized` /// - /// See: `https://doc.rust-lang.org/nightly/std/marker/trait.Unsize.html` - fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty, depth: usize) -> bool { + /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html + fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option { + let (ctor1, st1, ctor2, st2) = match (from_ty, to_ty) { + (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) => (ctor1, st1, ctor2, st2), + _ => return None, + }; + + let coerce_generic_index = *self.coerce_unsized_map.get(&(*ctor1, *ctor2))?; + + // Check `Unsize` first + match self.check_unsize_and_coerce( + st1.0.get(coerce_generic_index)?, + st2.0.get(coerce_generic_index)?, + 0, + ) { + Some(true) => {} + ret => return ret, + } + + let ret = st1 + .iter() + .zip(st2.iter()) + .enumerate() + .filter(|&(idx, _)| idx != coerce_generic_index) + .all(|(_, (ty1, ty2))| self.unify(ty1, ty2)); + + Some(ret) + } + + /// Check if `from_ty: Unsize`, and coerce to `to_ty` if it holds. + /// + /// It should not be directly called. It is only used by `try_coerce_unsized`. + /// + /// See: https://doc.rust-lang.org/nightly/std/marker/trait.Unsize.html + fn check_unsize_and_coerce(&mut self, from_ty: &Ty, to_ty: &Ty, depth: usize) -> Option { if depth > 1000 { panic!("Infinite recursion in coercion"); } @@ -954,29 +1026,113 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match (&from_ty, &to_ty) { // `[T; N]` -> `[T]` (ty_app!(TypeCtor::Array, st1), ty_app!(TypeCtor::Slice, st2)) => { - self.unify(&st1[0], &st2[0]) + Some(self.unify(&st1[0], &st2[0])) } // `T` -> `dyn Trait` when `T: Trait` (_, Ty::Dyn(_)) => { // FIXME: Check predicates - true + Some(true) } + // `(..., T)` -> `(..., U)` when `T: Unsize` + ( + ty_app!(TypeCtor::Tuple { cardinality: len1 }, st1), + ty_app!(TypeCtor::Tuple { cardinality: len2 }, st2), + ) => { + if len1 != len2 || *len1 == 0 { + return None; + } + + match self.check_unsize_and_coerce( + st1.last().unwrap(), + st2.last().unwrap(), + depth + 1, + ) { + Some(true) => {} + ret => return ret, + } + + let ret = st1[..st1.len() - 1] + .iter() + .zip(&st2[..st2.len() - 1]) + .all(|(ty1, ty2)| self.unify(ty1, ty2)); + + Some(ret) + } + + // Foo<..., T, ...> is Unsize> if: + // - T: Unsize + // - Foo is a struct + // - Only the last field of Foo has a type involving T + // - T is not part of the type of any other fields + // - Bar: Unsize>, if the last field of Foo has type Bar ( ty_app!(TypeCtor::Adt(Adt::Struct(struct1)), st1), ty_app!(TypeCtor::Adt(Adt::Struct(struct2)), st2), ) if struct1 == struct2 => { - // FIXME: Check preconditions here - for (ty1, ty2) in st1.iter().zip(st2.iter()) { - if !self.try_coerce_unsized(ty1, ty2, depth + 1) { - return false; + let fields = struct1.fields(self.db); + let (last_field, prev_fields) = fields.split_last()?; + + // Get the generic parameter involved in the last field. + let unsize_generic_index = { + let mut index = None; + let mut multiple_param = false; + last_field.ty(self.db).walk(&mut |ty| match ty { + &Ty::Param { idx, .. } => { + if index.is_none() { + index = Some(idx); + } else if Some(idx) != index { + multiple_param = true; + } + } + _ => {} + }); + + if multiple_param { + return None; } + index? + }; + + // Check other fields do not involve it. + let mut multiple_used = false; + prev_fields.iter().for_each(|field| { + field.ty(self.db).walk(&mut |ty| match ty { + &Ty::Param { idx, .. } if idx == unsize_generic_index => { + multiple_used = true + } + _ => {} + }) + }); + if multiple_used { + return None; } - true + + let unsize_generic_index = unsize_generic_index as usize; + + // Check `Unsize` first + match self.check_unsize_and_coerce( + st1.get(unsize_generic_index)?, + st2.get(unsize_generic_index)?, + depth + 1, + ) { + Some(true) => {} + ret => return ret, + } + + // Then unify other parameters + let ret = st1 + .iter() + .zip(st2.iter()) + .enumerate() + .filter(|&(idx, _)| idx != unsize_generic_index) + .all(|(_, (ty1, ty2))| self.unify(ty1, ty2)); + + Some(ret) } - _ => false, + _ => None, } } @@ -1433,12 +1589,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let decl_ty = type_ref.as_ref().map(|tr| self.make_ty(tr)).unwrap_or(Ty::Unknown); let decl_ty = self.insert_type_vars(decl_ty); - let ty = if let Some(expr) = initializer { - self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty)) - } else { - decl_ty - }; + if let Some(expr) = initializer { + self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone())); + } + let ty = self.resolve_ty_as_possible(&mut vec![], decl_ty); self.infer_pat(*pat, &ty, BindingMode::default()); } Statement::Expr(expr) => { diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 081bfe37c..cd5a05092 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -21,6 +21,7 @@ use crate::{ // update the snapshots. mod never_type; +mod coercion; #[test] fn infer_await() { @@ -925,196 +926,6 @@ fn test(a: A) { ); } -#[test] -fn infer_if_coerce() { - assert_snapshot!( - infer(r#" -fn foo(x: &[T]) -> &[T] { loop {} } -fn test() { - let x = if true { - foo(&[1]) - } else { - &[1] - }; -} -"#), - @r###" - [11; 12) 'x': &[T] - [28; 39) '{ loop {} }': ! - [30; 37) 'loop {}': ! - [35; 37) '{}': () - [50; 126) '{ ... }; }': () - [60; 61) 'x': &[i32] - [64; 123) 'if tru... }': &[i32] - [67; 71) 'true': bool - [72; 97) '{ ... }': &[i32] - [82; 85) 'foo': fn foo(&[T]) -> &[T] - [82; 91) 'foo(&[1])': &[i32] - [86; 90) '&[1]': &[i32;_] - [87; 90) '[1]': [i32;_] - [88; 89) '1': i32 - [103; 123) '{ ... }': &[i32;_] - [113; 117) '&[1]': &[i32;_] - [114; 117) '[1]': [i32;_] - [115; 116) '1': i32 -"### - ); -} - -#[test] -fn infer_if_else_coerce() { - assert_snapshot!( - infer(r#" -fn foo(x: &[T]) -> &[T] { loop {} } -fn test() { - let x = if true { - &[1] - } else { - foo(&[1]) - }; -} -"#), - @r###" - [11; 12) 'x': &[T] - [28; 39) '{ loop {} }': ! - [30; 37) 'loop {}': ! - [35; 37) '{}': () - [50; 126) '{ ... }; }': () - [60; 61) 'x': &[i32] - [64; 123) 'if tru... }': &[i32] - [67; 71) 'true': bool - [72; 92) '{ ... }': &[i32;_] - [82; 86) '&[1]': &[i32;_] - [83; 86) '[1]': [i32;_] - [84; 85) '1': i32 - [98; 123) '{ ... }': &[i32] - [108; 111) 'foo': fn foo(&[T]) -> &[T] - [108; 117) 'foo(&[1])': &[i32] - [112; 116) '&[1]': &[i32;_] - [113; 116) '[1]': [i32;_] - [114; 115) '1': i32 -"### - ); -} - -#[test] -fn infer_match_first_coerce() { - assert_snapshot!( - infer(r#" -fn foo(x: &[T]) -> &[T] { loop {} } -fn test(i: i32) { - let x = match i { - 2 => foo(&[2]), - 1 => &[1], - _ => &[3], - }; -} -"#), - @r###" - [11; 12) 'x': &[T] - [28; 39) '{ loop {} }': ! - [30; 37) 'loop {}': ! - [35; 37) '{}': () - [48; 49) 'i': i32 - [56; 150) '{ ... }; }': () - [66; 67) 'x': &[i32] - [70; 147) 'match ... }': &[i32] - [76; 77) 'i': i32 - [88; 89) '2': i32 - [93; 96) 'foo': fn foo(&[T]) -> &[T] - [93; 102) 'foo(&[2])': &[i32] - [97; 101) '&[2]': &[i32;_] - [98; 101) '[2]': [i32;_] - [99; 100) '2': i32 - [112; 113) '1': i32 - [117; 121) '&[1]': &[i32;_] - [118; 121) '[1]': [i32;_] - [119; 120) '1': i32 - [131; 132) '_': i32 - [136; 140) '&[3]': &[i32;_] - [137; 140) '[3]': [i32;_] - [138; 139) '3': i32 - "### - ); -} - -#[test] -fn infer_match_second_coerce() { - assert_snapshot!( - infer(r#" -fn foo(x: &[T]) -> &[T] { loop {} } -fn test(i: i32) { - let x = match i { - 1 => &[1], - 2 => foo(&[2]), - _ => &[3], - }; -} -"#), - @r###" - [11; 12) 'x': &[T] - [28; 39) '{ loop {} }': ! - [30; 37) 'loop {}': ! - [35; 37) '{}': () - [48; 49) 'i': i32 - [56; 150) '{ ... }; }': () - [66; 67) 'x': &[i32] - [70; 147) 'match ... }': &[i32] - [76; 77) 'i': i32 - [88; 89) '1': i32 - [93; 97) '&[1]': &[i32;_] - [94; 97) '[1]': [i32;_] - [95; 96) '1': i32 - [107; 108) '2': i32 - [112; 115) 'foo': fn foo(&[T]) -> &[T] - [112; 121) 'foo(&[2])': &[i32] - [116; 120) '&[2]': &[i32;_] - [117; 120) '[2]': [i32;_] - [118; 119) '2': i32 - [131; 132) '_': i32 - [136; 140) '&[3]': &[i32;_] - [137; 140) '[3]': [i32;_] - [138; 139) '3': i32 - "### - ); -} - -#[test] -fn coerce_merge_one_by_one1() { - covers!(coerce_merge_fail_fallback); - - assert_snapshot!( - infer(r#" -fn test() { - let t = &mut 1; - let x = match 1 { - 1 => t as *mut i32, - 2 => t as &i32, - _ => t as *const i32, - }; -} -"#), - @r###" - [11; 145) '{ ... }; }': () - [21; 22) 't': &mut i32 - [25; 31) '&mut 1': &mut i32 - [30; 31) '1': i32 - [41; 42) 'x': *const i32 - [45; 142) 'match ... }': *const i32 - [51; 52) '1': i32 - [63; 64) '1': i32 - [68; 69) 't': &mut i32 - [68; 81) 't as *mut i32': *mut i32 - [91; 92) '2': i32 - [96; 97) 't': &mut i32 - [96; 105) 't as &i32': &i32 - [115; 116) '_': i32 - [120; 121) 't': &mut i32 - [120; 135) 't as *const i32': *const i32 - "### - ); -} - #[test] fn bug_484() { assert_snapshot!( @@ -1302,7 +1113,6 @@ fn test(x: &str, y: isize) { } "#), @r###" - [9; 10) 'x': &str [18; 19) 'y': isize [28; 324) '{ ... 3]; }': () @@ -1355,7 +1165,7 @@ fn test(x: &str, y: isize) { [260; 263) '"b"': &str [275; 276) 'x': [u8;_] [288; 290) '[]': [u8;_] - [300; 301) 'z': &[u8;_] + [300; 301) 'z': &[u8] [311; 321) '&[1, 2, 3]': &[u8;_] [312; 321) '[1, 2, 3]': [u8;_] [313; 314) '1': u8 @@ -2288,10 +2098,9 @@ fn test() { } "#), @r###" - [11; 48) '{ ...&y]; }': () [21; 22) 'y': &{unknown} - [25; 32) 'unknown': &{unknown} + [25; 32) 'unknown': &&{unknown} [38; 45) '[y, &y]': [&&{unknown};_] [39; 40) 'y': &{unknown} [42; 44) '&y': &&{unknown} @@ -2313,12 +2122,11 @@ fn test() { } "#), @r###" - [11; 80) '{ ...x)]; }': () [21; 22) 'x': &&{unknown} - [25; 32) 'unknown': &&{unknown} + [25; 32) 'unknown': &&&{unknown} [42; 43) 'y': &&{unknown} - [46; 53) 'unknown': &&{unknown} + [46; 53) 'unknown': &&&{unknown} [59; 77) '[(x, y..., &x)]': [(&&{unknown}, &&{unknown});_] [60; 66) '(x, y)': (&&{unknown}, &&{unknown}) [61; 62) 'x': &&{unknown} diff --git a/crates/ra_hir/src/ty/tests/coercion.rs b/crates/ra_hir/src/ty/tests/coercion.rs new file mode 100644 index 000000000..d80d3fb6f --- /dev/null +++ b/crates/ra_hir/src/ty/tests/coercion.rs @@ -0,0 +1,278 @@ +use insta::assert_snapshot; +use test_utils::covers; + +// Infer with some common definitions and impls. +fn infer(source: &str) -> String { + let defs = r#" + #[lang = "sized"] + pub trait Sized {} + #[lang = "unsize"] + pub trait Unsize {} + #[lang = "coerce_unsized"] + pub trait CoerceUnsized {} + + impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} + impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} + "#; + + // Append to the end to keep positions unchanged. + super::infer(&format!("{}{}", source, defs)) +} + +#[test] +fn infer_let_stmt_coerce() { + assert_snapshot!( + infer(r#" +fn test() { + let x: &[i32] = &[1]; +} +"#), + @r###" + [11; 40) '{ ...[1]; }': () + [21; 22) 'x': &[i32] + [33; 37) '&[1]': &[i32;_] + [34; 37) '[1]': [i32;_] + [35; 36) '1': i32 + "###); +} + +#[test] +fn infer_custom_coerce_unsized() { + assert_snapshot!( + infer(r#" +struct A(*const T); +struct B(*const T); +struct C { inner: *const T } + +impl, U: ?Sized> CoerceUnsized> for B {} +impl, U: ?Sized> CoerceUnsized> for C {} + +fn foo1(x: A<[T]>) -> A<[T]> { x } +fn foo2(x: B<[T]>) -> B<[T]> { x } +fn foo3(x: C<[T]>) -> C<[T]> { x } + +fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) { + let d = foo1(a); + let e = foo2(b); + let f = foo3(c); +} +"#), + @r###" + [258; 259) 'x': A<[T]> + [279; 284) '{ x }': A<[T]> + [281; 282) 'x': A<[T]> + [296; 297) 'x': B<[T]> + [317; 322) '{ x }': B<[T]> + [319; 320) 'x': B<[T]> + [334; 335) 'x': C<[T]> + [355; 360) '{ x }': C<[T]> + [357; 358) 'x': C<[T]> + [370; 371) 'a': A<[u8;_]> + [385; 386) 'b': B<[u8;_]> + [400; 401) 'c': C<[u8;_]> + [415; 481) '{ ...(c); }': () + [425; 426) 'd': A<[{unknown}]> + [429; 433) 'foo1': fn foo1<{unknown}>(A<[T]>) -> A<[T]> + [429; 436) 'foo1(a)': A<[{unknown}]> + [434; 435) 'a': A<[u8;_]> + [446; 447) 'e': B<[u8]> + [450; 454) 'foo2': fn foo2(B<[T]>) -> B<[T]> + [450; 457) 'foo2(b)': B<[u8]> + [455; 456) 'b': B<[u8;_]> + [467; 468) 'f': C<[u8]> + [471; 475) 'foo3': fn foo3(C<[T]>) -> C<[T]> + [471; 478) 'foo3(c)': C<[u8]> + [476; 477) 'c': C<[u8;_]> + "### + ); +} + +#[test] +fn infer_if_coerce() { + assert_snapshot!( + infer(r#" +fn foo(x: &[T]) -> &[T] { loop {} } +fn test() { + let x = if true { + foo(&[1]) + } else { + &[1] + }; +} +"#), + @r###" + [11; 12) 'x': &[T] + [28; 39) '{ loop {} }': ! + [30; 37) 'loop {}': ! + [35; 37) '{}': () + [50; 126) '{ ... }; }': () + [60; 61) 'x': &[i32] + [64; 123) 'if tru... }': &[i32] + [67; 71) 'true': bool + [72; 97) '{ ... }': &[i32] + [82; 85) 'foo': fn foo(&[T]) -> &[T] + [82; 91) 'foo(&[1])': &[i32] + [86; 90) '&[1]': &[i32;_] + [87; 90) '[1]': [i32;_] + [88; 89) '1': i32 + [103; 123) '{ ... }': &[i32;_] + [113; 117) '&[1]': &[i32;_] + [114; 117) '[1]': [i32;_] + [115; 116) '1': i32 +"### + ); +} + +#[test] +fn infer_if_else_coerce() { + assert_snapshot!( + infer(r#" +fn foo(x: &[T]) -> &[T] { loop {} } +fn test() { + let x = if true { + &[1] + } else { + foo(&[1]) + }; +} +"#), + @r###" + [11; 12) 'x': &[T] + [28; 39) '{ loop {} }': ! + [30; 37) 'loop {}': ! + [35; 37) '{}': () + [50; 126) '{ ... }; }': () + [60; 61) 'x': &[i32] + [64; 123) 'if tru... }': &[i32] + [67; 71) 'true': bool + [72; 92) '{ ... }': &[i32;_] + [82; 86) '&[1]': &[i32;_] + [83; 86) '[1]': [i32;_] + [84; 85) '1': i32 + [98; 123) '{ ... }': &[i32] + [108; 111) 'foo': fn foo(&[T]) -> &[T] + [108; 117) 'foo(&[1])': &[i32] + [112; 116) '&[1]': &[i32;_] + [113; 116) '[1]': [i32;_] + [114; 115) '1': i32 +"### + ); +} + +#[test] +fn infer_match_first_coerce() { + assert_snapshot!( + infer(r#" +fn foo(x: &[T]) -> &[T] { loop {} } +fn test(i: i32) { + let x = match i { + 2 => foo(&[2]), + 1 => &[1], + _ => &[3], + }; +} +"#), + @r###" + [11; 12) 'x': &[T] + [28; 39) '{ loop {} }': ! + [30; 37) 'loop {}': ! + [35; 37) '{}': () + [48; 49) 'i': i32 + [56; 150) '{ ... }; }': () + [66; 67) 'x': &[i32] + [70; 147) 'match ... }': &[i32] + [76; 77) 'i': i32 + [88; 89) '2': i32 + [93; 96) 'foo': fn foo(&[T]) -> &[T] + [93; 102) 'foo(&[2])': &[i32] + [97; 101) '&[2]': &[i32;_] + [98; 101) '[2]': [i32;_] + [99; 100) '2': i32 + [112; 113) '1': i32 + [117; 121) '&[1]': &[i32;_] + [118; 121) '[1]': [i32;_] + [119; 120) '1': i32 + [131; 132) '_': i32 + [136; 140) '&[3]': &[i32;_] + [137; 140) '[3]': [i32;_] + [138; 139) '3': i32 + "### + ); +} + +#[test] +fn infer_match_second_coerce() { + assert_snapshot!( + infer(r#" +fn foo(x: &[T]) -> &[T] { loop {} } +fn test(i: i32) { + let x = match i { + 1 => &[1], + 2 => foo(&[2]), + _ => &[3], + }; +} +"#), + @r###" + [11; 12) 'x': &[T] + [28; 39) '{ loop {} }': ! + [30; 37) 'loop {}': ! + [35; 37) '{}': () + [48; 49) 'i': i32 + [56; 150) '{ ... }; }': () + [66; 67) 'x': &[i32] + [70; 147) 'match ... }': &[i32] + [76; 77) 'i': i32 + [88; 89) '1': i32 + [93; 97) '&[1]': &[i32;_] + [94; 97) '[1]': [i32;_] + [95; 96) '1': i32 + [107; 108) '2': i32 + [112; 115) 'foo': fn foo(&[T]) -> &[T] + [112; 121) 'foo(&[2])': &[i32] + [116; 120) '&[2]': &[i32;_] + [117; 120) '[2]': [i32;_] + [118; 119) '2': i32 + [131; 132) '_': i32 + [136; 140) '&[3]': &[i32;_] + [137; 140) '[3]': [i32;_] + [138; 139) '3': i32 + "### + ); +} + +#[test] +fn coerce_merge_one_by_one1() { + covers!(coerce_merge_fail_fallback); + + assert_snapshot!( + infer(r#" +fn test() { + let t = &mut 1; + let x = match 1 { + 1 => t as *mut i32, + 2 => t as &i32, + _ => t as *const i32, + }; +} +"#), + @r###" + [11; 145) '{ ... }; }': () + [21; 22) 't': &mut i32 + [25; 31) '&mut 1': &mut i32 + [30; 31) '1': i32 + [41; 42) 'x': *const i32 + [45; 142) 'match ... }': *const i32 + [51; 52) '1': i32 + [63; 64) '1': i32 + [68; 69) 't': &mut i32 + [68; 81) 't as *mut i32': *mut i32 + [91; 92) '2': i32 + [96; 97) 't': &mut i32 + [96; 105) 't as &i32': &i32 + [115; 116) '_': i32 + [120; 121) 't': &mut i32 + [120; 135) 't as *const i32': *const i32 + "### + ); +} diff --git a/crates/ra_hir/src/ty/tests/never_type.rs b/crates/ra_hir/src/ty/tests/never_type.rs index b9af918e9..c202f545a 100644 --- a/crates/ra_hir/src/ty/tests/never_type.rs +++ b/crates/ra_hir/src/ty/tests/never_type.rs @@ -19,12 +19,10 @@ fn infer_never2() { let t = type_at( r#" //- /main.rs -trait Foo { fn gen() -> Self; } -impl Foo for ! { fn gen() -> Self { loop {} } } -impl Foo for () { fn gen() -> Self { loop {} } } +fn gen() -> T { loop {} } fn test() { - let a = Foo::gen(); + let a = gen(); if false { a } else { loop {} }; a<|>; } @@ -38,12 +36,10 @@ fn infer_never3() { let t = type_at( r#" //- /main.rs -trait Foo { fn gen() -> Self; } -impl Foo for ! { fn gen() -> Self { loop {} } } -impl Foo for () { fn gen() -> Self { loop {} } } +fn gen() -> T { loop {} } fn test() { - let a = Foo::gen(); + let a = gen(); if false { loop {} } else { a }; a<|>; } @@ -73,12 +69,10 @@ fn never_type_can_be_reinferred1() { let t = type_at( r#" //- /main.rs -trait Foo { fn gen() -> Self; } -impl Foo for ! { fn gen() -> Self { loop {} } } -impl Foo for () { fn gen() -> Self { loop {} } } +fn gen() -> T { loop {} } fn test() { - let a = Foo::gen(); + let a = gen(); if false { loop {} } else { a }; a<|>; if false { a }; @@ -154,8 +148,7 @@ fn test() { } else { 3.0 }; - i<|> - () + i<|>; } "#, ); @@ -173,8 +166,7 @@ fn test(input: bool) { } else { return }; - i<|> - () + i<|>; } "#, ); @@ -193,8 +185,7 @@ fn test(a: i32) { 3 => loop {}, _ => 3.0, }; - i<|> - () + i<|>; } "#, ); @@ -213,8 +204,7 @@ fn test(a: i32) { 3 => 3.0, _ => return, }; - i<|> - () + i<|>; } "#, ); @@ -231,8 +221,7 @@ fn test(a: i32) { 2 => return, _ => loop {}, }; - i<|> - () + i<|>; } "#, ); @@ -249,8 +238,7 @@ fn test(a: i32) { 2 => 2.0, _ => 3.0, }; - i<|> - () + i<|>; } "#, ); -- cgit v1.2.3