From 5205c84ec7d6284b258e66a06c3e330c3f9fdd88 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Wed, 18 Sep 2019 04:15:31 +0800 Subject: Support auto-deref in argument position --- crates/ra_hir/src/ty/infer.rs | 44 +++++++++++++++- crates/ra_hir/src/ty/tests.rs | 118 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 76b4b6faa..746b07a05 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -806,6 +806,47 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } + fn unify_with_autoderef(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { + macro_rules! ty_app { + ($ctor:pat, $param:pat) => { + Ty::Apply(ApplicationTy { ctor: $ctor, parameters: $param }) + }; + } + + // If given type and expected type are compatible reference, + // trigger auto-deref. + let (_to_mut, from_ty, to_ty) = + match (&*self.resolve_ty_shallow(&from_ty), &*self.resolve_ty_shallow(&to_ty)) { + ( + ty_app!(TypeCtor::Ref(from_mut), from_param), + ty_app!(TypeCtor::Ref(to_mut), to_param), + ) if *from_mut == Mutability::Mut || from_mut == to_mut => { + (to_mut, from_param[0].clone(), to_param[0].clone()) + } + _ => { + // Otherwise, just unify + return self.unify(&from_ty, &to_ty); + } + }; + + let canonicalized = self.canonicalizer().canonicalize_ty(from_ty); + // FIXME: Auto DerefMut + for derefed_ty in + autoderef::autoderef(self.db, &self.resolver.clone(), canonicalized.value.clone()) + { + let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value); + match (&*self.resolve_ty_shallow(&derefed_ty), &*self.resolve_ty_shallow(&to_ty)) { + // Unify when constructor matches. + (ty_app!(from_ctor, _), ty_app!(to_ctor, _)) if from_ctor == to_ctor => { + return self.unify(&derefed_ty, &to_ty); + } + _ => {} + } + } + + false + } + fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(tgt_expr, expected); let could_unify = self.unify(&ty, &expected.ty); @@ -1285,7 +1326,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } let param_ty = self.normalize_associated_types_in(param_ty); - self.infer_expr(arg, &Expectation::has_type(param_ty)); + let arg_ty = self.infer_expr_inner(arg, &Expectation::has_type(param_ty.clone())); + self.unify_with_autoderef(&arg_ty, ¶m_ty); } } } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index f4f63ca93..6076e4025 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -800,6 +800,124 @@ fn test2(a1: *const A, a2: *mut A) { ); } +#[test] +fn infer_argument_autoderef() { + assert_snapshot!( + infer(r#" +#[lang = "deref"] +pub trait Deref { + type Target: ?Sized; + fn deref(&self) -> &Self::Target; +} + +struct A(T); + +impl A { + fn foo(&self) -> T { + self.0 + } +} + +struct B(T); + +impl Deref for B { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn test() { + A::foo(&&B(B(A(42)))); +} +"#), + @r###" + [76; 80) 'self': &Self + [153; 157) 'self': &A + [164; 186) '{ ... }': T + [174; 178) 'self': &A + [174; 180) 'self.0': T + [267; 271) 'self': &B + [290; 313) '{ ... }': &T + [300; 307) '&self.0': &T + [301; 305) 'self': &B + [301; 307) 'self.0': T + [327; 357) '{ ...))); }': () + [333; 339) 'A::foo': fn foo(&A) -> T + [333; 354) 'A::foo...42))))': i32 + [340; 353) '&&B(B(A(42)))': &&B>> + [341; 353) '&B(B(A(42)))': &B>> + [342; 343) 'B': B>>(T) -> B + [342; 353) 'B(B(A(42)))': B>> + [344; 345) 'B': B>(T) -> B + [344; 352) 'B(A(42))': B> + [346; 347) 'A': A(T) -> A + [346; 351) 'A(42)': A + [348; 350) '42': i32 +"### + ); +} + +#[test] +fn infer_method_argument_autoderef() { + assert_snapshot!( + infer(r#" +#[lang = "deref"] +pub trait Deref { + type Target: ?Sized; + fn deref(&self) -> &Self::Target; +} + +struct A(*mut T); + +impl A { + fn foo(&self, x: &A) -> T { + x + } +} + +struct B(T); + +impl Deref for B { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn test(a: A) { + A(0 as *mut _).foo(&&B(B(a))); +} +"#), + @r###" + [76; 80) 'self': &Self + [158; 162) 'self': &A + [164; 165) 'x': &A + [179; 196) '{ ... }': &A + [189; 190) 'x': &A + [277; 281) 'self': &B + [300; 323) '{ ... }': &T + [310; 317) '&self.0': &T + [311; 315) 'self': &B + [311; 317) 'self.0': T + [335; 336) 'a': A + [346; 384) '{ ...))); }': () + [352; 353) 'A': A(*mut T) -> A + [352; 366) 'A(0 as *mut _)': A + [352; 381) 'A(0 as...B(a)))': i32 + [354; 355) '0': i32 + [354; 365) '0 as *mut _': *mut i32 + [371; 380) '&&B(B(a))': &&B>> + [372; 380) '&B(B(a))': &B>> + [373; 374) 'B': B>>(T) -> B + [373; 380) 'B(B(a))': B>> + [375; 376) 'B': B>(T) -> B + [375; 379) 'B(a)': B> + [377; 378) 'a': A +"### + ); +} + #[test] fn bug_484() { assert_snapshot!( -- cgit v1.2.3 From 4bb66df6de6a832f53f09128ea038fc1c0068515 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Fri, 13 Sep 2019 02:59:21 +0800 Subject: Support basic implicit type coerce --- crates/ra_hir/src/ty/infer.rs | 254 +++++++++++++++++++++++++-------------- crates/ra_hir/src/ty/tests.rs | 271 +++++++++++++++++++++++++++++++++--------- 2 files changed, 383 insertions(+), 142 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 746b07a05..def787fb1 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -190,6 +190,15 @@ struct InferenceContext<'a, D: HirDatabase> { return_ty: Ty, } +macro_rules! ty_app { + ($ctor:pat, $param:pat) => { + Ty::Apply(ApplicationTy { ctor: $ctor, parameters: $param }) + }; + ($ctor:pat) => { + ty_app!($ctor, _) + }; +} + impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn new(db: &'a D, body: Arc, resolver: Resolver) -> Self { InferenceContext { @@ -278,10 +287,16 @@ 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, _) | (_, 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) } + _ => self.unify_inner_trivial(&ty1, &ty2), + } + } + + fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool { + match (ty1, ty2) { + (Ty::Unknown, _) | (_, Ty::Unknown) => true, (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) => { @@ -795,50 +810,146 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ret_ty } - /// This is similar to unify, but it makes the first type coerce to the - /// second one. + /// Infer type of expression with possibly implicit coerce to the expected type. + fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { + let ty = self.infer_expr_inner(expr, &expected); + self.coerce(&ty, &expected.ty); + ty + } + + /// Unify two types, but may coerce the first one to the second one + /// using "implicit coercion rules" if needed. + /// + /// See: https://doc.rust-lang.org/nomicon/coercions.html fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { - if is_never(from_ty) { - // ! coerces to any type - true - } else { - self.unify(from_ty, to_ty) + let from_ty = self.resolve_ty_shallow(from_ty).into_owned(); + let to_ty = self.resolve_ty_shallow(to_ty); + self.coerce_inner(from_ty, &to_ty) + } + + fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool { + match (&mut from_ty, &*to_ty) { + // Top and bottom type + (ty_app!(TypeCtor::Never), _) => return true, + + // FIXME: Solve `FromTy: CoerceUnsized` instead of listing common impls here. + + // `*mut T`, `&mut T, `&T`` -> `*const T` + // `&mut T` -> `&T` + // `&mut T` -> `*mut T` + (ty_app!(c1@TypeCtor::RawPtr(_)), ty_app!(c2@TypeCtor::RawPtr(Mutability::Shared))) + | (ty_app!(c1@TypeCtor::Ref(_)), ty_app!(c2@TypeCtor::RawPtr(Mutability::Shared))) + | (ty_app!(c1@TypeCtor::Ref(_)), ty_app!(c2@TypeCtor::Ref(Mutability::Shared))) + | (ty_app!(c1@TypeCtor::Ref(Mutability::Mut)), ty_app!(c2@TypeCtor::RawPtr(_))) => { + *c1 = *c2; + } + + // Illegal mutablity conversion + ( + ty_app!(TypeCtor::RawPtr(Mutability::Shared)), + ty_app!(TypeCtor::RawPtr(Mutability::Mut)), + ) + | ( + ty_app!(TypeCtor::Ref(Mutability::Shared)), + ty_app!(TypeCtor::Ref(Mutability::Mut)), + ) => return false, + + // `{function_type}` -> `fn()` + (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnPtr { .. })) => { + match from_ty.callable_sig(self.db) { + None => return false, + Some(sig) => { + let num_args = sig.params_and_return.len() as u16 - 1; + from_ty = + Ty::apply(TypeCtor::FnPtr { num_args }, Substs(sig.params_and_return)); + } + } + } + + // Trivial cases, this should go after `never` check to + // avoid infer result type to be never + _ => { + if self.unify_inner_trivial(&from_ty, &to_ty) { + return true; + } + } + } + + // Try coerce or unify + match (&from_ty, &to_ty) { + // FIXME: Solve `FromTy: CoerceUnsized` instead of listing common impls here. + (ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) + | (ty_app!(TypeCtor::RawPtr(_), st1), ty_app!(TypeCtor::RawPtr(_), st2)) => { + match self.try_coerce_unsized(&st1[0], &st2[0], 0) { + Some(ret) => return ret, + None => {} + } + } + _ => {} + } + + // Auto Deref if cannot coerce + match (&from_ty, &to_ty) { + (ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) => { + self.unify_autoderef_behind_ref(&st1[0], &st2[0]) + } + + // Normal unify + _ => self.unify(&from_ty, &to_ty), } } - fn unify_with_autoderef(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { - macro_rules! ty_app { - ($ctor:pat, $param:pat) => { - Ty::Apply(ApplicationTy { ctor: $ctor, parameters: $param }) - }; + /// Coerce a type to a DST if `FromTy: Unsize` + /// + /// 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) -> Option { + if depth > 1000 { + panic!("Infinite recursion in coercion"); } - // If given type and expected type are compatible reference, - // trigger auto-deref. - let (_to_mut, from_ty, to_ty) = - match (&*self.resolve_ty_shallow(&from_ty), &*self.resolve_ty_shallow(&to_ty)) { - ( - ty_app!(TypeCtor::Ref(from_mut), from_param), - ty_app!(TypeCtor::Ref(to_mut), to_param), - ) if *from_mut == Mutability::Mut || from_mut == to_mut => { - (to_mut, from_param[0].clone(), to_param[0].clone()) - } - _ => { - // Otherwise, just unify - return self.unify(&from_ty, &to_ty); + // FIXME: Correctly handle + match (&from_ty, &to_ty) { + // `[T; N]` -> `[T]` + (ty_app!(TypeCtor::Array, st1), ty_app!(TypeCtor::Slice, st2)) => { + Some(self.unify(&st1[0], &st2[0])) + } + + // `T` -> `dyn Trait` when `T: Trait` + (_, Ty::Dyn(_)) => { + // FIXME: Check predicates + Some(true) + } + + (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) if ctor1 == ctor2 => { + for (ty1, ty2) in st1.iter().zip(st2.iter()) { + match self.try_coerce_unsized(ty1, ty2, depth + 1) { + Some(true) => {} + ret => return ret, + } } - }; + Some(true) + } + + _ => None, + } + } - let canonicalized = self.canonicalizer().canonicalize_ty(from_ty); + /// Unify `from_ty` to `to_ty` with optional auto Deref + /// + /// Note that the parameters are already stripped the outer reference. + fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { + let canonicalized = self.canonicalizer().canonicalize_ty(from_ty.clone()); + let to_ty = self.resolve_ty_shallow(&to_ty); // FIXME: Auto DerefMut for derefed_ty in autoderef::autoderef(self.db, &self.resolver.clone(), canonicalized.value.clone()) { let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value); - match (&*self.resolve_ty_shallow(&derefed_ty), &*self.resolve_ty_shallow(&to_ty)) { - // Unify when constructor matches. - (ty_app!(from_ctor, _), ty_app!(to_ctor, _)) if from_ctor == to_ctor => { - return self.unify(&derefed_ty, &to_ty); + match (&*self.resolve_ty_shallow(&derefed_ty), &*to_ty) { + // Stop when constructor matches. + (ty_app!(from_ctor, st1), ty_app!(to_ctor, st2)) if from_ctor == to_ctor => { + // It will not recurse to `coerce`. + return self.unify_substs(st1, st2, 0); } _ => {} } @@ -875,9 +986,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), None => Ty::unit(), }; - self.coerce(&else_ty, &expected.ty); - - expected.ty.clone() + if !self.coerce(&else_ty, &expected.ty) { + self.coerce(&expected.ty, &else_ty); + else_ty.clone() + } else { + expected.ty.clone() + } } Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected), Expr::TryBlock { body } => { @@ -973,13 +1087,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .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 mut expected = match expected.ty { + Ty::Unknown => Expectation::has_type(Ty::simple(TypeCtor::Never)), + _ => expected.clone(), }; - - let mut arm_tys = Vec::with_capacity(arms.len()); + let mut all_never = true; for arm in arms { for &pat in &arm.pats { @@ -991,16 +1103,22 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { &Expectation::has_type(Ty::simple(TypeCtor::Bool)), ); } - arm_tys.push(self.infer_expr_inner(arm.expr, &expected)); + let arm_ty = self.infer_expr_inner(arm.expr, &expected); + match &arm_ty { + ty_app!(TypeCtor::Never) => (), + _ => all_never = false, + } + if !self.coerce(&arm_ty, &expected.ty) { + self.coerce(&expected.ty, &arm_ty); + expected = Expectation::has_type(arm_ty); + } } - let lub_ty = calculate_least_upper_bound(expected.ty, &arm_tys); - - for arm_ty in &arm_tys { - self.coerce(arm_ty, &lub_ty); + if all_never { + Ty::simple(TypeCtor::Never) + } else { + expected.ty } - - lub_ty } Expr::Path(p) => { // FIXME this could be more efficient... @@ -1289,8 +1407,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { 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 { - let expr_ty = self.infer_expr(*expr, &Expectation::has_type(decl_ty)); - expr_ty + self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty)) } else { decl_ty }; @@ -1326,8 +1443,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } let param_ty = self.normalize_associated_types_in(param_ty); - let arg_ty = self.infer_expr_inner(arg, &Expectation::has_type(param_ty.clone())); - self.unify_with_autoderef(&arg_ty, ¶m_ty); + self.infer_expr_coerce(arg, &Expectation::has_type(param_ty.clone())); } } } @@ -1517,37 +1633,3 @@ 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: &[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 6076e4025..2ce0039b1 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -806,14 +806,14 @@ fn infer_argument_autoderef() { infer(r#" #[lang = "deref"] pub trait Deref { - type Target: ?Sized; + type Target; fn deref(&self) -> &Self::Target; } struct A(T); -impl A { - fn foo(&self) -> T { +impl A { + fn foo(&self) -> &T { self.0 } } @@ -828,32 +828,33 @@ impl Deref for B { } fn test() { - A::foo(&&B(B(A(42)))); + let t = A::foo(&&B(B(A(42)))); } "#), @r###" - [76; 80) 'self': &Self - [153; 157) 'self': &A - [164; 186) '{ ... }': T - [174; 178) 'self': &A - [174; 180) 'self.0': T - [267; 271) 'self': &B - [290; 313) '{ ... }': &T - [300; 307) '&self.0': &T - [301; 305) 'self': &B - [301; 307) 'self.0': T - [327; 357) '{ ...))); }': () - [333; 339) 'A::foo': fn foo(&A) -> T - [333; 354) 'A::foo...42))))': i32 - [340; 353) '&&B(B(A(42)))': &&B>> - [341; 353) '&B(B(A(42)))': &B>> - [342; 343) 'B': B>>(T) -> B - [342; 353) 'B(B(A(42)))': B>> - [344; 345) 'B': B>(T) -> B - [344; 352) 'B(A(42))': B> - [346; 347) 'A': A(T) -> A - [346; 351) 'A(42)': A - [348; 350) '42': i32 + [68; 72) 'self': &Self + [139; 143) 'self': &A + [151; 173) '{ ... }': T + [161; 165) 'self': &A + [161; 167) 'self.0': T + [254; 258) 'self': &B + [277; 300) '{ ... }': &T + [287; 294) '&self.0': &T + [288; 292) 'self': &B + [288; 294) 'self.0': T + [314; 352) '{ ...))); }': () + [324; 325) 't': &i32 + [328; 334) 'A::foo': fn foo(&A) -> &T + [328; 349) 'A::foo...42))))': &i32 + [335; 348) '&&B(B(A(42)))': &&B>> + [336; 348) '&B(B(A(42)))': &B>> + [337; 338) 'B': B>>(T) -> B + [337; 348) 'B(B(A(42)))': B>> + [339; 340) 'B': B>(T) -> B + [339; 347) 'B(A(42))': B> + [341; 342) 'A': A(T) -> A + [341; 346) 'A(42)': A + [343; 345) '42': i32 "### ); } @@ -864,15 +865,15 @@ fn infer_method_argument_autoderef() { infer(r#" #[lang = "deref"] pub trait Deref { - type Target: ?Sized; + type Target; fn deref(&self) -> &Self::Target; } struct A(*mut T); -impl A { - fn foo(&self, x: &A) -> T { - x +impl A { + fn foo(&self, x: &A) -> &T { + &*x.0 } } @@ -886,38 +887,196 @@ impl Deref for B { } fn test(a: A) { - A(0 as *mut _).foo(&&B(B(a))); + let t = A(0 as *mut _).foo(&&B(B(a))); +} +"#), + @r###" + [68; 72) 'self': &Self + [144; 148) 'self': &A + [150; 151) 'x': &A + [166; 187) '{ ... }': &T + [176; 181) '&*x.0': &T + [177; 181) '*x.0': T + [178; 179) 'x': &A + [178; 181) 'x.0': *mut T + [268; 272) 'self': &B + [291; 314) '{ ... }': &T + [301; 308) '&self.0': &T + [302; 306) 'self': &B + [302; 308) 'self.0': T + [326; 327) 'a': A + [337; 383) '{ ...))); }': () + [347; 348) 't': &i32 + [351; 352) 'A': A(*mut T) -> A + [351; 365) 'A(0 as *mut _)': A + [351; 380) 'A(0 as...B(a)))': &i32 + [353; 354) '0': i32 + [353; 364) '0 as *mut _': *mut i32 + [370; 379) '&&B(B(a))': &&B>> + [371; 379) '&B(B(a))': &B>> + [372; 373) 'B': B>>(T) -> B + [372; 379) 'B(B(a))': B>> + [374; 375) 'B': B>(T) -> B + [374; 378) 'B(a)': B> + [376; 377) '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###" - [76; 80) 'self': &Self - [158; 162) 'self': &A - [164; 165) 'x': &A - [179; 196) '{ ... }': &A - [189; 190) 'x': &A - [277; 281) 'self': &B - [300; 323) '{ ... }': &T - [310; 317) '&self.0': &T - [311; 315) 'self': &B - [311; 317) 'self.0': T - [335; 336) 'a': A - [346; 384) '{ ...))); }': () - [352; 353) 'A': A(*mut T) -> A - [352; 366) 'A(0 as *mut _)': A - [352; 381) 'A(0 as...B(a)))': i32 - [354; 355) '0': i32 - [354; 365) '0 as *mut _': *mut i32 - [371; 380) '&&B(B(a))': &&B>> - [372; 380) '&B(B(a))': &B>> - [373; 374) 'B': B>>(T) -> B - [373; 380) 'B(B(a))': B>> - [375; 376) 'B': B>(T) -> B - [375; 379) 'B(a)': B> - [377; 378) 'a': A + [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 bug_484() { assert_snapshot!( @@ -3474,7 +3633,7 @@ trait Deref { } struct Arc; -impl Deref for Arc { +impl Deref for Arc { type Target = T; } -- cgit v1.2.3 From bf161fa3e58d57d9b15bd965405036d834f18595 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Wed, 18 Sep 2019 03:59:51 +0800 Subject: Better handle never type and branch merging Split out tests for never type to another file --- crates/ra_hir/src/marks.rs | 1 + crates/ra_hir/src/ty/infer.rs | 152 +++++++++++------- crates/ra_hir/src/ty/infer/unify.rs | 1 + crates/ra_hir/src/ty/tests.rs | 161 +++++-------------- crates/ra_hir/src/ty/tests/never_type.rs | 258 +++++++++++++++++++++++++++++++ 5 files changed, 392 insertions(+), 181 deletions(-) create mode 100644 crates/ra_hir/src/ty/tests/never_type.rs (limited to 'crates') diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index fe119b97c..b2111be05 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs @@ -13,4 +13,5 @@ test_utils::marks!( infer_while_let macro_rules_from_other_crates_are_visible_with_macro_use prelude_is_macro_use + coerce_merge_fail_fallback ); diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index def787fb1..cbbba8b23 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -297,23 +297,35 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool { match (ty1, ty2) { (Ty::Unknown, _) | (_, Ty::Unknown) => true, + (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) - | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) => { + | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) + | ( + Ty::Infer(InferTy::MaybeNeverTypeVar(tv1)), + Ty::Infer(InferTy::MaybeNeverTypeVar(tv2)), + ) => { // both type vars are unknown since we tried to resolve them self.var_unification_table.union(*tv1, *tv2); true } + + // The order of MaybeNeverTypeVar matters here. + // Unifying MaybeNeverTypeVar and TypeVar will let the latter become MaybeNeverTypeVar. + // Unifying MaybeNeverTypeVar and other concrete type will let the former become it. (Ty::Infer(InferTy::TypeVar(tv)), other) | (other, Ty::Infer(InferTy::TypeVar(tv))) - | (Ty::Infer(InferTy::IntVar(tv)), other) - | (other, Ty::Infer(InferTy::IntVar(tv))) - | (Ty::Infer(InferTy::FloatVar(tv)), other) - | (other, Ty::Infer(InferTy::FloatVar(tv))) => { + | (Ty::Infer(InferTy::MaybeNeverTypeVar(tv)), other) + | (other, Ty::Infer(InferTy::MaybeNeverTypeVar(tv))) + | (Ty::Infer(InferTy::IntVar(tv)), other @ ty_app!(TypeCtor::Int(_))) + | (other @ ty_app!(TypeCtor::Int(_)), Ty::Infer(InferTy::IntVar(tv))) + | (Ty::Infer(InferTy::FloatVar(tv)), other @ ty_app!(TypeCtor::Float(_))) + | (other @ ty_app!(TypeCtor::Float(_)), 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 } + _ => false, } } @@ -330,6 +342,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::Infer(InferTy::FloatVar(self.var_unification_table.new_key(TypeVarValue::Unknown))) } + fn new_maybe_never_type_var(&mut self) -> Ty { + Ty::Infer(InferTy::MaybeNeverTypeVar( + self.var_unification_table.new_key(TypeVarValue::Unknown), + )) + } + /// 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 { @@ -817,6 +835,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ty } + /// Merge two types from different branches, with possible implicit coerce. + /// + /// Note that it is only possible that one type are coerced to another. + /// Coercing both types to another least upper bound type is not possible in rustc, + /// which will simply result in "incompatible types" error. + fn coerce_merge_branch<'t>(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { + if self.coerce(ty1, ty2) { + ty2.clone() + } else if self.coerce(ty2, ty1) { + ty1.clone() + } else { + tested_by!(coerce_merge_fail_fallback); + // For incompatible types, we use the latter one as result + // to be better recovery for `if` without `else`. + ty2.clone() + } + } + /// Unify two types, but may coerce the first one to the second one /// using "implicit coercion rules" if needed. /// @@ -828,12 +864,26 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool { - match (&mut from_ty, &*to_ty) { - // Top and bottom type + match (&from_ty, to_ty) { + // Never type will make type variable to fallback to Never Type instead of Unknown. + (ty_app!(TypeCtor::Never), Ty::Infer(InferTy::TypeVar(tv))) => { + let var = self.new_maybe_never_type_var(); + self.var_unification_table.union_value(*tv, TypeVarValue::Known(var)); + return true; + } (ty_app!(TypeCtor::Never), _) => return true, - // FIXME: Solve `FromTy: CoerceUnsized` instead of listing common impls here. + // Trivial cases, this should go after `never` check to + // avoid infer result type to be never + _ => { + if self.unify_inner_trivial(&from_ty, &to_ty) { + return true; + } + } + } + // Pointer weakening and function to pointer + match (&mut from_ty, to_ty) { // `*mut T`, `&mut T, `&T`` -> `*const T` // `&mut T` -> `&T` // `&mut T` -> `*mut T` @@ -866,71 +916,67 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } - // Trivial cases, this should go after `never` check to - // avoid infer result type to be never - _ => { - if self.unify_inner_trivial(&from_ty, &to_ty) { - return true; - } - } + _ => {} } - // Try coerce or unify + // FIXME: Solve `FromTy: CoerceUnsized` instead of listing common impls here. match (&from_ty, &to_ty) { - // FIXME: Solve `FromTy: CoerceUnsized` instead of listing common impls here. + // Mutilibity is checked above (ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) | (ty_app!(TypeCtor::RawPtr(_), st1), ty_app!(TypeCtor::RawPtr(_), st2)) => { - match self.try_coerce_unsized(&st1[0], &st2[0], 0) { - Some(ret) => return ret, - None => {} + if self.try_coerce_unsized(&st1[0], &st2[0], 0) { + return true; } } _ => {} } // Auto Deref if cannot coerce - match (&from_ty, &to_ty) { + match (&from_ty, to_ty) { + // FIXME: DerefMut (ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) => { self.unify_autoderef_behind_ref(&st1[0], &st2[0]) } - // Normal unify - _ => self.unify(&from_ty, &to_ty), + // Otherwise, normal unify + _ => self.unify(&from_ty, to_ty), } } /// Coerce a type to a DST if `FromTy: Unsize` /// /// 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) -> Option { + fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty, depth: usize) -> bool { if depth > 1000 { panic!("Infinite recursion in coercion"); } - // FIXME: Correctly handle match (&from_ty, &to_ty) { // `[T; N]` -> `[T]` (ty_app!(TypeCtor::Array, st1), ty_app!(TypeCtor::Slice, st2)) => { - Some(self.unify(&st1[0], &st2[0])) + self.unify(&st1[0], &st2[0]) } // `T` -> `dyn Trait` when `T: Trait` (_, Ty::Dyn(_)) => { // FIXME: Check predicates - Some(true) + true } - (ty_app!(ctor1, st1), ty_app!(ctor2, st2)) if ctor1 == ctor2 => { + ( + 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()) { - match self.try_coerce_unsized(ty1, ty2, depth + 1) { - Some(true) => {} - ret => return ret, + if !self.try_coerce_unsized(ty1, ty2, depth + 1) { + return false; } } - Some(true) + true } - _ => None, + _ => false, } } @@ -980,18 +1026,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); let then_ty = self.infer_expr_inner(*then_branch, &expected); - self.coerce(&then_ty, &expected.ty); - let else_ty = match else_branch { Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), None => Ty::unit(), }; - if !self.coerce(&else_ty, &expected.ty) { - self.coerce(&expected.ty, &else_ty); - else_ty.clone() - } else { - expected.ty.clone() - } + + self.coerce_merge_branch(&then_ty, &else_ty) } Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected), Expr::TryBlock { body } => { @@ -1087,11 +1127,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .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 mut expected = match expected.ty { - Ty::Unknown => Expectation::has_type(Ty::simple(TypeCtor::Never)), - _ => expected.clone(), - }; - let mut all_never = true; + + let mut result_ty = self.new_maybe_never_type_var(); for arm in arms { for &pat in &arm.pats { @@ -1103,22 +1140,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { &Expectation::has_type(Ty::simple(TypeCtor::Bool)), ); } + let arm_ty = self.infer_expr_inner(arm.expr, &expected); - match &arm_ty { - ty_app!(TypeCtor::Never) => (), - _ => all_never = false, - } - if !self.coerce(&arm_ty, &expected.ty) { - self.coerce(&expected.ty, &arm_ty); - expected = Expectation::has_type(arm_ty); - } + result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); } - if all_never { - Ty::simple(TypeCtor::Never) - } else { - expected.ty - } + result_ty } Expr::Path(p) => { // FIXME this could be more efficient... @@ -1558,12 +1585,16 @@ pub enum InferTy { TypeVar(TypeVarId), IntVar(TypeVarId), FloatVar(TypeVarId), + MaybeNeverTypeVar(TypeVarId), } impl InferTy { fn to_inner(self) -> TypeVarId { match self { - InferTy::TypeVar(ty) | InferTy::IntVar(ty) | InferTy::FloatVar(ty) => ty, + InferTy::TypeVar(ty) + | InferTy::IntVar(ty) + | InferTy::FloatVar(ty) + | InferTy::MaybeNeverTypeVar(ty) => ty, } } @@ -1576,6 +1607,7 @@ impl InferTy { InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float( primitive::UncertainFloatTy::Known(primitive::FloatTy::f64()), )), + InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never), } } } diff --git a/crates/ra_hir/src/ty/infer/unify.rs b/crates/ra_hir/src/ty/infer/unify.rs index 9a0d2d8f9..b6ebee3b1 100644 --- a/crates/ra_hir/src/ty/infer/unify.rs +++ b/crates/ra_hir/src/ty/infer/unify.rs @@ -63,6 +63,7 @@ where InferTy::TypeVar(_) => InferTy::TypeVar(root), InferTy::IntVar(_) => InferTy::IntVar(root), InferTy::FloatVar(_) => InferTy::FloatVar(root), + InferTy::MaybeNeverTypeVar(_) => InferTy::MaybeNeverTypeVar(root), }; let position = self.add(free_var); Ty::Bound(position as u32) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 2ce0039b1..081bfe37c 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -20,6 +20,8 @@ use crate::{ // against snapshots of the expected results using insta. Use cargo-insta to // update the snapshots. +mod never_type; + #[test] fn infer_await() { let (mut db, pos) = MockDatabase::with_position( @@ -1077,6 +1079,42 @@ fn test(i: 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!( @@ -2458,7 +2496,6 @@ fn extra_compiler_flags() { } "#), @r###" - [27; 323) '{ ... } }': () [33; 321) 'for co... }': () [37; 44) 'content': &{unknown} @@ -2472,8 +2509,8 @@ fn extra_compiler_flags() { [135; 167) '{ ... }': &&{unknown} [149; 157) '&content': &&{unknown} [150; 157) 'content': &{unknown} - [182; 189) 'content': &&{unknown} - [192; 314) 'if ICE... }': &&{unknown} + [182; 189) 'content': &{unknown} + [192; 314) 'if ICE... }': &{unknown} [195; 232) 'ICE_RE..._VALUE': {unknown} [195; 248) 'ICE_RE...&name)': bool [242; 247) '&name': &&&{unknown} @@ -4683,121 +4720,3 @@ fn no_such_field_diagnostics() { "### ); } - -mod branching_with_never_tests { - use super::type_at; - - #[test] - fn 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() { - 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_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"); - } -} diff --git a/crates/ra_hir/src/ty/tests/never_type.rs b/crates/ra_hir/src/ty/tests/never_type.rs new file mode 100644 index 000000000..b9af918e9 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/never_type.rs @@ -0,0 +1,258 @@ +use super::type_at; + +#[test] +fn infer_never1() { + let t = type_at( + r#" +//- /main.rs +fn test() { + let t = return; + t<|>; +} +"#, + ); + assert_eq!(t, "!"); +} + +#[test] +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 test() { + let a = Foo::gen(); + if false { a } else { loop {} }; + a<|>; +} +"#, + ); + assert_eq!(t, "!"); +} + +#[test] +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 test() { + let a = Foo::gen(); + if false { loop {} } else { a }; + a<|>; +} +"#, + ); + assert_eq!(t, "!"); +} + +#[test] +fn never_type_in_generic_args() { + let t = type_at( + r#" +//- /main.rs +enum Option { None, Some(T) } + +fn test() { + let a = if true { Option::None } else { Option::Some(return) }; + a<|>; +} +"#, + ); + assert_eq!(t, "Option"); +} + +#[test] +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 test() { + let a = Foo::gen(); + if false { loop {} } else { a }; + a<|>; + if false { a }; +} +"#, + ); + assert_eq!(t, "()"); +} + +#[test] +fn never_type_can_be_reinferred2() { + let t = type_at( + r#" +//- /main.rs +enum Option { None, Some(T) } + +fn test() { + let a = if true { Option::None } else { Option::Some(return) }; + a<|>; + match 42 { + 42 => a, + _ => Option::Some(42), + }; +} +"#, + ); + assert_eq!(t, "Option"); +} +#[test] +fn never_type_can_be_reinferred3() { + let t = type_at( + r#" +//- /main.rs +enum Option { None, Some(T) } + +fn test() { + let a = if true { Option::None } else { Option::Some(return) }; + a<|>; + match 42 { + 42 => a, + _ => Option::Some("str"), + }; +} +"#, + ); + assert_eq!(t, "Option<&str>"); +} + +#[test] +fn match_no_arm() { + let t = type_at( + r#" +//- /main.rs +enum Void {} + +fn test(a: Void) { + let t = match a {}; + t<|>; +} +"#, + ); + assert_eq!(t, "!"); +} + +#[test] +fn 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() { + 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_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 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 From 29e56b8ee480828b81011cfa16c055fa0c9c89fe Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Thu, 26 Sep 2019 05:56:55 +0800 Subject: Support all coercion places --- crates/ra_hir/src/ty/infer.rs | 82 ++++++++++++++------- crates/ra_hir/src/ty/tests.rs | 127 ++++++++++++++++----------------- crates/ra_hir/src/ty/tests/coercion.rs | 103 ++++++++++++++++++++++++-- 3 files changed, 216 insertions(+), 96 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index ba63050a9..db3377357 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -14,7 +14,7 @@ //! the `ena` crate, which is extracted from rustc. use std::borrow::Cow; -use std::iter::repeat; +use std::iter::{repeat, repeat_with}; use std::mem; use std::ops::Index; use std::sync::Arc; @@ -876,10 +876,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } /// Infer type of expression with possibly implicit coerce to the expected type. + /// Return the type after possible coercion. fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(expr, &expected); - self.coerce(&ty, &expected.ty); - ty + let ty = if !self.coerce(&ty, &expected.ty) { + self.result + .type_mismatches + .insert(expr, TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() }); + // Return actual type when type mismatch. + // This is needed for diagnostic when return type mismatch. + ty + } else if expected.ty == Ty::Unknown { + ty + } else { + expected.ty.clone() + }; + + self.resolve_ty_as_possible(&mut vec![], ty) } /// Merge two types from different branches, with possible implicit coerce. @@ -1328,6 +1341,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.write_variant_resolution(tgt_expr.into(), variant); } + self.unify(&ty, &expected.ty); + let substs = ty.substs().unwrap_or_else(Substs::empty); for (field_idx, field) in fields.iter().enumerate() { let field_ty = def_id @@ -1343,7 +1358,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }) .map_or(Ty::Unknown, |field| field.ty(self.db)) .subst(&substs); - self.infer_expr(field.expr, &Expectation::has_type(field_ty)); + self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); } if let Some(expr) = spread { self.infer_expr(*expr, &Expectation::has_type(ty.clone())); @@ -1513,35 +1528,41 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::Unknown } Expr::Tuple { exprs } => { - let mut ty_vec = Vec::with_capacity(exprs.len()); - for arg in exprs.iter() { - ty_vec.push(self.infer_expr(*arg, &Expectation::none())); + let mut tys = match &expected.ty { + ty_app!(TypeCtor::Tuple { .. }, st) => st + .iter() + .cloned() + .chain(repeat_with(|| self.new_type_var())) + .take(exprs.len()) + .collect::>(), + _ => (0..exprs.len()).map(|_| self.new_type_var()).collect(), + }; + + for (expr, ty) in exprs.iter().zip(tys.iter_mut()) { + self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone())); } - Ty::apply( - TypeCtor::Tuple { cardinality: ty_vec.len() as u16 }, - Substs(ty_vec.into()), - ) + Ty::apply(TypeCtor::Tuple { cardinality: tys.len() as u16 }, Substs(tys.into())) } Expr::Array(array) => { let elem_ty = match &expected.ty { - Ty::Apply(a_ty) => match a_ty.ctor { - TypeCtor::Slice | TypeCtor::Array => { - Ty::clone(&a_ty.parameters.as_single()) - } - _ => self.new_type_var(), - }, + ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => { + st.as_single().clone() + } _ => self.new_type_var(), }; match array { Array::ElementList(items) => { for expr in items.iter() { - self.infer_expr(*expr, &Expectation::has_type(elem_ty.clone())); + self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); } } Array::Repeat { initializer, repeat } => { - self.infer_expr(*initializer, &Expectation::has_type(elem_ty.clone())); + self.infer_expr_coerce( + *initializer, + &Expectation::has_type(elem_ty.clone()), + ); self.infer_expr( *repeat, &Expectation::has_type(Ty::simple(TypeCtor::Int( @@ -1588,12 +1609,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Statement::Let { pat, type_ref, initializer } => { 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); + + // Always use the declared type when specified + let mut ty = decl_ty.clone(); + if let Some(expr) = initializer { - self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone())); + let actual_ty = + self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone())); + if decl_ty == Ty::Unknown { + ty = actual_ty; + } } - let ty = self.resolve_ty_as_possible(&mut vec![], decl_ty); + let ty = self.resolve_ty_as_possible(&mut vec![], ty); self.infer_pat(*pat, &ty, BindingMode::default()); } Statement::Expr(expr) => { @@ -1601,9 +1629,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } } - let ty = - if let Some(expr) = tail { self.infer_expr_inner(expr, expected) } else { Ty::unit() }; - ty + + if let Some(expr) = tail { + self.infer_expr_coerce(expr, expected) + } else { + self.coerce(&Ty::unit(), &expected.ty); + Ty::unit() + } } fn check_call_arguments(&mut self, args: &[ExprId], param_tys: &[Ty]) { diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index cd5a05092..4362bb27a 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -239,17 +239,23 @@ fn test() { let a = 1isize; let b: usize = 1; let c = b; + let d: u32; + let e; + let f: i32 = e; } "#), @r###" - - [11; 71) '{ ...= b; }': () + [11; 118) '{ ...= e; }': () [21; 22) 'a': isize [25; 31) '1isize': isize [41; 42) 'b': usize [52; 53) '1': usize [63; 64) 'c': usize [67; 68) 'b': usize + [78; 79) 'd': u32 + [94; 95) 'e': i32 + [105; 106) 'f': i32 + [114; 115) 'e': i32 "### ); } @@ -331,14 +337,14 @@ fn test() { "#), @r###" [45; 49) 'self': &[T] - [56; 79) '{ ... }': ! + [56; 79) '{ ... }': T [66; 73) 'loop {}': ! [71; 73) '{}': () [133; 160) '{ ...o"); }': () [139; 149) '<[_]>::foo': fn foo(&[T]) -> T [139; 157) '<[_]>:..."foo")': u8 [150; 156) 'b"foo"': &[u8] -"### + "### ); } @@ -817,7 +823,7 @@ struct A(T); impl A { fn foo(&self) -> &T { - self.0 + &self.0 } } @@ -837,28 +843,29 @@ fn test() { @r###" [68; 72) 'self': &Self [139; 143) 'self': &A - [151; 173) '{ ... }': T - [161; 165) 'self': &A - [161; 167) 'self.0': T - [254; 258) 'self': &B - [277; 300) '{ ... }': &T - [287; 294) '&self.0': &T - [288; 292) 'self': &B - [288; 294) 'self.0': T - [314; 352) '{ ...))); }': () - [324; 325) 't': &i32 - [328; 334) 'A::foo': fn foo(&A) -> &T - [328; 349) 'A::foo...42))))': &i32 - [335; 348) '&&B(B(A(42)))': &&B>> - [336; 348) '&B(B(A(42)))': &B>> - [337; 338) 'B': B>>(T) -> B - [337; 348) 'B(B(A(42)))': B>> - [339; 340) 'B': B>(T) -> B - [339; 347) 'B(A(42))': B> - [341; 342) 'A': A(T) -> A - [341; 346) 'A(42)': A - [343; 345) '42': i32 -"### + [151; 174) '{ ... }': &T + [161; 168) '&self.0': &T + [162; 166) 'self': &A + [162; 168) 'self.0': T + [255; 259) 'self': &B + [278; 301) '{ ... }': &T + [288; 295) '&self.0': &T + [289; 293) 'self': &B + [289; 295) 'self.0': T + [315; 353) '{ ...))); }': () + [325; 326) 't': &i32 + [329; 335) 'A::foo': fn foo(&A) -> &T + [329; 350) 'A::foo...42))))': &i32 + [336; 349) '&&B(B(A(42)))': &&B>> + [337; 349) '&B(B(A(42)))': &B>> + [338; 339) 'B': B>>(T) -> B + [338; 349) 'B(B(A(42)))': B>> + [340; 341) 'B': B>(T) -> B + [340; 348) 'B(A(42))': B> + [342; 343) 'A': A(T) -> A + [342; 347) 'A(42)': A + [344; 346) '42': i32 + "### ); } @@ -1109,13 +1116,12 @@ fn test(x: &str, y: isize) { let b = [a, ["b"]]; let x: [u8; 0] = []; - let z: &[u8] = &[1, 2, 3]; } "#), @r###" [9; 10) 'x': &str [18; 19) 'y': isize - [28; 324) '{ ... 3]; }': () + [28; 293) '{ ... []; }': () [38; 39) 'a': [&str;_] [42; 45) '[x]': [&str;_] [43; 44) 'x': &str @@ -1165,12 +1171,6 @@ fn test(x: &str, y: isize) { [260; 263) '"b"': &str [275; 276) 'x': [u8;_] [288; 290) '[]': [u8;_] - [300; 301) 'z': &[u8] - [311; 321) '&[1, 2, 3]': &[u8;_] - [312; 321) '[1, 2, 3]': [u8;_] - [313; 314) '1': u8 - [316; 317) '2': u8 - [319; 320) '3': u8 "### ); } @@ -1892,8 +1892,7 @@ fn test() { } "#), @r###" - - [80; 104) '{ ... }': ! + [80; 104) '{ ... }': Gen [90; 98) 'loop { }': ! [95; 98) '{ }': () [118; 146) '{ ...e(); }': () @@ -1923,8 +1922,7 @@ fn test() { } "#), @r###" - - [76; 100) '{ ... }': ! + [76; 100) '{ ... }': Gen [86; 94) 'loop { }': ! [91; 94) '{ }': () [114; 149) '{ ...e(); }': () @@ -1955,8 +1953,7 @@ fn test() { } "#), @r###" - - [102; 126) '{ ... }': ! + [102; 126) '{ ... }': Gen [112; 120) 'loop { }': ! [117; 120) '{ }': () [140; 180) '{ ...e(); }': () @@ -2100,7 +2097,7 @@ 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} @@ -2124,11 +2121,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} - [59; 77) '[(x, y..., &x)]': [(&&{unknown}, &&{unknown});_] - [60; 66) '(x, y)': (&&{unknown}, &&{unknown}) + [46; 53) 'unknown': &&{unknown} + [59; 77) '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown});_] + [60; 66) '(x, y)': (&&&{unknown}, &&&{unknown}) [61; 62) 'x': &&{unknown} [64; 65) 'y': &&{unknown} [68; 76) '(&y, &x)': (&&&{unknown}, &&&{unknown}) @@ -2149,7 +2146,7 @@ fn id(x: T) -> T { } fn clone(x: &T) -> T { - x + *x } fn test() { @@ -2160,26 +2157,26 @@ fn test() { } "#), @r###" - [10; 11) 'x': T [21; 30) '{ x }': T [27; 28) 'x': T [44; 45) 'x': &T - [56; 65) '{ x }': &T - [62; 63) 'x': &T - [77; 157) '{ ...(1); }': () - [87; 88) 'y': u32 - [91; 96) '10u32': u32 - [102; 104) 'id': fn id(T) -> T - [102; 107) 'id(y)': u32 - [105; 106) 'y': u32 - [117; 118) 'x': bool - [127; 132) 'clone': fn clone(&T) -> T - [127; 135) 'clone(z)': bool - [133; 134) 'z': &bool - [141; 151) 'id::': fn id(T) -> T - [141; 154) 'id::(1)': i128 - [152; 153) '1': i128 + [56; 66) '{ *x }': T + [62; 64) '*x': T + [63; 64) 'x': &T + [78; 158) '{ ...(1); }': () + [88; 89) 'y': u32 + [92; 97) '10u32': u32 + [103; 105) 'id': fn id(T) -> T + [103; 108) 'id(y)': u32 + [106; 107) 'y': u32 + [118; 119) 'x': bool + [128; 133) 'clone': fn clone(&T) -> T + [128; 136) 'clone(z)': bool + [134; 135) 'z': &bool + [142; 152) 'id::': fn id(T) -> T + [142; 155) 'id::(1)': i128 + [153; 154) '1': i128 "### ); } @@ -3404,7 +3401,7 @@ impl S { } fn test(s: Arc) { - (*s, s.foo())<|> + (*s, s.foo())<|>; } "#, ); @@ -3488,7 +3485,7 @@ impl S { } fn test(s: Arc) { - (*s, s.foo())<|> + (*s, s.foo())<|>; } "#, ); diff --git a/crates/ra_hir/src/ty/tests/coercion.rs b/crates/ra_hir/src/ty/tests/coercion.rs index d80d3fb6f..1530fcc63 100644 --- a/crates/ra_hir/src/ty/tests/coercion.rs +++ b/crates/ra_hir/src/ty/tests/coercion.rs @@ -19,6 +19,97 @@ fn infer(source: &str) -> String { super::infer(&format!("{}{}", source, defs)) } +#[test] +fn infer_block_expr_type_mismatch() { + assert_snapshot!( + infer(r#" +fn test() { + let a: i32 = { 1i64 }; +} +"#), + @r###" + [11; 41) '{ ...4 }; }': () + [21; 22) 'a': i32 + [30; 38) '{ 1i64 }': i64 + [32; 36) '1i64': i64 + "###); +} + +#[test] +fn coerce_places() { + assert_snapshot!( + infer(r#" +struct S { a: T } + +fn f(_: &[T]) -> T { loop {} } +fn g(_: S<&[T]>) -> T { loop {} } + +fn gen() -> *mut [T; 2] { loop {} } +fn test1() -> *mut [U] { + gen() +} + +fn test2() { + let arr: &[u8; 1] = &[1]; + + let a: &[_] = arr; + let b = f(arr); + let c: &[_] = { arr }; + let d = g(S { a: arr }); + let e: [&[_]; 1] = [arr]; + let f: [&[_]; 2] = [arr; 2]; + let g: (&[_], &[_]) = (arr, arr); +} +"#), + @r###" + [31; 32) '_': &[T] + [45; 56) '{ loop {} }': T + [47; 54) 'loop {}': ! + [52; 54) '{}': () + [65; 66) '_': S<&[T]> + [82; 93) '{ loop {} }': T + [84; 91) 'loop {}': ! + [89; 91) '{}': () + [122; 133) '{ loop {} }': *mut [T;_] + [124; 131) 'loop {}': ! + [129; 131) '{}': () + [160; 173) '{ gen() }': *mut [U] + [166; 169) 'gen': fn gen() -> *mut [T;_] + [166; 171) 'gen()': *mut [U;_] + [186; 420) '{ ...rr); }': () + [196; 199) 'arr': &[u8;_] + [212; 216) '&[1]': &[u8;_] + [213; 216) '[1]': [u8;_] + [214; 215) '1': u8 + [227; 228) 'a': &[u8] + [237; 240) 'arr': &[u8;_] + [250; 251) 'b': u8 + [254; 255) 'f': fn f(&[T]) -> T + [254; 260) 'f(arr)': u8 + [256; 259) 'arr': &[u8;_] + [270; 271) 'c': &[u8] + [280; 287) '{ arr }': &[u8] + [282; 285) 'arr': &[u8;_] + [297; 298) 'd': u8 + [301; 302) 'g': fn g(S<&[T]>) -> T + [301; 316) 'g(S { a: arr })': u8 + [303; 315) 'S { a: arr }': S<&[u8]> + [310; 313) 'arr': &[u8;_] + [326; 327) 'e': [&[u8];_] + [341; 346) '[arr]': [&[u8];_] + [342; 345) 'arr': &[u8;_] + [356; 357) 'f': [&[u8];_] + [371; 379) '[arr; 2]': [&[u8];_] + [372; 375) 'arr': &[u8;_] + [377; 378) '2': usize + [389; 390) 'g': (&[u8], &[u8]) + [407; 417) '(arr, arr)': (&[u8], &[u8]) + [408; 411) 'arr': &[u8;_] + [413; 416) 'arr': &[u8;_] + "### + ); +} + #[test] fn infer_let_stmt_coerce() { assert_snapshot!( @@ -102,7 +193,7 @@ fn test() { "#), @r###" [11; 12) 'x': &[T] - [28; 39) '{ loop {} }': ! + [28; 39) '{ loop {} }': &[T] [30; 37) 'loop {}': ! [35; 37) '{}': () [50; 126) '{ ... }; }': () @@ -119,7 +210,7 @@ fn test() { [113; 117) '&[1]': &[i32;_] [114; 117) '[1]': [i32;_] [115; 116) '1': i32 -"### + "### ); } @@ -138,7 +229,7 @@ fn test() { "#), @r###" [11; 12) 'x': &[T] - [28; 39) '{ loop {} }': ! + [28; 39) '{ loop {} }': &[T] [30; 37) 'loop {}': ! [35; 37) '{}': () [50; 126) '{ ... }; }': () @@ -155,7 +246,7 @@ fn test() { [112; 116) '&[1]': &[i32;_] [113; 116) '[1]': [i32;_] [114; 115) '1': i32 -"### + "### ); } @@ -174,7 +265,7 @@ fn test(i: i32) { "#), @r###" [11; 12) 'x': &[T] - [28; 39) '{ loop {} }': ! + [28; 39) '{ loop {} }': &[T] [30; 37) 'loop {}': ! [35; 37) '{}': () [48; 49) 'i': i32 @@ -215,7 +306,7 @@ fn test(i: i32) { "#), @r###" [11; 12) 'x': &[T] - [28; 39) '{ loop {} }': ! + [28; 39) '{ loop {} }': &[T] [30; 37) 'loop {}': ! [35; 37) '{}': () [48; 49) 'i': i32 -- cgit v1.2.3