mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
Support custom CoerceUnsized
Split out tests about coercion
This commit is contained in:
parent
bf161fa3e5
commit
5807e261c2
4 changed files with 476 additions and 247 deletions
|
@ -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) => {
|
||||
|
|
|
@ -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}
|
||||
|
|
278
crates/ra_hir/src/ty/tests/coercion.rs
Normal file
278
crates/ra_hir/src/ty/tests/coercion.rs
Normal 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
|
||||
"###
|
||||
);
|
||||
}
|
|
@ -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<|>;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue