diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 959b1e21240..89200255a28 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -51,7 +51,7 @@ fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool { // Trivial cases, this should go after `never` check to // avoid infer result type to be never _ => { - if self.table.unify_inner_trivial(&from_ty, &to_ty) { + if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) { return true; } } @@ -175,7 +175,7 @@ fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { return self.table.unify_substs(st1, st2, 0); } _ => { - if self.table.unify_inner_trivial(&derefed_ty, &to_ty) { + if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) { return true; } } diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index 5f6cea8d318..ab0bc8b70b6 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs @@ -8,7 +8,8 @@ use super::{InferenceContext, Obligation}; use crate::{ - BoundVar, Canonical, DebruijnIndex, InEnvironment, InferTy, Substs, Ty, TypeCtor, TypeWalk, + BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty, + TypeCtor, TypeWalk, }; impl<'a> InferenceContext<'a> { @@ -226,16 +227,26 @@ fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { (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), + + _ => self.unify_inner_trivial(&ty1, &ty2, depth), } } - pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool { + pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { match (ty1, ty2) { (Ty::Unknown, _) | (_, Ty::Unknown) => true, (Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true, + (Ty::Dyn(dyn1), Ty::Dyn(dyn2)) if dyn1.len() == dyn2.len() => { + for (pred1, pred2) in dyn1.iter().zip(dyn2.iter()) { + if !self.unify_preds(pred1, pred2, depth + 1) { + return false; + } + } + 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))) @@ -268,6 +279,31 @@ pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool { } } + fn unify_preds( + &mut self, + pred1: &GenericPredicate, + pred2: &GenericPredicate, + depth: usize, + ) -> bool { + match (pred1, pred2) { + (GenericPredicate::Implemented(tr1), GenericPredicate::Implemented(tr2)) + if tr1.trait_ == tr2.trait_ => + { + self.unify_substs(&tr1.substs, &tr2.substs, depth + 1) + } + (GenericPredicate::Projection(proj1), GenericPredicate::Projection(proj2)) + if proj1.projection_ty.associated_ty == proj2.projection_ty.associated_ty => + { + self.unify_substs( + &proj1.projection_ty.parameters, + &proj2.projection_ty.parameters, + depth + 1, + ) && self.unify_inner(&proj1.ty, &proj2.ty, depth + 1) + } + _ => false, + } + } + /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. pub fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index dc517fc4a6a..f6e3e07cde1 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -2378,3 +2378,27 @@ fn main() { ); assert_eq!(t, "Foo"); } + +#[test] +fn trait_object_no_coercion() { + assert_snapshot!( + infer_with_mismatches(r#" +trait Foo {} + +fn foo(x: &dyn Foo) {} + +fn test(x: &dyn Foo) { + foo(x); +} +"#, true), + @r###" + [22; 23) 'x': &dyn Foo + [35; 37) '{}': () + [47; 48) 'x': &dyn Foo + [60; 75) '{ foo(x); }': () + [66; 69) 'foo': fn foo(&dyn Foo) + [66; 72) 'foo(x)': () + [70; 71) 'x': &dyn Foo + "### + ); +}