From 3bbc70a5f72a8e54e50d981c01d748d3d9087b5d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Thu, 26 Oct 2023 18:33:03 +0000
Subject: [PATCH] Restrict param constraint suggestion

When encountering an associated item with a type param that could be
constrained, do not look at the parent item if the type param comes from
the associated item.

Fix #117209.
---
 .../infer/error_reporting/note_and_explain.rs | 11 +++++---
 ...on-for-type-param-of-current-assoc-item.rs | 28 +++++++++++++++++++
 ...or-type-param-of-current-assoc-item.stderr | 18 ++++++++++++
 3 files changed, 53 insertions(+), 4 deletions(-)
 create mode 100644 tests/ui/associated-type-bounds/do-not-look-at-parent-item-in-suggestion-for-type-param-of-current-assoc-item.rs
 create mode 100644 tests/ui/associated-type-bounds/do-not-look-at-parent-item-in-suggestion-for-type-param-of-current-assoc-item.stderr

diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index 00289f9bfb4..799e41650b6 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -364,12 +364,11 @@ impl<T> Trait<T> for X {
         };
         // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
         // This will also work for `impl Trait`.
-        let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
-            let generics = tcx.generics_of(body_owner_def_id);
-            generics.type_param(param_ty, tcx).def_id
-        } else {
+        let ty::Param(param_ty) = proj_ty.self_ty().kind() else {
             return false;
         };
+        let generics = tcx.generics_of(body_owner_def_id);
+        let def_id = generics.type_param(param_ty, tcx).def_id;
         let Some(def_id) = def_id.as_local() else {
             return false;
         };
@@ -390,6 +389,10 @@ impl<T> Trait<T> for X {
                 return true;
             }
         }
+        if (param_ty.index as usize) >= generics.parent_count {
+            // The param comes from the current item, do not look at the parent. (#117209)
+            return false;
+        }
         // If associated item, look to constrain the params of the trait/impl.
         let hir_id = match item {
             hir::Node::ImplItem(item) => item.hir_id(),
diff --git a/tests/ui/associated-type-bounds/do-not-look-at-parent-item-in-suggestion-for-type-param-of-current-assoc-item.rs b/tests/ui/associated-type-bounds/do-not-look-at-parent-item-in-suggestion-for-type-param-of-current-assoc-item.rs
new file mode 100644
index 00000000000..c1047d85645
--- /dev/null
+++ b/tests/ui/associated-type-bounds/do-not-look-at-parent-item-in-suggestion-for-type-param-of-current-assoc-item.rs
@@ -0,0 +1,28 @@
+use std::collections::HashMap;
+use std::hash::Hash;
+
+trait LowT: Identify {}
+
+trait Identify {
+    type Id: Clone + Hash + PartialEq + Eq;
+    fn identify(&self) -> Self::Id;
+}
+
+struct MapStore<L, I>
+where
+    L: LowT + Identify<Id = I>,
+{
+    lows: HashMap<I, L>,
+}
+
+impl<L, I> MapStore<L, I>
+where
+    L: LowT + Identify<Id = I>,
+    I: Clone + Hash + PartialEq + Eq,
+{
+    fn remove_low(&mut self, low: &impl LowT) {
+        let _low = self.lows.remove(low.identify()).unwrap(); //~ ERROR mismatched types
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/associated-type-bounds/do-not-look-at-parent-item-in-suggestion-for-type-param-of-current-assoc-item.stderr b/tests/ui/associated-type-bounds/do-not-look-at-parent-item-in-suggestion-for-type-param-of-current-assoc-item.stderr
new file mode 100644
index 00000000000..78bf93c32d5
--- /dev/null
+++ b/tests/ui/associated-type-bounds/do-not-look-at-parent-item-in-suggestion-for-type-param-of-current-assoc-item.stderr
@@ -0,0 +1,18 @@
+error[E0308]: mismatched types
+  --> $DIR/do-not-look-at-parent-item-in-suggestion-for-type-param-of-current-assoc-item.rs:24:37
+   |
+LL |         let _low = self.lows.remove(low.identify()).unwrap();
+   |                              ------ ^^^^^^^^^^^^^^ expected `&I`, found associated type
+   |                              |
+   |                              arguments to this method are incorrect
+   |
+   = note:    expected reference `&I`
+           found associated type `<impl LowT as Identify>::Id`
+   = help: consider constraining the associated type `<impl LowT as Identify>::Id` to `&I`
+   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+note: method defined here
+  --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.