diff options
| author | bors <bors@rust-lang.org> | 2023-12-08 09:30:52 +0000 | 
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-12-08 09:30:52 +0000 | 
| commit | 5ea62560f294947e55b0cf895dae23fe3c41777c (patch) | |
| tree | 86937c70c9ab49acadc4529441313be787653edb | |
| parent | 8043f62258602051caf50e2d83e9b7c1329d14ff (diff) | |
| parent | 55559d93e74d5cf48a041803597201640046b5bd (diff) | |
| download | rust-5ea62560f294947e55b0cf895dae23fe3c41777c.tar.gz rust-5ea62560f294947e55b0cf895dae23fe3c41777c.zip | |
Auto merge of #118668 - fmease:resolve-assoc-item-bindings-by-namespace, r=compiler-errors
Resolve associated item bindings by namespace
This is the 3rd commit split off from #118360 with tests reblessed (they no longer contain duplicated diags which were caused by 4c0addc80af4666f26d7ad51fe34a0e2dd0b8b74) & slightly adapted (removed supertraits from a UI test, cc #118040).
> * Resolve all assoc item bindings (type, const, fn (feature `return_type_notation`)) by namespace instead of trying to resolve a type first (in the non-RTN case) and falling back to consts afterwards. This is consistent with RTN. E.g., for `Tr<K = {…}>` we now always try to look up assoc consts (this extends to supertrait bounds). This gets rid of assoc tys shadowing assoc consts in assoc item bindings which is undesirable & inconsistent (types and consts live in different namespaces after all)
> * Consolidate the resolution of assoc {ty, const} bindings and RTN (dedup, better diags for RTN)
> * Fix assoc consts being labeled as assoc *types* in several diagnostics
> * Make a bunch of diagnostics translatable
Fixes #112560 (error → pass).
As discussed
r? `@compiler-errors`
---
**Addendum**: What I call “associated item bindings” are commonly referred to as “type bindings” for historical reasons. Nowadays, “type bindings” include assoc type bindings, assoc const bindings and RTN (return type notation) which is why I prefer not to use this outdated term.
27 files changed, 598 insertions, 416 deletions
| diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 8ab91bebcf6..139e1c0ac5f 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -1,8 +1,28 @@ +hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}` + .label = ambiguous associated {$assoc_kind} `{$assoc_name}` + hir_analysis_ambiguous_lifetime_bound = ambiguous lifetime bound, explicit lifetime bound required -hir_analysis_assoc_bound_on_const = expected associated type, found {$descr} - .note = trait bounds not allowed on {$descr} +hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}` + +hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> + [true] an + *[false] a similarly named + } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` +hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found +hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind} +hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` +hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name +hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name + +hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} + .label = unexpected {$got} + .expected_because_label = expected a {$expected} because of this associated {$expected} + .note = the associated {$assoc_kind} is defined here + .bound_on_assoc_const_label = bounds are not allowed on associated constants + +hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here hir_analysis_assoc_type_binding_not_allowed = associated type bindings are not allowed here @@ -280,10 +300,6 @@ hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is no hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` -hir_analysis_return_type_notation_conflicting_bound = - ambiguous associated function `{$assoc_name}` for `{$ty_name}` - .note = `{$assoc_name}` is declared in two supertraits: `{$first_bound}` and `{$second_bound}` - hir_analysis_return_type_notation_equality_bound = return type notation is not allowed to use type equality @@ -294,9 +310,6 @@ hir_analysis_return_type_notation_illegal_param_type = return type notation is not allowed for functions that have type parameters .label = type parameter declared here -hir_analysis_return_type_notation_missing_method = - cannot find associated function `{$assoc_name}` for `{$ty_name}` - hir_analysis_return_type_notation_on_non_rpitit = return type notation used on function that is not `async` and does not return `impl Trait` .note = function returns `{$ty}`, which is not compatible with associated type return bounds diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index f6907019d6e..dfec3c5e829 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -3,8 +3,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_lint_defs::Applicability; -use rustc_middle::ty::{self as ty, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self as ty, Ty}; use rustc_span::symbol::Ident; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits; @@ -256,64 +255,49 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let tcx = self.tcx(); - let return_type_notation = - binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation; - - let candidate = if return_type_notation { - if self.trait_defines_associated_item_named( - trait_ref.def_id(), - ty::AssocKind::Fn, - binding.item_name, - ) { - trait_ref + let assoc_kind = + if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation { + ty::AssocKind::Fn + } else if let ConvertedBindingKind::Equality(term) = binding.kind + && let ty::TermKind::Const(_) = term.node.unpack() + { + ty::AssocKind::Const } else { - self.one_bound_for_assoc_method( - traits::supertraits(tcx, trait_ref), - trait_ref.print_only_trait_path(), - binding.item_name, - path_span, - )? - } - } else if self.trait_defines_associated_item_named( + ty::AssocKind::Type + }; + + let candidate = if self.trait_defines_associated_item_named( trait_ref.def_id(), - ty::AssocKind::Type, + assoc_kind, binding.item_name, ) { - // Simple case: X is defined in the current trait. + // Simple case: The assoc item is defined in the current trait. trait_ref } else { // Otherwise, we have to walk through the supertraits to find - // those that do. - self.one_bound_for_assoc_type( + // one that does define it. + self.one_bound_for_assoc_item( || traits::supertraits(tcx, trait_ref), trait_ref.skip_binder().print_only_trait_name(), None, + assoc_kind, binding.item_name, path_span, - match binding.kind { - ConvertedBindingKind::Equality(term) => Some(term), - _ => None, - }, + Some(&binding), )? }; let (assoc_ident, def_scope) = tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id); - // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead - // of calling `filter_by_name_and_kind`. - let find_item_of_kind = |kind| { - tcx.associated_items(candidate.def_id()) - .filter_by_name_unhygienic(assoc_ident.name) - .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) - }; - let assoc_item = if return_type_notation { - find_item_of_kind(ty::AssocKind::Fn) - } else { - find_item_of_kind(ty::AssocKind::Type) - .or_else(|| find_item_of_kind(ty::AssocKind::Const)) - } - .expect("missing associated type"); + // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` + // instead of calling `filter_by_name_and_kind` which would needlessly normalize the + // `assoc_ident` again and again. + let assoc_item = tcx + .associated_items(candidate.def_id()) + .filter_by_name_unhygienic(assoc_ident.name) + .find(|i| i.kind == assoc_kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) + .expect("missing associated item"); if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) { tcx.sess @@ -340,7 +324,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { .or_insert(binding.span); } - let projection_ty = if return_type_notation { + let projection_ty = if let ty::AssocKind::Fn = assoc_kind { let mut emitted_bad_param_err = false; // If we have an method return type bound, then we need to substitute // the method's early bound params with suitable late-bound params. @@ -467,7 +451,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&projection_ty); let late_bound_in_ty = - tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty)); + tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty.node)); debug!(?late_bound_in_trait_ref); debug!(?late_bound_in_ty); @@ -492,77 +476,27 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - let assoc_item_def_id = projection_ty.skip_binder().def_id; - let def_kind = tcx.def_kind(assoc_item_def_id); match binding.kind { - ConvertedBindingKind::Equality(..) if return_type_notation => { + ConvertedBindingKind::Equality(..) if let ty::AssocKind::Fn = assoc_kind => { return Err(self.tcx().sess.emit_err( crate::errors::ReturnTypeNotationEqualityBound { span: binding.span }, )); } - ConvertedBindingKind::Equality(mut term) => { + ConvertedBindingKind::Equality(term) => { // "Desugar" a constraint like `T: Iterator<Item = u32>` this to // the "projection predicate" for: // // `<T as Iterator>::Item = u32` - match (def_kind, term.unpack()) { - (DefKind::AssocTy, ty::TermKind::Ty(_)) - | (DefKind::AssocConst, ty::TermKind::Const(_)) => (), - (_, _) => { - let got = if let Some(_) = term.ty() { "type" } else { "constant" }; - let expected = tcx.def_descr(assoc_item_def_id); - let mut err = tcx.sess.struct_span_err( - binding.span, - format!("expected {expected} bound, found {got}"), - ); - err.span_note( - tcx.def_span(assoc_item_def_id), - format!("{expected} defined here"), - ); - - if let DefKind::AssocConst = def_kind - && let Some(t) = term.ty() - && (t.is_enum() || t.references_error()) - && tcx.features().associated_const_equality - { - err.span_suggestion( - binding.span, - "if equating a const, try wrapping with braces", - format!("{} = {{ const }}", binding.item_name), - Applicability::HasPlaceholders, - ); - } - let reported = err.emit(); - term = match def_kind { - DefKind::AssocTy => Ty::new_error(tcx, reported).into(), - DefKind::AssocConst => ty::Const::new_error( - tcx, - reported, - tcx.type_of(assoc_item_def_id) - .instantiate(tcx, projection_ty.skip_binder().args), - ) - .into(), - _ => unreachable!(), - }; - } - } bounds.push_projection_bound( tcx, - projection_ty - .map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }), + projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate { + projection_ty, + term: term.node, + }), binding.span, ); } ConvertedBindingKind::Constraint(ast_bounds) => { - match def_kind { - DefKind::AssocTy => {} - _ => { - return Err(tcx.sess.emit_err(errors::AssocBoundOnConst { - span: assoc_ident.span, - descr: tcx.def_descr(assoc_item_def_id), - })); - } - } // "Desugar" a constraint like `T: Iterator<Item: Debug>` to // // `<T as Iterator>::Item: Debug` diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index eb12ec7a098..13ad9a453b2 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -1,15 +1,16 @@ -use crate::astconv::AstConv; +use crate::astconv::{AstConv, ConvertedBindingKind}; use crate::errors::{ - AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, + self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, ParenthesizedFnTraitExpansion, }; +use crate::fluent_generated as fluent; use crate::traits::error_reporting::report_object_safety_error; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::traits::FulfillmentError; -use rustc_middle::ty::{self, suggest_constraining_type_param, AssocItem, AssocKind, Ty, TyCtxt}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident}; @@ -97,83 +98,88 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } - pub(crate) fn complain_about_assoc_type_not_found<I>( + pub(super) fn complain_about_assoc_item_not_found<I>( &self, all_candidates: impl Fn() -> I, ty_param_name: &str, ty_param_def_id: Option<LocalDefId>, + assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, + binding: Option<&super::ConvertedBinding<'_, 'tcx>>, ) -> ErrorGuaranteed where I: Iterator<Item = ty::PolyTraitRef<'tcx>>, { + let tcx = self.tcx(); + + // First and foremost, provide a more user-friendly & “intuitive” error on kind mismatches. + if let Some(assoc_item) = all_candidates().find_map(|r| { + tcx.associated_items(r.def_id()) + .filter_by_name_unhygienic(assoc_name.name) + .find(|item| tcx.hygienic_eq(assoc_name, item.ident(tcx), r.def_id())) + }) { + return self.complain_about_assoc_kind_mismatch( + assoc_item, assoc_kind, assoc_name, span, binding, + ); + } + + let assoc_kind_str = super::assoc_kind_str(assoc_kind); + // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a // valid span, so we point at the whole path segment instead. let is_dummy = assoc_name.span == DUMMY_SP; - let mut err = struct_span_err!( - self.tcx().sess, - if is_dummy { span } else { assoc_name.span }, - E0220, - "associated type `{}` not found for `{}`", + let mut err = errors::AssocItemNotFound { + span: if is_dummy { span } else { assoc_name.span }, assoc_name, - ty_param_name - ); + assoc_kind: assoc_kind_str, + ty_param_name, + label: None, + sugg: None, + }; if is_dummy { - err.span_label(span, format!("associated type `{assoc_name}` not found")); - return err.emit(); + err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span }); + return tcx.sess.emit_err(err); } let all_candidate_names: Vec<_> = all_candidates() - .flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) + .flat_map(|r| tcx.associated_items(r.def_id()).in_definition_order()) .filter_map(|item| { - if !item.is_impl_trait_in_trait() && item.kind == ty::AssocKind::Type { - Some(item.name) - } else { - None - } + (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name) }) .collect(); if let Some(suggested_name) = find_best_match_for_name(&all_candidate_names, assoc_name.name, None) { - err.span_suggestion( - assoc_name.span, - "there is an associated type with a similar name", + err.sugg = Some(errors::AssocItemNotFoundSugg::Similar { + span: assoc_name.span, + assoc_kind: assoc_kind_str, suggested_name, - Applicability::MaybeIncorrect, - ); - return err.emit(); + }); + return tcx.sess.emit_err(err); } // If we didn't find a good item in the supertraits (or couldn't get // the supertraits), like in ItemCtxt, then look more generally from // all visible traits. If there's one clear winner, just suggest that. - let visible_traits: Vec<_> = self - .tcx() + let visible_traits: Vec<_> = tcx .all_traits() .filter(|trait_def_id| { - let viz = self.tcx().visibility(*trait_def_id); + let viz = tcx.visibility(*trait_def_id); let def_id = self.item_def_id(); - viz.is_accessible_from(def_id, self.tcx()) + viz.is_accessible_from(def_id, tcx) }) .collect(); let wider_candidate_names: Vec<_> = visible_traits .iter() - .flat_map(|trait_def_id| { - self.tcx().associated_items(*trait_def_id).in_definition_order() - }) + .flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order()) .filter_map(|item| { - if !item.is_impl_trait_in_trait() && item.kind == ty::AssocKind::Type { - Some(item.name) - } else { - None - } + (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name) }) .collect(); @@ -182,52 +188,51 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { { if let [best_trait] = visible_traits .iter() + .copied() .filter(|trait_def_id| { - self.tcx() - .associated_items(*trait_def_id) + tcx.associated_items(trait_def_id) .filter_by_name_unhygienic(suggested_name) - .any(|item| item.kind == ty::AssocKind::Type) + .any(|item| item.kind == assoc_kind) }) .collect::<Vec<_>>()[..] { - let trait_name = self.tcx().def_path_str(*best_trait); - let an = if suggested_name != assoc_name.name { "a similarly named" } else { "an" }; - err.span_label( - assoc_name.span, - format!( - "there is {an} associated type `{suggested_name}` in the \ - trait `{trait_name}`", - ), - ); - let hir = self.tcx().hir(); + let trait_name = tcx.def_path_str(best_trait); + err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait { + span: assoc_name.span, + assoc_kind: assoc_kind_str, + trait_name: &trait_name, + suggested_name, + identically_named: suggested_name == assoc_name.name, + }); + let hir = tcx.hir(); if let Some(def_id) = ty_param_def_id - && let parent = hir.get_parent_item(self.tcx().local_def_id_to_hir_id(def_id)) + && let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id)) && let Some(generics) = hir.get_generics(parent.def_id) { if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( |b| match b { hir::GenericBound::Trait(t, ..) => { - t.trait_ref.trait_def_id().as_ref() == Some(best_trait) + t.trait_ref.trait_def_id() == Some(best_trait) } _ => false, }, ) { // The type param already has a bound for `trait_name`, we just need to - // change the associated type. - err.span_suggestion_verbose( - assoc_name.span, - format!( - "change the associated type name to use `{suggested_name}` from \ - `{trait_name}`", - ), - suggested_name.to_string(), - Applicability::MaybeIncorrect, - ); - } else if suggest_constraining_type_param( - self.tcx(), + // change the associated item. + err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { + span: assoc_name.span, + assoc_kind: assoc_kind_str, + suggested_name, + }); + return tcx.sess.emit_err(err); + } + + let mut err = tcx.sess.create_err(err); + if suggest_constraining_type_param( + tcx, generics, &mut err, - ty_param_name, + &ty_param_name, &trait_name, None, None, @@ -237,39 +242,101 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // was also not an exact match, so we also suggest changing it. err.span_suggestion_verbose( assoc_name.span, - "and also change the associated type name", + fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, suggested_name.to_string(), Applicability::MaybeIncorrect, ); } + return err.emit(); } - return err.emit(); + return tcx.sess.emit_err(err); } } // If we still couldn't find any associated type, and only one associated type exists, // suggests using it. - - if all_candidate_names.len() == 1 { + if let [candidate_name] = all_candidate_names.as_slice() { // this should still compile, except on `#![feature(associated_type_defaults)]` // where it could suggests `type A = Self::A`, thus recursing infinitely - let applicability = if self.tcx().features().associated_type_defaults { + let applicability = if tcx.features().associated_type_defaults { Applicability::Unspecified } else { Applicability::MaybeIncorrect }; - err.span_suggestion( - assoc_name.span, - format!("`{ty_param_name}` has the following associated type"), - all_candidate_names.first().unwrap().to_string(), + err.sugg = Some(errors::AssocItemNotFoundSugg::Other { + span: assoc_name.span, applicability, - ); + ty_param_name, + assoc_kind: assoc_kind_str, + suggested_name: *candidate_name, + }); } else { - err.span_label(assoc_name.span, format!("associated type `{assoc_name}` not found")); + err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span }); } - err.emit() + tcx.sess.emit_err(err) + } + + fn complain_about_assoc_kind_mismatch( + &self, + assoc_item: &ty::AssocItem, + assoc_kind: ty::AssocKind, + ident: Ident, + span: Span, + binding: Option<&super::ConvertedBinding<'_, 'tcx>>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + + let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind + && let Some(binding) = binding + && let ConvertedBindingKind::Constraint(_) = binding.kind + { + let lo = if binding.gen_args.span_ext.is_dummy() { + ident.span + } else { + binding.gen_args.span_ext + }; + Some(lo.between(span.shrink_to_hi())) + } else { + None + }; + + // FIXME(associated_const_equality): This has quite a few false positives and negatives. + let wrap_in_braces_sugg = if let Some(binding) = binding + && let ConvertedBindingKind::Equality(term) = binding.kind + && let ty::TermKind::Ty(ty) = term.node.unpack() + && (ty.is_enum() || ty.references_error()) + && tcx.features().associated_const_equality + { + Some(errors::AssocKindMismatchWrapInBracesSugg { + lo: term.span.shrink_to_lo(), + hi: term.span.shrink_to_hi(), + }) + } else { + None + }; + + // For equality bounds, we want to blame the term (RHS) instead of the item (LHS) since + // one can argue that that's more “untuitive” to the user. + let (span, expected_because_label, expected, got) = if let Some(binding) = binding + && let ConvertedBindingKind::Equality(term) = binding.kind + { + (term.span, Some(ident.span), assoc_item.kind, assoc_kind) + } else { + (ident.span, None, assoc_kind, assoc_item.kind) + }; + + tcx.sess.emit_err(errors::AssocKindMismatch { + span, + expected: super::assoc_kind_str(expected), + got: super::assoc_kind_str(got), + expected_because_label, + assoc_kind: super::assoc_kind_str(assoc_item.kind), + def_span: tcx.def_span(assoc_item.def_id), + bound_on_assoc_const_label, + wrap_in_braces_sugg, + }) } pub(crate) fn complain_about_ambiguous_inherent_assoc_type( @@ -598,7 +665,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind( tcx, ident, - AssocKind::Type, + ty::AssocKind::Type, trait_def, ); @@ -606,7 +673,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { })) }) .flatten() - .collect::<FxHashMap<Symbol, &AssocItem>>(); + .collect::<FxHashMap<Symbol, &ty::AssocItem>>(); let mut names = names .into_iter() diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 0f06407f445..20d36a1b069 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -18,8 +18,8 @@ use crate::require_c_abi_if_c_variadic; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError, - MultiSpan, + error_code, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + FatalError, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; @@ -36,6 +36,7 @@ use rustc_middle::ty::{ }; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, BytePos, Span, DUMMY_SP}; use rustc_target::spec::abi; @@ -162,7 +163,7 @@ struct ConvertedBinding<'a, 'tcx> { #[derive(Debug)] enum ConvertedBindingKind<'a, 'tcx> { - Equality(ty::Term<'tcx>), + Equality(Spanned<ty::Term<'tcx>>), Constraint(&'a [hir::GenericBound<'a>]), } @@ -595,12 +596,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map(|binding| { let kind = match &binding.kind { hir::TypeBindingKind::Equality { term } => match term { - hir::Term::Ty(ty) => { - ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into()) - } + hir::Term::Ty(ty) => ConvertedBindingKind::Equality(respan( + ty.span, + self.ast_ty_to_ty(ty).into(), + )), hir::Term::Const(c) => { + let span = self.tcx().def_span(c.def_id); let c = Const::from_anon_const(self.tcx(), c.def_id); - ConvertedBindingKind::Equality(c.into()) + ConvertedBindingKind::Equality(respan(span, c.into())) } }, hir::TypeBindingKind::Constraint { bounds } => { @@ -1056,7 +1059,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("find_bound_for_assoc_item: predicates={:#?}", predicates); let param_name = tcx.hir().ty_param_name(ty_param_def_id); - self.one_bound_for_assoc_type( + self.one_bound_for_assoc_item( || { traits::transitive_bounds_that_define_assoc_item( tcx, @@ -1068,6 +1071,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }, param_name, Some(ty_param_def_id), + ty::AssocKind::Type, assoc_name, span, None, @@ -1076,48 +1080,45 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Checks that `bounds` contains exactly one element and reports appropriate // errors otherwise. - #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)] - fn one_bound_for_assoc_type<I>( + #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, binding), ret)] + fn one_bound_for_assoc_item<I>( &self, all_candidates: impl Fn() -> I, ty_param_name: impl Display, ty_param_def_id: Option<LocalDefId>, + assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, - is_equality: Option<ty::Term<'tcx>>, + binding: Option<&ConvertedBinding<'_, 'tcx>>, ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> where I: Iterator<Item = ty::PolyTraitRef<'tcx>>, { + let tcx = self.tcx(); + let mut matching_candidates = all_candidates().filter(|r| { - self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Type, assoc_name) - }); - let mut const_candidates = all_candidates().filter(|r| { - self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Const, assoc_name) + self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name) }); - let (mut bound, mut next_cand) = match (matching_candidates.next(), const_candidates.next()) - { - (Some(bound), _) => (bound, matching_candidates.next()), - (None, Some(bound)) => (bound, const_candidates.next()), - (None, None) => { - let reported = self.complain_about_assoc_type_not_found( - all_candidates, - &ty_param_name.to_string(), - ty_param_def_id, - assoc_name, - span, - ); - return Err(reported); - } + let Some(mut bound) = matching_candidates.next() else { + let reported = self.complain_about_assoc_item_not_found( + all_candidates, + &ty_param_name.to_string(), + ty_param_def_id, + assoc_kind, + assoc_name, + span, + binding, + ); + return Err(reported); }; debug!(?bound); // look for a candidate that is not the same as our first bound, disregarding // whether the bound is const. + let mut next_cand = matching_candidates.next(); while let Some(mut bound2) = next_cand { debug!(?bound2); - let tcx = self.tcx(); if bound2.bound_vars() != bound.bound_vars() { break; } @@ -1138,7 +1139,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg }); if unconsted_args.eq(bound2.skip_binder().args.iter()) { - next_cand = matching_candidates.next().or_else(|| const_candidates.next()); + next_cand = matching_candidates.next(); } else { break; } @@ -1147,48 +1148,53 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if let Some(bound2) = next_cand { debug!(?bound2); - let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates); - let mut err = if is_equality.is_some() { - // More specific Error Index entry. - struct_span_err!( - self.tcx().sess, - span, - E0222, - "ambiguous associated type `{}` in bounds of `{}`", - assoc_name, - ty_param_name - ) - } else { - struct_span_err!( - self.tcx().sess, - span, - E0221, - "ambiguous associated type `{}` in bounds of `{}`", - assoc_name, - ty_param_name - ) - }; - err.span_label(span, format!("ambiguous associated type `{assoc_name}`")); + let assoc_kind_str = assoc_kind_str(assoc_kind); + let ty_param_name = &ty_param_name.to_string(); + let mut err = tcx.sess.create_err(crate::errors::AmbiguousAssocItem { + span, + assoc_kind: assoc_kind_str, + assoc_name, + ty_param_name, + }); + // Provide a more specific error code index entry for equality bindings. + err.code( + if let Some(binding) = binding + && let ConvertedBindingKind::Equality(_) = binding.kind + { + error_code!(E0222) + } else { + error_code!(E0221) + }, + ); + // FIXME(#97583): Resugar equality bounds to type/const bindings. + // FIXME: Turn this into a structured, translateable & more actionable suggestion. let mut where_bounds = vec![]; - for bound in bounds { + for bound in [bound, bound2].into_iter().chain(matching_candidates) { let bound_id = bound.def_id(); - let bound_span = self - .tcx() + let bound_span = tcx .associated_items(bound_id) - .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, bound_id) - .and_then(|item| self.tcx().hir().span_if_local(item.def_id)); + .find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id) + .and_then(|item| tcx.hir().span_if_local(item.def_id)); if let Some(bound_span) = bound_span { err.span_label( bound_span, format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),), ); - if let Some(constraint) = &is_equality { - where_bounds.push(format!( - " T: {trait}::{assoc_name} = {constraint}", - trait=bound.print_only_trait_path(), - )); + if let Some(binding) = binding { + match binding.kind { + ConvertedBindingKind::Equality(term) => { + // FIXME(#97583): This isn't syntactically well-formed! + where_bounds.push(format!( + " T: {trait}::{assoc_name} = {term}", + trait = bound.print_only_trait_path(), + term = term.node, + )); + } + // FIXME: Provide a suggestion. + ConvertedBindingKind::Constraint(_bounds) => {} + } } else { err.span_suggestion_verbose( span.with_hi(assoc_name.span.lo()), @@ -1199,7 +1205,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } else { err.note(format!( - "associated type `{ty_param_name}` could derive from `{}`", + "associated {assoc_kind_str} `{assoc_name}` could derive from `{}`", bound.print_only_trait_path(), )); } @@ -1220,46 +1226,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Ok(bound) } - #[instrument(level = "debug", skip(self, all_candidates, ty_name), ret)] - fn one_bound_for_assoc_method( - &self, - all_candidates: impl Iterator<Item = ty::PolyTraitRef<'tcx>>, - ty_name: impl Display, - assoc_name: Ident, - span: Span, - ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> { - let mut matching_candidates = all_candidates.filter(|r| { - self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Fn, assoc_name) - }); - - let candidate = match matching_candidates.next() { - Some(candidate) => candidate, - None => { - return Err(self.tcx().sess.emit_err( - crate::errors::ReturnTypeNotationMissingMethod { - span, - ty_name: ty_name.to_string(), - assoc_name: assoc_name.name, - }, - )); - } - }; - - if let Some(conflicting_candidate) = matching_candidates.next() { - return Err(self.tcx().sess.emit_err( - crate::errors::ReturnTypeNotationConflictingBound { - span, - ty_name: ty_name.to_string(), - assoc_name: assoc_name.name, - first_bound: candidate.print_only_trait_path(), - second_bound: conflicting_candidate.print_only_trait_path(), - }, - )); - } - - Ok(candidate) - } - // Create a type from a path to an associated type or to an enum variant. // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C` // and item_segment is the path segment for `D`. We return a type and a def for @@ -1421,7 +1387,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Err(guar); }; - self.one_bound_for_assoc_type( + self.one_bound_for_assoc_item( || { traits::supertraits( tcx, @@ -1430,6 +1396,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }, kw::SelfUpper, None, + ty::AssocKind::Type, assoc_ident, span, None, @@ -1510,15 +1477,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; let trait_did = bound.def_id(); - let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did) - else { - // Assume that if it's not matched, there must be a const defined with the same name - // but it was used in a type position. - let msg = format!("found associated const `{assoc_ident}` when type was expected"); - let guar = tcx.sess.struct_span_err(span, msg).emit(); - return Err(guar); - }; - + let assoc_ty_did = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap(); let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound); if let Some(variant_def_id) = variant_resolution { @@ -1731,8 +1690,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let tcx = self.tcx(); let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block); - // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead - // of calling `find_by_name_and_kind`. + // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` + // instead of calling `filter_by_name_and_kind` which would needlessly normalize the + // `ident` again and again. let item = tcx.associated_items(scope).in_definition_order().find(|i| { i.kind.namespace() == Namespace::TypeNS && i.ident(tcx).normalize_to_macros_2_0() == ident @@ -2858,3 +2818,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Some(r) } } + +fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { + match kind { + ty::AssocKind::Fn => "function", + ty::AssocKind::Const => "constant", + ty::AssocKind::Type => "type", + } +} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index dd83b5b6f2c..a4772293697 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -6,10 +6,122 @@ use rustc_errors::{ MultiSpan, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_middle::ty::{self, print::TraitRefPrintOnlyTraitPath, Ty}; +use rustc_middle::ty::Ty; use rustc_span::{symbol::Ident, Span, Symbol}; #[derive(Diagnostic)] +#[diag(hir_analysis_ambiguous_assoc_item)] +pub struct AmbiguousAssocItem<'a> { + #[primary_span] + #[label] + pub span: Span, + pub assoc_kind: &'static str, + pub assoc_name: Ident, + pub ty_param_name: &'a str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_assoc_kind_mismatch)] +pub struct AssocKindMismatch { + #[primary_span] + #[label] + pub span: Span, + pub expected: &'static str, + pub got: &'static str, + #[label(hir_analysis_expected_because_label)] + pub expected_because_label: Option<Span>, + pub assoc_kind: &'static str, + #[note] + pub def_span: Span, + #[label(hir_analysis_bound_on_assoc_const_label)] + pub bound_on_assoc_const_label: Option<Span>, + #[subdiagnostic] + pub wrap_in_braces_sugg: Option<AssocKindMismatchWrapInBracesSugg>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg, + applicability = "maybe-incorrect" +)] +pub struct AssocKindMismatchWrapInBracesSugg { + #[suggestion_part(code = "{{ ")] + pub lo: Span, + #[suggestion_part(code = " }}")] + pub hi: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_assoc_item_not_found, code = "E0220")] +pub struct AssocItemNotFound<'a> { + #[primary_span] + pub span: Span, + pub assoc_name: Ident, + pub assoc_kind: &'static str, + pub ty_param_name: &'a str, + #[subdiagnostic] + pub label: Option<AssocItemNotFoundLabel<'a>>, + #[subdiagnostic] + pub sugg: Option<AssocItemNotFoundSugg<'a>>, +} + +#[derive(Subdiagnostic)] +pub enum AssocItemNotFoundLabel<'a> { + #[label(hir_analysis_assoc_item_not_found_label)] + NotFound { + #[primary_span] + span: Span, + }, + #[label(hir_analysis_assoc_item_not_found_found_in_other_trait_label)] + FoundInOtherTrait { + #[primary_span] + span: Span, + assoc_kind: &'static str, + trait_name: &'a str, + suggested_name: Symbol, + identically_named: bool, + }, +} + +#[derive(Subdiagnostic)] + +pub enum AssocItemNotFoundSugg<'a> { + #[suggestion( + hir_analysis_assoc_item_not_found_similar_sugg, + code = "{suggested_name}", + applicability = "maybe-incorrect" + )] + Similar { + #[primary_span] + span: Span, + assoc_kind: &'static str, + suggested_name: Symbol, + }, + #[suggestion( + hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg, + code = "{suggested_name}", + style = "verbose", + applicability = "maybe-incorrect" + )] + SimilarInOtherTrait { + #[primary_span] + span: Span, + assoc_kind: &'static str, + suggested_name: Symbol, + }, + #[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")] + Other { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + ty_param_name: &'a str, + assoc_kind: &'static str, + suggested_name: Symbol, + }, +} + +#[derive(Diagnostic)] #[diag(hir_analysis_unrecognized_atomic_operation, code = "E0092")] pub struct UnrecognizedAtomicOperation<'a> { #[primary_span] @@ -538,27 +650,6 @@ pub(crate) struct ReturnTypeNotationEqualityBound { } #[derive(Diagnostic)] -#[diag(hir_analysis_return_type_notation_missing_method)] -pub(crate) struct ReturnTypeNotationMissingMethod { - #[primary_span] - pub span: Span, - pub ty_name: String, - pub assoc_name: Symbol, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_return_type_notation_conflicting_bound)] -#[note] -pub(crate) struct ReturnTypeNotationConflictingBound<'tcx> { - #[primary_span] - pub span: Span, - pub ty_name: String, - pub assoc_name: Symbol, - pub first_bound: ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - pub second_bound: ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, -} - -#[derive(Diagnostic)] #[diag(hir_analysis_placeholder_not_allowed_item_signatures, code = "E0121")] pub(crate) struct PlaceholderNotAllowedItemSignatures { #[primary_span] @@ -955,15 +1046,6 @@ pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> { } #[derive(Diagnostic)] -#[diag(hir_analysis_assoc_bound_on_const)] -#[note] -pub struct AssocBoundOnConst { - #[primary_span] - pub span: Span, - pub descr: &'static str, -} - -#[derive(Diagnostic)] #[diag(hir_analysis_inherent_ty_outside, code = "E0390")] #[help] pub struct InherentTyOutside { diff --git a/tests/ui/associated-consts/assoc-const-eq-ambiguity.rs b/tests/ui/associated-consts/assoc-const-eq-ambiguity.rs new file mode 100644 index 00000000000..ac085864ff0 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-ambiguity.rs @@ -0,0 +1,19 @@ +// We used to say "ambiguous associated type" on ambiguous associated consts. +// Ensure that we now use the correct label. + +#![feature(associated_const_equality)] + +trait Trait0: Parent0<i32> + Parent0<u32> {} +trait Parent0<T> { const K: (); } + +fn take0(_: impl Trait0<K = { () }>) {} +//~^ ERROR ambiguous associated constant `K` in bounds of `Trait0` + +trait Trait1: Parent1 + Parent2 {} +trait Parent1 { const C: i32; } +trait Parent2 { const C: &'static str; } + +fn take1(_: impl Trait1<C = "?">) {} +//~^ ERROR ambiguous associated constant `C` in bounds of `Trait1` + +fn main() {} diff --git a/tests/ui/associated-consts/assoc-const-eq-ambiguity.stderr b/tests/ui/associated-consts/assoc-const-eq-ambiguity.stderr new file mode 100644 index 00000000000..ba3a8701316 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-ambiguity.stderr @@ -0,0 +1,38 @@ +error[E0222]: ambiguous associated constant `K` in bounds of `Trait0` + --> $DIR/assoc-const-eq-ambiguity.rs:9:25 + | +LL | trait Parent0<T> { const K: (); } + | ----------- + | | + | ambiguous `K` from `Parent0<u32>` + | ambiguous `K` from `Parent0<i32>` +LL | +LL | fn take0(_: impl Trait0<K = { () }>) {} + | ^^^^^^^^^^ ambiguous associated constant `K` + | + = help: consider introducing a new type parameter `T` and adding `where` constraints: + where + T: Trait0, + T: Parent0<u32>::K = { () }, + T: Parent0<i32>::K = { () } + +error[E0222]: ambiguous associated constant `C` in bounds of `Trait1` + --> $DIR/assoc-const-eq-ambiguity.rs:16:25 + | +LL | trait Parent1 { const C: i32; } + | ------------ ambiguous `C` from `Parent1` +LL | trait Parent2 { const C: &'static str; } + | --------------------- ambiguous `C` from `Parent2` +LL | +LL | fn take1(_: impl Trait1<C = "?">) {} + | ^^^^^^^ ambiguous associated constant `C` + | + = help: consider introducing a new type parameter `T` and adding `where` constraints: + where + T: Trait1, + T: Parent2::C = "?", + T: Parent1::C = "?" + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0222`. diff --git a/tests/ui/associated-consts/assoc-const-eq-missing.rs b/tests/ui/associated-consts/assoc-const-eq-missing.rs index 5e029a12df2..f384927e4a3 100644 --- a/tests/ui/associated-consts/assoc-const-eq-missing.rs +++ b/tests/ui/associated-consts/assoc-const-eq-missing.rs @@ -11,13 +11,12 @@ impl Foo for Bar { const N: usize = 3; } - -fn foo1<F: Foo<Z=3>>() {} -//~^ ERROR associated type -fn foo2<F: Foo<Z=usize>>() {} -//~^ ERROR associated type -fn foo3<F: Foo<Z=5>>() {} -//~^ ERROR associated type +fn foo1<F: Foo<Z = 3>>() {} +//~^ ERROR associated constant `Z` not found for `Foo` +fn foo2<F: Foo<Z = usize>>() {} +//~^ ERROR associated type `Z` not found for `Foo` +fn foo3<F: Foo<Z = 5>>() {} +//~^ ERROR associated constant `Z` not found for `Foo` fn main() { foo1::<Bar>(); diff --git a/tests/ui/associated-consts/assoc-const-eq-missing.stderr b/tests/ui/associated-consts/assoc-const-eq-missing.stderr index b4bd6456c85..318c85dcfd6 100644 --- a/tests/ui/associated-consts/assoc-const-eq-missing.stderr +++ b/tests/ui/associated-consts/assoc-const-eq-missing.stderr @@ -1,20 +1,20 @@ -error[E0220]: associated type `Z` not found for `Foo` - --> $DIR/assoc-const-eq-missing.rs:15:16 +error[E0220]: associated constant `Z` not found for `Foo` + --> $DIR/assoc-const-eq-missing.rs:14:16 | -LL | fn foo1<F: Foo<Z=3>>() {} - | ^ associated type `Z` not found +LL | fn foo1<F: Foo<Z = 3>>() {} + | ^ help: there is an associated constant with a similar name: `N` error[E0220]: associated type `Z` not found for `Foo` - --> $DIR/assoc-const-eq-missing.rs:17:16 + --> $DIR/assoc-const-eq-missing.rs:16:16 | -LL | fn foo2<F: Foo<Z=usize>>() {} +LL | fn foo2<F: Foo<Z = usize>>() {} | ^ associated type `Z` not found -error[E0220]: associated type `Z` not found for `Foo` - --> $DIR/assoc-const-eq-missing.rs:19:16 +error[E0220]: associated constant `Z` not found for `Foo` + --> $DIR/assoc-const-eq-missing.rs:18:16 | -LL | fn foo3<F: Foo<Z=5>>() {} - | ^ associated type `Z` not found +LL | fn foo3<F: Foo<Z = 5>>() {} + | ^ help: there is an associated constant with a similar name: `N` error: aborting due to 3 previous errors diff --git a/tests/ui/associated-consts/assoc-const-eq-ty-alias-noninteracting.rs b/tests/ui/associated-consts/assoc-const-eq-ty-alias-noninteracting.rs new file mode 100644 index 00000000000..de9008bfcf9 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-ty-alias-noninteracting.rs @@ -0,0 +1,21 @@ +// Regression test for issue #112560. +// Respect the fact that (associated) types and constants live in different namespaces and +// therefore equality bounds involving identically named associated items don't conflict if +// their kind (type vs. const) differs. + +// FIXME(fmease): Extend this test to cover supertraits again +// once #118040 is fixed. See initial version of PR #118360. + +// check-pass + +#![feature(associated_const_equality)] + +trait Trait { + type N; + + const N: usize; +} + +fn take(_: impl Trait<N = 0, N = ()>) {} + +fn main() {} diff --git a/tests/ui/associated-consts/assoc-const-ty-mismatch.rs b/tests/ui/associated-consts/assoc-const-ty-mismatch.rs index c5d78469e95..7211637659b 100644 --- a/tests/ui/associated-consts/assoc-const-ty-mismatch.rs +++ b/tests/ui/associated-consts/assoc-const-ty-mismatch.rs @@ -2,30 +2,30 @@ #![allow(unused)] pub trait Foo { - const N: usize; + const N: usize; } pub trait FooTy { - type T; + type T; } pub struct Bar; impl Foo for Bar { - const N: usize = 3; + const N: usize = 3; } impl FooTy for Bar { - type T = usize; + type T = usize; } -fn foo<F: Foo<N=usize>>() {} -//~^ ERROR expected associated constant bound, found type -fn foo2<F: FooTy<T=3usize>>() {} -//~^ ERROR expected associated type bound, found constant +fn foo<F: Foo<N = usize>>() {} +//~^ ERROR expected constant, found type +fn foo2<F: FooTy<T = 3usize>>() {} +//~^ ERROR expected type, found constant fn main() { - foo::<Bar>(); - foo2::<Bar>(); + foo::<Bar>(); + foo2::<Bar>(); } diff --git a/tests/ui/associated-consts/assoc-const-ty-mismatch.stderr b/tests/ui/associated-consts/assoc-const-ty-mismatch.stderr index 11198729e38..b844cfc4ae4 100644 --- a/tests/ui/associated-consts/assoc-const-ty-mismatch.stderr +++ b/tests/ui/associated-consts/assoc-const-ty-mismatch.stderr @@ -1,26 +1,30 @@ -error: expected associated constant bound, found type - --> $DIR/assoc-const-ty-mismatch.rs:23:15 +error: expected constant, found type + --> $DIR/assoc-const-ty-mismatch.rs:23:19 | -LL | fn foo<F: Foo<N=usize>>() {} - | ^^^^^^^ +LL | fn foo<F: Foo<N = usize>>() {} + | - ^^^^^ unexpected type + | | + | expected a constant because of this associated constant | -note: associated constant defined here - --> $DIR/assoc-const-ty-mismatch.rs:5:3 +note: the associated constant is defined here + --> $DIR/assoc-const-ty-mismatch.rs:5:5 | -LL | const N: usize; - | ^^^^^^^^^^^^^^ +LL | const N: usize; + | ^^^^^^^^^^^^^^ -error: expected associated type bound, found constant - --> $DIR/assoc-const-ty-mismatch.rs:25:18 +error: expected type, found constant + --> $DIR/assoc-const-ty-mismatch.rs:25:22 | -LL | fn foo2<F: FooTy<T=3usize>>() {} - | ^^^^^^^^ +LL | fn foo2<F: FooTy<T = 3usize>>() {} + | - ^^^^^^ unexpected constant + | | + | expected a type because of this associated type | -note: associated type defined here - --> $DIR/assoc-const-ty-mismatch.rs:9:3 +note: the associated type is defined here + --> $DIR/assoc-const-ty-mismatch.rs:9:5 | -LL | type T; - | ^^^^^^ +LL | type T; + | ^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/associated-consts/shadowed-const.rs b/tests/ui/associated-consts/shadowed-const.rs index cfdb391d39d..d9b565742d4 100644 --- a/tests/ui/associated-consts/shadowed-const.rs +++ b/tests/ui/associated-consts/shadowed-const.rs @@ -17,7 +17,7 @@ trait Baz2: Foo { trait Baz3 { const BAR: usize; const QUX: Self::BAR; - //~^ ERROR found associated const + //~^ ERROR expected type, found constant } fn main() {} diff --git a/tests/ui/associated-consts/shadowed-const.stderr b/tests/ui/associated-consts/shadowed-const.stderr index a01a9ae561f..2db645b3c96 100644 --- a/tests/ui/associated-consts/shadowed-const.stderr +++ b/tests/ui/associated-consts/shadowed-const.stderr @@ -1,8 +1,14 @@ -error: found associated const `BAR` when type was expected - --> $DIR/shadowed-const.rs:19:14 +error: expected type, found constant + --> $DIR/shadowed-const.rs:19:20 | LL | const QUX: Self::BAR; - | ^^^^^^^^^ + | ^^^ unexpected constant + | +note: the associated constant is defined here + --> $DIR/shadowed-const.rs:18:3 + | +LL | const BAR: usize; + | ^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/associated-type-bounds/consts.rs b/tests/ui/associated-type-bounds/consts.rs index 9b95b1b52c0..8f90c36ed45 100644 --- a/tests/ui/associated-type-bounds/consts.rs +++ b/tests/ui/associated-type-bounds/consts.rs @@ -1,7 +1,7 @@ #![feature(associated_type_bounds)] pub fn accept(_: impl Trait<K: Copy>) {} -//~^ ERROR expected associated type, found associated constant +//~^ ERROR expected type, found constant pub trait Trait { const K: i32; diff --git a/tests/ui/associated-type-bounds/consts.stderr b/tests/ui/associated-type-bounds/consts.stderr index eef24c8827b..7f9fe5e500a 100644 --- a/tests/ui/associated-type-bounds/consts.stderr +++ b/tests/ui/associated-type-bounds/consts.stderr @@ -1,10 +1,16 @@ -error: expected associated type, found associated constant +error: expected type, found constant --> $DIR/consts.rs:3:29 | LL | pub fn accept(_: impl Trait<K: Copy>) {} - | ^ + | ^------ bounds are not allowed on associated constants + | | + | unexpected constant | - = note: trait bounds not allowed on associated constant +note: the associated constant is defined here + --> $DIR/consts.rs:7:5 + | +LL | const K: i32; + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/associated-type-bounds/issue-99828.rs b/tests/ui/associated-type-bounds/issue-99828.rs index 7b711283f5b..67ba50f3cbc 100644 --- a/tests/ui/associated-type-bounds/issue-99828.rs +++ b/tests/ui/associated-type-bounds/issue-99828.rs @@ -1,5 +1,5 @@ fn get_iter(vec: &[i32]) -> impl Iterator<Item = {}> + '_ { - //~^ ERROR expected associated type bound, found constant + //~^ ERROR expected type, found constant //~| ERROR associated const equality is incomplete vec.iter() } diff --git a/tests/ui/associated-type-bounds/issue-99828.stderr b/tests/ui/associated-type-bounds/issue-99828.stderr index dc93c47dace..8813baf84de 100644 --- a/tests/ui/associated-type-bounds/issue-99828.stderr +++ b/tests/ui/associated-type-bounds/issue-99828.stderr @@ -7,13 +7,15 @@ LL | fn get_iter(vec: &[i32]) -> impl Iterator<Item = {}> + '_ { = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable -error: expected associated type bound, found constant - --> $DIR/issue-99828.rs:1:43 +error: expected type, found constant + --> $DIR/issue-99828.rs:1:50 | LL | fn get_iter(vec: &[i32]) -> impl Iterator<Item = {}> + '_ { - | ^^^^^^^^^ + | ---- ^^ unexpected constant + | | + | expected a type because of this associated type | -note: associated type defined here +note: the associated type is defined here --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error: aborting due to 2 previous errors diff --git a/tests/ui/associated-type-bounds/return-type-notation/missing.rs b/tests/ui/associated-type-bounds/return-type-notation/missing.rs index 0679b96f6c5..e6270ec3166 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/missing.rs +++ b/tests/ui/associated-type-bounds/return-type-notation/missing.rs @@ -8,6 +8,6 @@ trait Trait { } fn bar<T: Trait<methid(): Send>>() {} -//~^ ERROR cannot find associated function `methid` for `Trait` +//~^ ERROR associated function `methid` not found for `Trait` fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/missing.stderr b/tests/ui/associated-type-bounds/return-type-notation/missing.stderr index 3ca5e66866d..db9cb9f49a3 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/missing.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/missing.stderr @@ -7,11 +7,12 @@ LL | #![feature(return_type_notation)] = note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information = note: `#[warn(incomplete_features)]` on by default -error: cannot find associated function `methid` for `Trait` +error[E0220]: associated function `methid` not found for `Trait` --> $DIR/missing.rs:10:17 | LL | fn bar<T: Trait<methid(): Send>>() {} - | ^^^^^^^^^^^^^^ + | ^^^^^^ help: there is an associated function with a similar name: `method` error: aborting due to 1 previous error; 1 warning emitted +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/async-await/return-type-notation/super-method-bound-ambig.rs b/tests/ui/async-await/return-type-notation/super-method-bound-ambig.rs index 891b30638ee..73c08531599 100644 --- a/tests/ui/async-await/return-type-notation/super-method-bound-ambig.rs +++ b/tests/ui/async-await/return-type-notation/super-method-bound-ambig.rs @@ -23,7 +23,7 @@ impl Foo for () {} fn test<T>() where T: Foo<test(): Send>, - //~^ ERROR ambiguous associated function `test` for `Foo` + //~^ ERROR ambiguous associated function `test` in bounds of `Foo` { } diff --git a/tests/ui/async-await/return-type-notation/super-method-bound-ambig.stderr b/tests/ui/async-await/return-type-notation/super-method-bound-ambig.stderr index 7eaf3b82d98..4003aad6d03 100644 --- a/tests/ui/async-await/return-type-notation/super-method-bound-ambig.stderr +++ b/tests/ui/async-await/return-type-notation/super-method-bound-ambig.stderr @@ -7,13 +7,18 @@ LL | #![feature(return_type_notation)] = note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information = note: `#[warn(incomplete_features)]` on by default -error: ambiguous associated function `test` for `Foo` +error[E0221]: ambiguous associated function `test` in bounds of `Foo` --> $DIR/super-method-bound-ambig.rs:25:12 | +LL | async fn test(); + | ---------------- ambiguous `test` from `for<'a> Super1<'a>` +... +LL | async fn test(); + | ---------------- ambiguous `test` from `Super2` +... LL | T: Foo<test(): Send>, - | ^^^^^^^^^^^^ - | - = note: `test` is declared in two supertraits: `Super2` and `Super1<'a>` + | ^^^^^^^^^^^^ ambiguous associated function `test` error: aborting due to 1 previous error; 1 warning emitted +For more information about this error, try `rustc --explain E0221`. diff --git a/tests/ui/const-generics/assoc_const_eq_diagnostic.rs b/tests/ui/const-generics/assoc_const_eq_diagnostic.rs index bf8202ac152..d51696f9ebd 100644 --- a/tests/ui/const-generics/assoc_const_eq_diagnostic.rs +++ b/tests/ui/const-generics/assoc_const_eq_diagnostic.rs @@ -9,9 +9,9 @@ pub trait Parse { } pub trait CoolStuff: Parse<MODE = Mode::Cool> {} -//~^ ERROR expected associated constant bound -//~| ERROR expected associated constant bound -//~| ERROR expected type +//~^ ERROR expected type, found variant +//~| ERROR expected constant, found type +//~| ERROR expected constant, found type fn no_help() -> Mode::Cool {} //~^ ERROR expected type, found variant diff --git a/tests/ui/const-generics/assoc_const_eq_diagnostic.stderr b/tests/ui/const-generics/assoc_const_eq_diagnostic.stderr index 6d5cd45479e..3d724bb1642 100644 --- a/tests/ui/const-generics/assoc_const_eq_diagnostic.stderr +++ b/tests/ui/const-generics/assoc_const_eq_diagnostic.stderr @@ -16,30 +16,42 @@ LL | fn no_help() -> Mode::Cool {} | not a type | help: try using the variant's enum: `Mode` -error: expected associated constant bound, found type - --> $DIR/assoc_const_eq_diagnostic.rs:11:28 +error: expected constant, found type + --> $DIR/assoc_const_eq_diagnostic.rs:11:35 | LL | pub trait CoolStuff: Parse<MODE = Mode::Cool> {} - | ^^^^^^^^^^^^^^^^^ help: if equating a const, try wrapping with braces: `MODE = { const }` + | ---- ^^^^^^^^^^ unexpected type + | | + | expected a constant because of this associated constant | -note: associated constant defined here +note: the associated constant is defined here --> $DIR/assoc_const_eq_diagnostic.rs:8:5 | LL | const MODE: Mode; | ^^^^^^^^^^^^^^^^ +help: consider adding braces here + | +LL | pub trait CoolStuff: Parse<MODE = { Mode::Cool }> {} + | + + -error: expected associated constant bound, found type - --> $DIR/assoc_const_eq_diagnostic.rs:11:28 +error: expected constant, found type + --> $DIR/assoc_const_eq_diagnostic.rs:11:35 | LL | pub trait CoolStuff: Parse<MODE = Mode::Cool> {} - | ^^^^^^^^^^^^^^^^^ help: if equating a const, try wrapping with braces: `MODE = { const }` + | ---- ^^^^^^^^^^ unexpected type + | | + | expected a constant because of this associated constant | -note: associated constant defined here +note: the associated constant is defined here --> $DIR/assoc_const_eq_diagnostic.rs:8:5 | LL | const MODE: Mode; | ^^^^^^^^^^^^^^^^ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding braces here + | +LL | pub trait CoolStuff: Parse<MODE = { Mode::Cool }> {} + | + + error: aborting due to 4 previous errors diff --git a/tests/ui/error-codes/E0221.stderr b/tests/ui/error-codes/E0221.stderr index e600acf7834..07e7485b67e 100644 --- a/tests/ui/error-codes/E0221.stderr +++ b/tests/ui/error-codes/E0221.stderr @@ -28,7 +28,7 @@ LL | fn test() { LL | let _: Self::Err; | ^^^^^^^^^ ambiguous associated type `Err` | - = note: associated type `Self` could derive from `FromStr` + = note: associated type `Err` could derive from `FromStr` help: use fully-qualified syntax to disambiguate | LL | let _: <Self as My>::Err; diff --git a/tests/ui/feature-gates/feature-gate-return_type_notation.cfg.stderr b/tests/ui/feature-gates/feature-gate-return_type_notation.cfg.stderr index 1bdb2574ead..a15b01618f5 100644 --- a/tests/ui/feature-gates/feature-gate-return_type_notation.cfg.stderr +++ b/tests/ui/feature-gates/feature-gate-return_type_notation.cfg.stderr @@ -15,13 +15,18 @@ LL | fn foo<T: Trait<m(): Send>>() {} | | | help: remove these parentheses -error[E0220]: associated type `m` not found for `Trait` +error: expected type, found function --> $DIR/feature-gate-return_type_notation.rs:14:17 | LL | fn foo<T: Trait<m(): Send>>() {} - | ^ associated type `m` not found + | ^ unexpected function + | +note: the associated function is defined here + --> $DIR/feature-gate-return_type_notation.rs:10:5 + | +LL | async fn m(); + | ^^^^^^^^^^^^^ error: aborting due to 3 previous errors -Some errors have detailed explanations: E0220, E0658. -For more information about an error, try `rustc --explain E0220`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-return_type_notation.rs b/tests/ui/feature-gates/feature-gate-return_type_notation.rs index 86e2c48e188..60ac9f8d4f1 100644 --- a/tests/ui/feature-gates/feature-gate-return_type_notation.rs +++ b/tests/ui/feature-gates/feature-gate-return_type_notation.rs @@ -14,7 +14,7 @@ trait Trait { fn foo<T: Trait<m(): Send>>() {} //[cfg]~^ ERROR return type notation is experimental //[cfg]~| ERROR parenthesized generic arguments cannot be used in associated type constraints -//[cfg]~| ERROR associated type `m` not found for `Trait` +//[cfg]~| ERROR expected type, found function //[no]~^^^^ WARN return type notation is experimental //[no]~| WARN unstable syntax can change at any point in the future, causing a hard error! | 
