Support custom CoerceUnsized

Split out tests about coercion
This commit is contained in:
uHOOCCOOHu 2019-09-19 00:36:12 +08:00
parent bf161fa3e5
commit 5807e261c2
No known key found for this signature in database
GPG key ID: CED392DE0C483D00
4 changed files with 476 additions and 247 deletions

View file

@ -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<ToTy>` 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<ToTy>`
/// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
///
/// 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<bool> {
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<to_ty>`, 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<bool> {
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<U>`
(
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<Foo<..., U, ...>> if:
// - T: Unsize<U>
// - 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<T>: Unsize<Bar<U>>, if the last field of Foo has type Bar<T>
(
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) => {

View file

@ -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<i32>) {
);
}
#[test]
fn infer_if_coerce() {
assert_snapshot!(
infer(r#"
fn foo<T>(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<i32>(&[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<T>(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<i32>(&[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<T>(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<i32>(&[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<T>(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<i32>(&[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}

View file

@ -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<T: ?Sized> {}
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T> {}
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<T: ?Sized + Unsize<U>, 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<T: ?Sized>(*const T);
struct B<T: ?Sized>(*const T);
struct C<T: ?Sized> { inner: *const T }
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {}
fn foo1<T>(x: A<[T]>) -> A<[T]> { x }
fn foo2<T>(x: B<[T]>) -> B<[T]> { x }
fn foo3<T>(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<u8>(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<u8>(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<T>(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<i32>(&[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<T>(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<i32>(&[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<T>(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<i32>(&[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<T>(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<i32>(&[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
"###
);
}

View file

@ -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>() -> 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>() -> 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>() -> 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<|>;
}
"#,
);