From 3bbc70a5f72a8e54e50d981c01d748d3d9087b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 26 Oct 2023 18:33:03 +0000 Subject: [PATCH 1/2] 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 00289f9bfb411..799e41650b692 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 Trait for X { }; // Get the `DefId` for the type parameter corresponding to `A` in `::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 Trait 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 0000000000000..c1047d85645c5 --- /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 +where + L: LowT + Identify, +{ + lows: HashMap, +} + +impl MapStore +where + L: LowT + Identify, + 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 0000000000000..78bf93c32d57c --- /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 `::Id` + = help: consider constraining the associated type `::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`. From 7449478c2f6fd2d72c12a51d8562f1e6108facab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 26 Oct 2023 19:56:46 +0000 Subject: [PATCH 2/2] Account for type param from other item in `note_and_explain` Fix #89868. --- .../infer/error_reporting/note_and_explain.rs | 104 ++++++++++-------- compiler/rustc_middle/src/ty/generics.rs | 28 +++++ .../do-not-ice-on-note_and_explain.rs | 7 ++ .../do-not-ice-on-note_and_explain.stderr | 79 +++++++++++++ 4 files changed, 175 insertions(+), 43 deletions(-) create mode 100644 tests/ui/malformed/do-not-ice-on-note_and_explain.rs create mode 100644 tests/ui/malformed/do-not-ice-on-note_and_explain.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 799e41650b692..a8ae43d52978d 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 @@ -54,13 +54,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } (ty::Param(expected), ty::Param(found)) => { let generics = tcx.generics_of(body_owner_def_id); - let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id); - if !sp.contains(e_span) { - diag.span_label(e_span, "expected type parameter"); + if let Some(param) = generics.opt_type_param(expected, tcx) { + let e_span = tcx.def_span(param.def_id); + if !sp.contains(e_span) { + diag.span_label(e_span, "expected type parameter"); + } } - let f_span = tcx.def_span(generics.type_param(found, tcx).def_id); - if !sp.contains(f_span) { - diag.span_label(f_span, "found type parameter"); + if let Some(param) = generics.opt_type_param(found, tcx) { + let f_span = tcx.def_span(param.def_id); + if !sp.contains(f_span) { + diag.span_label(f_span, "found type parameter"); + } } diag.note( "a type parameter was expected, but a different one was found; \ @@ -83,23 +87,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { | (ty::Alias(ty::Projection, proj), ty::Param(p)) if !tcx.is_impl_trait_in_trait(proj.def_id) => { - let p_def_id = tcx.generics_of(body_owner_def_id).type_param(p, tcx).def_id; - let p_span = tcx.def_span(p_def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); - } - let hir = tcx.hir(); + let parent = tcx.generics_of(body_owner_def_id) + .opt_type_param(p, tcx) + .and_then(|param| { + let p_def_id = param.def_id; + let p_span = tcx.def_span(p_def_id); + let expected = match (values.expected.kind(), values.found.kind()) { + (ty::Param(_), _) => "expected ", + (_, ty::Param(_)) => "found ", + _ => "", + }; + if !sp.contains(p_span) { + diag.span_label( + p_span, + format!("{expected}this type parameter"), + ); + } + p_def_id.as_local().and_then(|id| { + let local_id = tcx.hir().local_def_id_to_hir_id(id); + let generics = tcx.hir().find_parent(local_id)?.generics()?; + Some((id, generics)) + }) + }); let mut note = true; - let parent = p_def_id.as_local().and_then(|id| { - let local_id = hir.local_def_id_to_hir_id(id); - let generics = tcx.hir().find_parent(local_id)?.generics()?; - Some((id, generics)) - }); if let Some((local_id, generics)) = parent { // Synthesize the associated type restriction `Add`. // FIXME: extract this logic for use in other diagnostics. @@ -172,14 +182,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..)) | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => { let generics = tcx.generics_of(body_owner_def_id); - let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); + if let Some(param) = generics.opt_type_param(p, tcx) { + let p_span = tcx.def_span(param.def_id); + let expected = match (values.expected.kind(), values.found.kind()) { + (ty::Param(_), _) => "expected ", + (_, ty::Param(_)) => "found ", + _ => "", + }; + if !sp.contains(p_span) { + diag.span_label(p_span, format!("{expected}this type parameter")); + } } diag.help("type parameters must be constrained to match other types"); if tcx.sess.teach(&diag.get_code().unwrap()) { @@ -217,9 +229,11 @@ impl Trait for X { } (ty::Param(p), ty::Closure(..) | ty::Coroutine(..)) => { let generics = tcx.generics_of(body_owner_def_id); - let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); - if !sp.contains(p_span) { - diag.span_label(p_span, "expected this type parameter"); + if let Some(param) = generics.opt_type_param(p, tcx) { + let p_span = tcx.def_span(param.def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "expected this type parameter"); + } } diag.help(format!( "every closure has a distinct type and so could not always match the \ @@ -228,14 +242,16 @@ impl Trait for X { } (ty::Param(p), _) | (_, ty::Param(p)) => { let generics = tcx.generics_of(body_owner_def_id); - let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); + if let Some(param) = generics.opt_type_param(p, tcx) { + let p_span = tcx.def_span(param.def_id); + let expected = match (values.expected.kind(), values.found.kind()) { + (ty::Param(_), _) => "expected ", + (_, ty::Param(_)) => "found ", + _ => "", + }; + if !sp.contains(p_span) { + diag.span_label(p_span, format!("{expected}this type parameter")); + } } } (ty::Alias(ty::Projection | ty::Inherent, proj_ty), _) @@ -368,8 +384,10 @@ impl Trait for X { 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 { + let Some(param) = generics.opt_type_param(param_ty, tcx) else { + return false; + }; + let Some(def_id) = param.def_id.as_local() else { return false; }; diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 44e88d3e2302f..888ee1d237ae7 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -237,6 +237,20 @@ impl<'tcx> Generics { } } + /// Returns the `GenericParamDef` with the given index if available. + pub fn opt_param_at( + &'tcx self, + param_index: usize, + tcx: TyCtxt<'tcx>, + ) -> Option<&'tcx GenericParamDef> { + if let Some(index) = param_index.checked_sub(self.parent_count) { + self.params.get(index) + } else { + tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) + .opt_param_at(param_index, tcx) + } + } + pub fn params_to(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx [GenericParamDef] { if let Some(index) = param_index.checked_sub(self.parent_count) { &self.params[..index] @@ -268,6 +282,20 @@ impl<'tcx> Generics { } } + /// Returns the `GenericParamDef` associated with this `ParamTy` if it belongs to this + /// `Generics`. + pub fn opt_type_param( + &'tcx self, + param: &ParamTy, + tcx: TyCtxt<'tcx>, + ) -> Option<&'tcx GenericParamDef> { + let param = self.opt_param_at(param.index as usize, tcx)?; + match param.kind { + GenericParamDefKind::Type { .. } => Some(param), + _ => None, + } + } + /// Returns the `GenericParamDef` associated with this `ParamConst`. pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { let param = self.param_at(param.index as usize, tcx); diff --git a/tests/ui/malformed/do-not-ice-on-note_and_explain.rs b/tests/ui/malformed/do-not-ice-on-note_and_explain.rs new file mode 100644 index 0000000000000..e65276fb738dd --- /dev/null +++ b/tests/ui/malformed/do-not-ice-on-note_and_explain.rs @@ -0,0 +1,7 @@ +struct A(B); +implA{fn d(){fn d(){Self(1)}}} +//~^ ERROR the size for values of type `B` cannot be known at compilation time +//~| ERROR the size for values of type `B` cannot be known at compilation time +//~| ERROR mismatched types +//~| ERROR mismatched types +//~| ERROR `main` function not found in crate diff --git a/tests/ui/malformed/do-not-ice-on-note_and_explain.stderr b/tests/ui/malformed/do-not-ice-on-note_and_explain.stderr new file mode 100644 index 0000000000000..27b86145e90f4 --- /dev/null +++ b/tests/ui/malformed/do-not-ice-on-note_and_explain.stderr @@ -0,0 +1,79 @@ +error[E0601]: `main` function not found in crate `do_not_ice_on_note_and_explain` + --> $DIR/do-not-ice-on-note_and_explain.rs:2:37 + | +LL | implA{fn d(){fn d(){Self(1)}}} + | ^ consider adding a `main` function to `$DIR/do-not-ice-on-note_and_explain.rs` + +error[E0277]: the size for values of type `B` cannot be known at compilation time + --> $DIR/do-not-ice-on-note_and_explain.rs:2:32 + | +LL | implA{fn d(){fn d(){Self(1)}}} + | - ---- ^ doesn't have a size known at compile-time + | | | + | | required by a bound introduced by this call + | this type parameter needs to be `Sized` + | +note: required by a bound in `A` + --> $DIR/do-not-ice-on-note_and_explain.rs:1:10 + | +LL | struct A(B); + | ^ required by this bound in `A` + +error[E0308]: mismatched types + --> $DIR/do-not-ice-on-note_and_explain.rs:2:32 + | +LL | implA{fn d(){fn d(){Self(1)}}} + | ---- ^ expected type parameter `B`, found integer + | | + | arguments to this function are incorrect + | + = note: expected type parameter `B` + found type `{integer}` +note: tuple struct defined here + --> $DIR/do-not-ice-on-note_and_explain.rs:1:8 + | +LL | struct A(B); + | ^ + +error[E0308]: mismatched types + --> $DIR/do-not-ice-on-note_and_explain.rs:2:27 + | +LL | implA{fn d(){fn d(){Self(1)}}} + | ^^^^^^^ expected `()`, found `A` + | + = note: expected unit type `()` + found struct `A` +help: consider using a semicolon here + | +LL | implA{fn d(){fn d(){Self(1);}}} + | + +help: try adding a return type + | +LL | implA{fn d(){fn d() -> A{Self(1)}}} + | +++++++ + +error[E0277]: the size for values of type `B` cannot be known at compilation time + --> $DIR/do-not-ice-on-note_and_explain.rs:2:27 + | +LL | implA{fn d(){fn d(){Self(1)}}} + | - ^^^^^^^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | +note: required by a bound in `A` + --> $DIR/do-not-ice-on-note_and_explain.rs:1:10 + | +LL | struct A(B); + | ^ required by this bound in `A` +help: you could relax the implicit `Sized` bound on `B` if it were used through indirection like `&B` or `Box` + --> $DIR/do-not-ice-on-note_and_explain.rs:1:10 + | +LL | struct A(B); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `B: ?Sized`... + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0308, E0601. +For more information about an error, try `rustc --explain E0277`.