about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-02-16 12:16:43 +0100
committerLukas Wirth <lukastw97@gmail.com>2024-02-16 12:16:43 +0100
commitfd652ceb73539ebee1fc9ea3166b35c227167b25 (patch)
tree4662f1ea1c1cfd17b9e4f68c238ef73fc74d27e9
parentc00c9ee95965953c8f79b9ce71f766b06eff1a33 (diff)
downloadrust-fd652ceb73539ebee1fc9ea3166b35c227167b25.tar.gz
rust-fd652ceb73539ebee1fc9ea3166b35c227167b25.zip
fix: Don't show type mismatches for `{unknown}` to non-`{unknown}` mismatches
-rw-r--r--crates/hir-ty/src/infer.rs131
-rw-r--r--crates/hir-ty/src/tests/diagnostics.rs42
-rw-r--r--crates/hir-ty/src/tests/simple.rs3
3 files changed, 168 insertions, 8 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index b0ae437ee3c..1977f00517c 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -26,7 +26,7 @@ use std::{convert::identity, ops::Index};
 
 use chalk_ir::{
     cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
-    Scalar, TyKind, TypeFlags,
+    Scalar, TyKind, TypeFlags, Variance,
 };
 use either::Either;
 use hir_def::{
@@ -58,8 +58,9 @@ use crate::{
     static_lifetime, to_assoc_type_id,
     traits::FnTrait,
     utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
-    AliasEq, AliasTy, ClosureId, DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment,
-    Interner, ProjectionTy, RpitId, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt,
+    AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
+    InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment,
+    TraitRef, Ty, TyBuilder, TyExt,
 };
 
 // This lint has a false positive here. See the link below for details.
@@ -688,10 +689,17 @@ impl<'a> InferenceContext<'a> {
         for ty in type_of_for_iterator.values_mut() {
             *ty = table.resolve_completely(ty.clone());
         }
-        for mismatch in type_mismatches.values_mut() {
+        type_mismatches.retain(|_, mismatch| {
             mismatch.expected = table.resolve_completely(mismatch.expected.clone());
             mismatch.actual = table.resolve_completely(mismatch.actual.clone());
-        }
+            chalk_ir::zip::Zip::zip_with(
+                &mut UnknownMismatch(self.db),
+                Variance::Invariant,
+                &mismatch.expected,
+                &mismatch.actual,
+            )
+            .is_ok()
+        });
         diagnostics.retain_mut(|diagnostic| {
             use InferenceDiagnostic::*;
             match diagnostic {
@@ -1502,3 +1510,116 @@ impl std::ops::BitOrAssign for Diverges {
         *self = *self | other;
     }
 }
+/// A zipper that checks for unequal `{unknown}` occurrences in the two types. Used to filter out
+/// mismatch diagnostics that only differ in `{unknown}`. These mismatches are usually not helpful.
+/// As the cause is usually an underlying name resolution problem.
+struct UnknownMismatch<'db>(&'db dyn HirDatabase);
+impl chalk_ir::zip::Zipper<Interner> for UnknownMismatch<'_> {
+    fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> chalk_ir::Fallible<()> {
+        let zip_substs = |this: &mut Self,
+                          variances,
+                          sub_a: &Substitution,
+                          sub_b: &Substitution| {
+            this.zip_substs(variance, variances, sub_a.as_slice(Interner), sub_b.as_slice(Interner))
+        };
+        match (a.kind(Interner), b.kind(Interner)) {
+            (TyKind::Adt(id_a, sub_a), TyKind::Adt(id_b, sub_b)) if id_a == id_b => zip_substs(
+                self,
+                Some(self.unification_database().adt_variance(*id_a)),
+                sub_a,
+                sub_b,
+            )?,
+            (
+                TyKind::AssociatedType(assoc_ty_a, sub_a),
+                TyKind::AssociatedType(assoc_ty_b, sub_b),
+            ) if assoc_ty_a == assoc_ty_b => zip_substs(self, None, sub_a, sub_b)?,
+            (TyKind::Tuple(arity_a, sub_a), TyKind::Tuple(arity_b, sub_b))
+                if arity_a == arity_b =>
+            {
+                zip_substs(self, None, sub_a, sub_b)?
+            }
+            (TyKind::OpaqueType(opaque_ty_a, sub_a), TyKind::OpaqueType(opaque_ty_b, sub_b))
+                if opaque_ty_a == opaque_ty_b =>
+            {
+                zip_substs(self, None, sub_a, sub_b)?
+            }
+            (TyKind::Slice(ty_a), TyKind::Slice(ty_b)) => self.zip_tys(variance, ty_a, ty_b)?,
+            (TyKind::FnDef(fn_def_a, sub_a), TyKind::FnDef(fn_def_b, sub_b))
+                if fn_def_a == fn_def_b =>
+            {
+                zip_substs(
+                    self,
+                    Some(self.unification_database().fn_def_variance(*fn_def_a)),
+                    sub_a,
+                    sub_b,
+                )?
+            }
+            (TyKind::Ref(mutability_a, _, ty_a), TyKind::Ref(mutability_b, _, ty_b))
+                if mutability_a == mutability_b =>
+            {
+                self.zip_tys(variance, ty_a, ty_b)?
+            }
+            (TyKind::Raw(mutability_a, ty_a), TyKind::Raw(mutability_b, ty_b))
+                if mutability_a == mutability_b =>
+            {
+                self.zip_tys(variance, ty_a, ty_b)?
+            }
+            (TyKind::Array(ty_a, const_a), TyKind::Array(ty_b, const_b)) if const_a == const_b => {
+                self.zip_tys(variance, ty_a, ty_b)?
+            }
+            (TyKind::Closure(id_a, sub_a), TyKind::Closure(id_b, sub_b)) if id_a == id_b => {
+                zip_substs(self, None, sub_a, sub_b)?
+            }
+            (TyKind::Coroutine(coroutine_a, sub_a), TyKind::Coroutine(coroutine_b, sub_b))
+                if coroutine_a == coroutine_b =>
+            {
+                zip_substs(self, None, sub_a, sub_b)?
+            }
+            (
+                TyKind::CoroutineWitness(coroutine_a, sub_a),
+                TyKind::CoroutineWitness(coroutine_b, sub_b),
+            ) if coroutine_a == coroutine_b => zip_substs(self, None, sub_a, sub_b)?,
+            (TyKind::Function(fn_ptr_a), TyKind::Function(fn_ptr_b))
+                if fn_ptr_a.sig == fn_ptr_b.sig && fn_ptr_a.num_binders == fn_ptr_b.num_binders =>
+            {
+                zip_substs(self, None, &fn_ptr_a.substitution.0, &fn_ptr_b.substitution.0)?
+            }
+            (TyKind::Error, TyKind::Error) => (),
+            (TyKind::Error, _) | (_, TyKind::Error) => return Err(chalk_ir::NoSolution),
+            _ => (),
+        }
+
+        Ok(())
+    }
+
+    fn zip_lifetimes(&mut self, _: Variance, _: &Lifetime, _: &Lifetime) -> chalk_ir::Fallible<()> {
+        Ok(())
+    }
+
+    fn zip_consts(&mut self, _: Variance, _: &Const, _: &Const) -> chalk_ir::Fallible<()> {
+        Ok(())
+    }
+
+    fn zip_binders<T>(
+        &mut self,
+        variance: Variance,
+        a: &Binders<T>,
+        b: &Binders<T>,
+    ) -> chalk_ir::Fallible<()>
+    where
+        T: Clone
+            + HasInterner<Interner = Interner>
+            + chalk_ir::zip::Zip<Interner>
+            + TypeFoldable<Interner>,
+    {
+        chalk_ir::zip::Zip::zip_with(self, variance, a.skip_binders(), b.skip_binders())
+    }
+
+    fn interner(&self) -> Interner {
+        Interner
+    }
+
+    fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {
+        &self.0
+    }
+}
diff --git a/crates/hir-ty/src/tests/diagnostics.rs b/crates/hir-ty/src/tests/diagnostics.rs
index 1876be303ad..80f92eaf435 100644
--- a/crates/hir-ty/src/tests/diagnostics.rs
+++ b/crates/hir-ty/src/tests/diagnostics.rs
@@ -1,3 +1,5 @@
+use crate::tests::check_no_mismatches;
+
 use super::check;
 
 #[test]
@@ -94,3 +96,43 @@ fn test(x: bool) {
 "#,
     );
 }
+
+#[test]
+fn no_mismatches_on_atpit() {
+    check_no_mismatches(
+        r#"
+//- minicore: option, sized
+#![feature(impl_trait_in_assoc_type)]
+
+trait WrappedAssoc {
+    type Assoc;
+    fn do_thing(&self) -> Option<Self::Assoc>;
+}
+
+struct Foo;
+impl WrappedAssoc for Foo {
+    type Assoc = impl Sized;
+
+    fn do_thing(&self) -> Option<Self::Assoc> {
+        Some(())
+    }
+}
+"#,
+    );
+    check_no_mismatches(
+        r#"
+//- minicore: option, sized
+#![feature(impl_trait_in_assoc_type)]
+
+trait Trait {
+    type Assoc;
+    const DEFINE: Option<Self::Assoc>;
+}
+
+impl Trait for () {
+    type Assoc = impl Sized;
+    const DEFINE: Option<Self::Assoc> = Option::Some(());
+}
+"#,
+    );
+}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 84747822826..6c7dbe1db6f 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -3376,11 +3376,8 @@ fn main() {
     [x,] = &[1,];
   //^^^^expected &[i32; 1], got [{unknown}; _]
 
-    // FIXME we only want the outermost error, but this matches the current
-    // behavior of slice patterns
     let x;
     [(x,),] = &[(1,),];
-  // ^^^^expected {unknown}, got ({unknown},)
   //^^^^^^^expected &[(i32,); 1], got [{unknown}; _]
 
     let x;