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 4c0addc80a) & 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.
This commit is contained in:
bors 2023-12-08 09:30:52 +00:00
commit 5ea62560f2
27 changed files with 598 additions and 416 deletions

View File

@ -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 = hir_analysis_ambiguous_lifetime_bound =
ambiguous lifetime bound, explicit lifetime bound required ambiguous lifetime bound, explicit lifetime bound required
hir_analysis_assoc_bound_on_const = expected associated type, found {$descr} hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}`
.note = trait bounds not allowed on {$descr}
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 = hir_analysis_assoc_type_binding_not_allowed =
associated type bindings are not allowed here 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_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 = hir_analysis_return_type_notation_equality_bound =
return type notation is not allowed to use type equality 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 return type notation is not allowed for functions that have type parameters
.label = type parameter declared here .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 = hir_analysis_return_type_notation_on_non_rpitit =
return type notation used on function that is not `async` and does not return `impl Trait` 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 .note = function returns `{$ty}`, which is not compatible with associated type return bounds

View File

@ -3,8 +3,7 @@ use rustc_errors::struct_span_err;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_lint_defs::Applicability; use rustc_middle::ty::{self as ty, Ty};
use rustc_middle::ty::{self as ty, Ty, TypeVisitableExt};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span}; use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits; use rustc_trait_selection::traits;
@ -256,64 +255,49 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
let tcx = self.tcx(); let tcx = self.tcx();
let return_type_notation = let assoc_kind =
binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation; if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
ty::AssocKind::Fn
let candidate = if return_type_notation { } else if let ConvertedBindingKind::Equality(term) = binding.kind
if self.trait_defines_associated_item_named( && let ty::TermKind::Const(_) = term.node.unpack()
trait_ref.def_id(), {
ty::AssocKind::Fn, ty::AssocKind::Const
binding.item_name,
) {
trait_ref
} else { } else {
self.one_bound_for_assoc_method( ty::AssocKind::Type
traits::supertraits(tcx, trait_ref), };
trait_ref.print_only_trait_path(),
binding.item_name, let candidate = if self.trait_defines_associated_item_named(
path_span,
)?
}
} else if self.trait_defines_associated_item_named(
trait_ref.def_id(), trait_ref.def_id(),
ty::AssocKind::Type, assoc_kind,
binding.item_name, 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 trait_ref
} else { } else {
// Otherwise, we have to walk through the supertraits to find // Otherwise, we have to walk through the supertraits to find
// those that do. // one that does define it.
self.one_bound_for_assoc_type( self.one_bound_for_assoc_item(
|| traits::supertraits(tcx, trait_ref), || traits::supertraits(tcx, trait_ref),
trait_ref.skip_binder().print_only_trait_name(), trait_ref.skip_binder().print_only_trait_name(),
None, None,
assoc_kind,
binding.item_name, binding.item_name,
path_span, path_span,
match binding.kind { Some(&binding),
ConvertedBindingKind::Equality(term) => Some(term),
_ => None,
},
)? )?
}; };
let (assoc_ident, def_scope) = let (assoc_ident, def_scope) =
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id); 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 // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
// of calling `filter_by_name_and_kind`. // instead of calling `filter_by_name_and_kind` which would needlessly normalize the
let find_item_of_kind = |kind| { // `assoc_ident` again and again.
tcx.associated_items(candidate.def_id()) let assoc_item = tcx
.filter_by_name_unhygienic(assoc_ident.name) .associated_items(candidate.def_id())
.find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) .filter_by_name_unhygienic(assoc_ident.name)
}; .find(|i| i.kind == assoc_kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
let assoc_item = if return_type_notation { .expect("missing associated item");
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");
if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) { if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
tcx.sess tcx.sess
@ -340,7 +324,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
.or_insert(binding.span); .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; let mut emitted_bad_param_err = false;
// If we have an method return type bound, then we need to substitute // If we have an method return type bound, then we need to substitute
// the method's early bound params with suitable late-bound params. // 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 = let late_bound_in_trait_ref =
tcx.collect_constrained_late_bound_regions(&projection_ty); tcx.collect_constrained_late_bound_regions(&projection_ty);
let late_bound_in_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_trait_ref);
debug!(?late_bound_in_ty); 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 { 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( return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span }, crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
)); ));
} }
ConvertedBindingKind::Equality(mut term) => { ConvertedBindingKind::Equality(term) => {
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to // "Desugar" a constraint like `T: Iterator<Item = u32>` this to
// the "projection predicate" for: // the "projection predicate" for:
// //
// `<T as Iterator>::Item = u32` // `<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( bounds.push_projection_bound(
tcx, tcx,
projection_ty projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }), projection_ty,
term: term.node,
}),
binding.span, binding.span,
); );
} }
ConvertedBindingKind::Constraint(ast_bounds) => { 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 // "Desugar" a constraint like `T: Iterator<Item: Debug>` to
// //
// `<T as Iterator>::Item: Debug` // `<T as Iterator>::Item: Debug`

View File

@ -1,15 +1,16 @@
use crate::astconv::AstConv; use crate::astconv::{AstConv, ConvertedBindingKind};
use crate::errors::{ use crate::errors::{
AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
ParenthesizedFnTraitExpansion, ParenthesizedFnTraitExpansion,
}; };
use crate::fluent_generated as fluent;
use crate::traits::error_reporting::report_object_safety_error; use crate::traits::error_reporting::report_object_safety_error;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::traits::FulfillmentError; 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_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{sym, Ident}; 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, &self,
all_candidates: impl Fn() -> I, all_candidates: impl Fn() -> I,
ty_param_name: &str, ty_param_name: &str,
ty_param_def_id: Option<LocalDefId>, ty_param_def_id: Option<LocalDefId>,
assoc_kind: ty::AssocKind,
assoc_name: Ident, assoc_name: Ident,
span: Span, span: Span,
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
) -> ErrorGuaranteed ) -> ErrorGuaranteed
where where
I: Iterator<Item = ty::PolyTraitRef<'tcx>>, 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 // 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. // valid span, so we point at the whole path segment instead.
let is_dummy = assoc_name.span == DUMMY_SP; let is_dummy = assoc_name.span == DUMMY_SP;
let mut err = struct_span_err!( let mut err = errors::AssocItemNotFound {
self.tcx().sess, span: if is_dummy { span } else { assoc_name.span },
if is_dummy { span } else { assoc_name.span },
E0220,
"associated type `{}` not found for `{}`",
assoc_name, assoc_name,
ty_param_name assoc_kind: assoc_kind_str,
); ty_param_name,
label: None,
sugg: None,
};
if is_dummy { if is_dummy {
err.span_label(span, format!("associated type `{assoc_name}` not found")); err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span });
return err.emit(); return tcx.sess.emit_err(err);
} }
let all_candidate_names: Vec<_> = all_candidates() 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| { .filter_map(|item| {
if !item.is_impl_trait_in_trait() && item.kind == ty::AssocKind::Type { (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name)
Some(item.name)
} else {
None
}
}) })
.collect(); .collect();
if let Some(suggested_name) = if let Some(suggested_name) =
find_best_match_for_name(&all_candidate_names, assoc_name.name, None) find_best_match_for_name(&all_candidate_names, assoc_name.name, None)
{ {
err.span_suggestion( err.sugg = Some(errors::AssocItemNotFoundSugg::Similar {
assoc_name.span, span: assoc_name.span,
"there is an associated type with a similar name", assoc_kind: assoc_kind_str,
suggested_name, suggested_name,
Applicability::MaybeIncorrect, });
); return tcx.sess.emit_err(err);
return err.emit();
} }
// If we didn't find a good item in the supertraits (or couldn't get // 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 // the supertraits), like in ItemCtxt, then look more generally from
// all visible traits. If there's one clear winner, just suggest that. // all visible traits. If there's one clear winner, just suggest that.
let visible_traits: Vec<_> = self let visible_traits: Vec<_> = tcx
.tcx()
.all_traits() .all_traits()
.filter(|trait_def_id| { .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(); let def_id = self.item_def_id();
viz.is_accessible_from(def_id, self.tcx()) viz.is_accessible_from(def_id, tcx)
}) })
.collect(); .collect();
let wider_candidate_names: Vec<_> = visible_traits let wider_candidate_names: Vec<_> = visible_traits
.iter() .iter()
.flat_map(|trait_def_id| { .flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order())
self.tcx().associated_items(*trait_def_id).in_definition_order()
})
.filter_map(|item| { .filter_map(|item| {
if !item.is_impl_trait_in_trait() && item.kind == ty::AssocKind::Type { (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name)
Some(item.name)
} else {
None
}
}) })
.collect(); .collect();
@ -182,52 +188,51 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
{ {
if let [best_trait] = visible_traits if let [best_trait] = visible_traits
.iter() .iter()
.copied()
.filter(|trait_def_id| { .filter(|trait_def_id| {
self.tcx() tcx.associated_items(trait_def_id)
.associated_items(*trait_def_id)
.filter_by_name_unhygienic(suggested_name) .filter_by_name_unhygienic(suggested_name)
.any(|item| item.kind == ty::AssocKind::Type) .any(|item| item.kind == assoc_kind)
}) })
.collect::<Vec<_>>()[..] .collect::<Vec<_>>()[..]
{ {
let trait_name = self.tcx().def_path_str(*best_trait); let trait_name = tcx.def_path_str(best_trait);
let an = if suggested_name != assoc_name.name { "a similarly named" } else { "an" }; err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait {
err.span_label( span: assoc_name.span,
assoc_name.span, assoc_kind: assoc_kind_str,
format!( trait_name: &trait_name,
"there is {an} associated type `{suggested_name}` in the \ suggested_name,
trait `{trait_name}`", identically_named: suggested_name == assoc_name.name,
), });
); let hir = tcx.hir();
let hir = self.tcx().hir();
if let Some(def_id) = ty_param_def_id 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) && let Some(generics) = hir.get_generics(parent.def_id)
{ {
if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any(
|b| match b { |b| match b {
hir::GenericBound::Trait(t, ..) => { 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, _ => false,
}, },
) { ) {
// The type param already has a bound for `trait_name`, we just need to // The type param already has a bound for `trait_name`, we just need to
// change the associated type. // change the associated item.
err.span_suggestion_verbose( err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
assoc_name.span, span: assoc_name.span,
format!( assoc_kind: assoc_kind_str,
"change the associated type name to use `{suggested_name}` from \ suggested_name,
`{trait_name}`", });
), return tcx.sess.emit_err(err);
suggested_name.to_string(), }
Applicability::MaybeIncorrect,
); let mut err = tcx.sess.create_err(err);
} else if suggest_constraining_type_param( if suggest_constraining_type_param(
self.tcx(), tcx,
generics, generics,
&mut err, &mut err,
ty_param_name, &ty_param_name,
&trait_name, &trait_name,
None, None,
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. // was also not an exact match, so we also suggest changing it.
err.span_suggestion_verbose( err.span_suggestion_verbose(
assoc_name.span, 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(), suggested_name.to_string(),
Applicability::MaybeIncorrect, 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, // If we still couldn't find any associated type, and only one associated type exists,
// suggests using it. // suggests using it.
if let [candidate_name] = all_candidate_names.as_slice() {
if all_candidate_names.len() == 1 {
// this should still compile, except on `#![feature(associated_type_defaults)]` // this should still compile, except on `#![feature(associated_type_defaults)]`
// where it could suggests `type A = Self::A`, thus recursing infinitely // 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 Applicability::Unspecified
} else { } else {
Applicability::MaybeIncorrect Applicability::MaybeIncorrect
}; };
err.span_suggestion( err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
assoc_name.span, span: assoc_name.span,
format!("`{ty_param_name}` has the following associated type"),
all_candidate_names.first().unwrap().to_string(),
applicability, applicability,
); ty_param_name,
assoc_kind: assoc_kind_str,
suggested_name: *candidate_name,
});
} else { } 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( 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( let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind(
tcx, tcx,
ident, ident,
AssocKind::Type, ty::AssocKind::Type,
trait_def, trait_def,
); );
@ -606,7 +673,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
})) }))
}) })
.flatten() .flatten()
.collect::<FxHashMap<Symbol, &AssocItem>>(); .collect::<FxHashMap<Symbol, &ty::AssocItem>>();
let mut names = names let mut names = names
.into_iter() .into_iter()

View File

@ -18,8 +18,8 @@ use crate::require_c_abi_if_c_variadic;
use rustc_ast::TraitObjectSyntax; use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{ use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError, error_code, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan, FatalError, MultiSpan,
}; };
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; 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_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_span::edit_distance::find_best_match_for_name; 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::symbol::{kw, Ident, Symbol};
use rustc_span::{sym, BytePos, Span, DUMMY_SP}; use rustc_span::{sym, BytePos, Span, DUMMY_SP};
use rustc_target::spec::abi; use rustc_target::spec::abi;
@ -162,7 +163,7 @@ struct ConvertedBinding<'a, 'tcx> {
#[derive(Debug)] #[derive(Debug)]
enum ConvertedBindingKind<'a, 'tcx> { enum ConvertedBindingKind<'a, 'tcx> {
Equality(ty::Term<'tcx>), Equality(Spanned<ty::Term<'tcx>>),
Constraint(&'a [hir::GenericBound<'a>]), Constraint(&'a [hir::GenericBound<'a>]),
} }
@ -595,12 +596,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.map(|binding| { .map(|binding| {
let kind = match &binding.kind { let kind = match &binding.kind {
hir::TypeBindingKind::Equality { term } => match term { hir::TypeBindingKind::Equality { term } => match term {
hir::Term::Ty(ty) => { hir::Term::Ty(ty) => ConvertedBindingKind::Equality(respan(
ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into()) ty.span,
} self.ast_ty_to_ty(ty).into(),
)),
hir::Term::Const(c) => { hir::Term::Const(c) => {
let span = self.tcx().def_span(c.def_id);
let c = Const::from_anon_const(self.tcx(), 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 } => { hir::TypeBindingKind::Constraint { bounds } => {
@ -1056,7 +1059,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
debug!("find_bound_for_assoc_item: predicates={:#?}", predicates); debug!("find_bound_for_assoc_item: predicates={:#?}", predicates);
let param_name = tcx.hir().ty_param_name(ty_param_def_id); 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( traits::transitive_bounds_that_define_assoc_item(
tcx, tcx,
@ -1068,6 +1071,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}, },
param_name, param_name,
Some(ty_param_def_id), Some(ty_param_def_id),
ty::AssocKind::Type,
assoc_name, assoc_name,
span, span,
None, None,
@ -1076,48 +1080,45 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// Checks that `bounds` contains exactly one element and reports appropriate // Checks that `bounds` contains exactly one element and reports appropriate
// errors otherwise. // errors otherwise.
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)] #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, binding), ret)]
fn one_bound_for_assoc_type<I>( fn one_bound_for_assoc_item<I>(
&self, &self,
all_candidates: impl Fn() -> I, all_candidates: impl Fn() -> I,
ty_param_name: impl Display, ty_param_name: impl Display,
ty_param_def_id: Option<LocalDefId>, ty_param_def_id: Option<LocalDefId>,
assoc_kind: ty::AssocKind,
assoc_name: Ident, assoc_name: Ident,
span: Span, span: Span,
is_equality: Option<ty::Term<'tcx>>, binding: Option<&ConvertedBinding<'_, 'tcx>>,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed>
where where
I: Iterator<Item = ty::PolyTraitRef<'tcx>>, I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
{ {
let tcx = self.tcx();
let mut matching_candidates = all_candidates().filter(|r| { let mut matching_candidates = all_candidates().filter(|r| {
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Type, assoc_name) self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name)
});
let mut const_candidates = all_candidates().filter(|r| {
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Const, assoc_name)
}); });
let (mut bound, mut next_cand) = match (matching_candidates.next(), const_candidates.next()) let Some(mut bound) = matching_candidates.next() else {
{ let reported = self.complain_about_assoc_item_not_found(
(Some(bound), _) => (bound, matching_candidates.next()), all_candidates,
(None, Some(bound)) => (bound, const_candidates.next()), &ty_param_name.to_string(),
(None, None) => { ty_param_def_id,
let reported = self.complain_about_assoc_type_not_found( assoc_kind,
all_candidates, assoc_name,
&ty_param_name.to_string(), span,
ty_param_def_id, binding,
assoc_name, );
span, return Err(reported);
);
return Err(reported);
}
}; };
debug!(?bound); debug!(?bound);
// look for a candidate that is not the same as our first bound, disregarding // look for a candidate that is not the same as our first bound, disregarding
// whether the bound is const. // whether the bound is const.
let mut next_cand = matching_candidates.next();
while let Some(mut bound2) = next_cand { while let Some(mut bound2) = next_cand {
debug!(?bound2); debug!(?bound2);
let tcx = self.tcx();
if bound2.bound_vars() != bound.bound_vars() { if bound2.bound_vars() != bound.bound_vars() {
break; 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 }); .map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg });
if unconsted_args.eq(bound2.skip_binder().args.iter()) { 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 { } else {
break; break;
} }
@ -1147,48 +1148,53 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
if let Some(bound2) = next_cand { if let Some(bound2) = next_cand {
debug!(?bound2); debug!(?bound2);
let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates); let assoc_kind_str = assoc_kind_str(assoc_kind);
let mut err = if is_equality.is_some() { let ty_param_name = &ty_param_name.to_string();
// More specific Error Index entry. let mut err = tcx.sess.create_err(crate::errors::AmbiguousAssocItem {
struct_span_err!( span,
self.tcx().sess, assoc_kind: assoc_kind_str,
span, assoc_name,
E0222, ty_param_name,
"ambiguous associated type `{}` in bounds of `{}`", });
assoc_name, // Provide a more specific error code index entry for equality bindings.
ty_param_name err.code(
) if let Some(binding) = binding
} else { && let ConvertedBindingKind::Equality(_) = binding.kind
struct_span_err!( {
self.tcx().sess, error_code!(E0222)
span, } else {
E0221, error_code!(E0221)
"ambiguous associated type `{}` in bounds of `{}`", },
assoc_name, );
ty_param_name
)
};
err.span_label(span, format!("ambiguous associated type `{assoc_name}`"));
// FIXME(#97583): Resugar equality bounds to type/const bindings.
// FIXME: Turn this into a structured, translateable & more actionable suggestion.
let mut where_bounds = vec![]; 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_id = bound.def_id();
let bound_span = self let bound_span = tcx
.tcx()
.associated_items(bound_id) .associated_items(bound_id)
.find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, bound_id) .find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id)
.and_then(|item| self.tcx().hir().span_if_local(item.def_id)); .and_then(|item| tcx.hir().span_if_local(item.def_id));
if let Some(bound_span) = bound_span { if let Some(bound_span) = bound_span {
err.span_label( err.span_label(
bound_span, bound_span,
format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),), format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),),
); );
if let Some(constraint) = &is_equality { if let Some(binding) = binding {
where_bounds.push(format!( match binding.kind {
" T: {trait}::{assoc_name} = {constraint}", ConvertedBindingKind::Equality(term) => {
trait=bound.print_only_trait_path(), // 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 { } else {
err.span_suggestion_verbose( err.span_suggestion_verbose(
span.with_hi(assoc_name.span.lo()), span.with_hi(assoc_name.span.lo()),
@ -1199,7 +1205,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
} }
} else { } else {
err.note(format!( 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(), bound.print_only_trait_path(),
)); ));
} }
@ -1220,46 +1226,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Ok(bound) 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. // 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` // 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 // 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); return Err(guar);
}; };
self.one_bound_for_assoc_type( self.one_bound_for_assoc_item(
|| { || {
traits::supertraits( traits::supertraits(
tcx, tcx,
@ -1430,6 +1396,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}, },
kw::SelfUpper, kw::SelfUpper,
None, None,
ty::AssocKind::Type,
assoc_ident, assoc_ident,
span, span,
None, None,
@ -1510,15 +1477,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}; };
let trait_did = bound.def_id(); let trait_did = bound.def_id();
let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did) let assoc_ty_did = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap();
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 ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound); let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound);
if let Some(variant_def_id) = variant_resolution { if let Some(variant_def_id) = variant_resolution {
@ -1731,8 +1690,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let tcx = self.tcx(); let tcx = self.tcx();
let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block); 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 // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
// of calling `find_by_name_and_kind`. // 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| { let item = tcx.associated_items(scope).in_definition_order().find(|i| {
i.kind.namespace() == Namespace::TypeNS i.kind.namespace() == Namespace::TypeNS
&& i.ident(tcx).normalize_to_macros_2_0() == ident && i.ident(tcx).normalize_to_macros_2_0() == ident
@ -2858,3 +2818,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Some(r) 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",
}
}

View File

@ -6,9 +6,121 @@ use rustc_errors::{
MultiSpan, MultiSpan,
}; };
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; 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}; 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)] #[derive(Diagnostic)]
#[diag(hir_analysis_unrecognized_atomic_operation, code = "E0092")] #[diag(hir_analysis_unrecognized_atomic_operation, code = "E0092")]
pub struct UnrecognizedAtomicOperation<'a> { pub struct UnrecognizedAtomicOperation<'a> {
@ -537,27 +649,6 @@ pub(crate) struct ReturnTypeNotationEqualityBound {
pub span: Span, pub span: Span,
} }
#[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)] #[derive(Diagnostic)]
#[diag(hir_analysis_placeholder_not_allowed_item_signatures, code = "E0121")] #[diag(hir_analysis_placeholder_not_allowed_item_signatures, code = "E0121")]
pub(crate) struct PlaceholderNotAllowedItemSignatures { pub(crate) struct PlaceholderNotAllowedItemSignatures {
@ -954,15 +1045,6 @@ pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> {
pub return_ty: Ty<'tcx>, pub return_ty: Ty<'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)] #[derive(Diagnostic)]
#[diag(hir_analysis_inherent_ty_outside, code = "E0390")] #[diag(hir_analysis_inherent_ty_outside, code = "E0390")]
#[help] #[help]

View File

@ -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() {}

View File

@ -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`.

View File

@ -11,13 +11,12 @@ impl Foo for Bar {
const N: usize = 3; const N: usize = 3;
} }
fn foo1<F: Foo<Z = 3>>() {}
fn foo1<F: Foo<Z=3>>() {} //~^ ERROR associated constant `Z` not found for `Foo`
//~^ ERROR associated type fn foo2<F: Foo<Z = usize>>() {}
fn foo2<F: Foo<Z=usize>>() {} //~^ ERROR associated type `Z` not found for `Foo`
//~^ ERROR associated type fn foo3<F: Foo<Z = 5>>() {}
fn foo3<F: Foo<Z=5>>() {} //~^ ERROR associated constant `Z` not found for `Foo`
//~^ ERROR associated type
fn main() { fn main() {
foo1::<Bar>(); foo1::<Bar>();

View File

@ -1,20 +1,20 @@
error[E0220]: associated type `Z` not found for `Foo` error[E0220]: associated constant `Z` not found for `Foo`
--> $DIR/assoc-const-eq-missing.rs:15:16 --> $DIR/assoc-const-eq-missing.rs:14:16
| |
LL | fn foo1<F: Foo<Z=3>>() {} LL | fn foo1<F: Foo<Z = 3>>() {}
| ^ associated type `Z` not found | ^ help: there is an associated constant with a similar name: `N`
error[E0220]: associated type `Z` not found for `Foo` 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 | ^ associated type `Z` not found
error[E0220]: associated type `Z` not found for `Foo` error[E0220]: associated constant `Z` not found for `Foo`
--> $DIR/assoc-const-eq-missing.rs:19:16 --> $DIR/assoc-const-eq-missing.rs:18:16
| |
LL | fn foo3<F: Foo<Z=5>>() {} LL | fn foo3<F: Foo<Z = 5>>() {}
| ^ associated type `Z` not found | ^ help: there is an associated constant with a similar name: `N`
error: aborting due to 3 previous errors error: aborting due to 3 previous errors

View File

@ -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() {}

View File

@ -2,30 +2,30 @@
#![allow(unused)] #![allow(unused)]
pub trait Foo { pub trait Foo {
const N: usize; const N: usize;
} }
pub trait FooTy { pub trait FooTy {
type T; type T;
} }
pub struct Bar; pub struct Bar;
impl Foo for Bar { impl Foo for Bar {
const N: usize = 3; const N: usize = 3;
} }
impl FooTy for Bar { impl FooTy for Bar {
type T = usize; type T = usize;
} }
fn foo<F: Foo<N=usize>>() {} fn foo<F: Foo<N = usize>>() {}
//~^ ERROR expected associated constant bound, found type //~^ ERROR expected constant, found type
fn foo2<F: FooTy<T=3usize>>() {} fn foo2<F: FooTy<T = 3usize>>() {}
//~^ ERROR expected associated type bound, found constant //~^ ERROR expected type, found constant
fn main() { fn main() {
foo::<Bar>(); foo::<Bar>();
foo2::<Bar>(); foo2::<Bar>();
} }

View File

@ -1,26 +1,30 @@
error: expected associated constant bound, found type error: expected constant, found type
--> $DIR/assoc-const-ty-mismatch.rs:23:15 --> $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 note: the associated constant is defined here
--> $DIR/assoc-const-ty-mismatch.rs:5:3 --> $DIR/assoc-const-ty-mismatch.rs:5:5
| |
LL | const N: usize; LL | const N: usize;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: expected associated type bound, found constant error: expected type, found constant
--> $DIR/assoc-const-ty-mismatch.rs:25:18 --> $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 note: the associated type is defined here
--> $DIR/assoc-const-ty-mismatch.rs:9:3 --> $DIR/assoc-const-ty-mismatch.rs:9:5
| |
LL | type T; LL | type T;
| ^^^^^^ | ^^^^^^
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View File

@ -17,7 +17,7 @@ trait Baz2: Foo {
trait Baz3 { trait Baz3 {
const BAR: usize; const BAR: usize;
const QUX: Self::BAR; const QUX: Self::BAR;
//~^ ERROR found associated const //~^ ERROR expected type, found constant
} }
fn main() {} fn main() {}

View File

@ -1,8 +1,14 @@
error: found associated const `BAR` when type was expected error: expected type, found constant
--> $DIR/shadowed-const.rs:19:14 --> $DIR/shadowed-const.rs:19:20
| |
LL | const QUX: Self::BAR; 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 error: aborting due to 1 previous error

View File

@ -1,7 +1,7 @@
#![feature(associated_type_bounds)] #![feature(associated_type_bounds)]
pub fn accept(_: impl Trait<K: Copy>) {} pub fn accept(_: impl Trait<K: Copy>) {}
//~^ ERROR expected associated type, found associated constant //~^ ERROR expected type, found constant
pub trait Trait { pub trait Trait {
const K: i32; const K: i32;

View File

@ -1,10 +1,16 @@
error: expected associated type, found associated constant error: expected type, found constant
--> $DIR/consts.rs:3:29 --> $DIR/consts.rs:3:29
| |
LL | pub fn accept(_: impl Trait<K: Copy>) {} 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 error: aborting due to 1 previous error

View File

@ -1,5 +1,5 @@
fn get_iter(vec: &[i32]) -> impl Iterator<Item = {}> + '_ { 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 //~| ERROR associated const equality is incomplete
vec.iter() vec.iter()
} }

View File

@ -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 = 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 = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
error: expected associated type bound, found constant error: expected type, found constant
--> $DIR/issue-99828.rs:1:43 --> $DIR/issue-99828.rs:1:50
| |
LL | fn get_iter(vec: &[i32]) -> impl Iterator<Item = {}> + '_ { 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 --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View File

@ -8,6 +8,6 @@ trait Trait {
} }
fn bar<T: Trait<methid(): Send>>() {} fn bar<T: Trait<methid(): Send>>() {}
//~^ ERROR cannot find associated function `methid` for `Trait` //~^ ERROR associated function `methid` not found for `Trait`
fn main() {} fn main() {}

View File

@ -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: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default = 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 --> $DIR/missing.rs:10:17
| |
LL | fn bar<T: Trait<methid(): Send>>() {} 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 error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0220`.

View File

@ -23,7 +23,7 @@ impl Foo for () {}
fn test<T>() fn test<T>()
where where
T: Foo<test(): Send>, T: Foo<test(): Send>,
//~^ ERROR ambiguous associated function `test` for `Foo` //~^ ERROR ambiguous associated function `test` in bounds of `Foo`
{ {
} }

View File

@ -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: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default = 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 --> $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>, LL | T: Foo<test(): Send>,
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^ ambiguous associated function `test`
|
= note: `test` is declared in two supertraits: `Super2` and `Super1<'a>`
error: aborting due to 1 previous error; 1 warning emitted error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0221`.

View File

@ -9,9 +9,9 @@ pub trait Parse {
} }
pub trait CoolStuff: Parse<MODE = Mode::Cool> {} pub trait CoolStuff: Parse<MODE = Mode::Cool> {}
//~^ ERROR expected associated constant bound //~^ ERROR expected type, found variant
//~| ERROR expected associated constant bound //~| ERROR expected constant, found type
//~| ERROR expected type //~| ERROR expected constant, found type
fn no_help() -> Mode::Cool {} fn no_help() -> Mode::Cool {}
//~^ ERROR expected type, found variant //~^ ERROR expected type, found variant

View File

@ -16,30 +16,42 @@ LL | fn no_help() -> Mode::Cool {}
| not a type | not a type
| help: try using the variant's enum: `Mode` | help: try using the variant's enum: `Mode`
error: expected associated constant bound, found type error: expected constant, found type
--> $DIR/assoc_const_eq_diagnostic.rs:11:28 --> $DIR/assoc_const_eq_diagnostic.rs:11:35
| |
LL | pub trait CoolStuff: Parse<MODE = Mode::Cool> {} 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 --> $DIR/assoc_const_eq_diagnostic.rs:8:5
| |
LL | const MODE: Mode; LL | const MODE: Mode;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
help: consider adding braces here
|
LL | pub trait CoolStuff: Parse<MODE = { Mode::Cool }> {}
| + +
error: expected associated constant bound, found type error: expected constant, found type
--> $DIR/assoc_const_eq_diagnostic.rs:11:28 --> $DIR/assoc_const_eq_diagnostic.rs:11:35
| |
LL | pub trait CoolStuff: Parse<MODE = Mode::Cool> {} 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 --> $DIR/assoc_const_eq_diagnostic.rs:8:5
| |
LL | const MODE: Mode; LL | const MODE: Mode;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` = 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 error: aborting due to 4 previous errors

View File

@ -28,7 +28,7 @@ LL | fn test() {
LL | let _: Self::Err; LL | let _: Self::Err;
| ^^^^^^^^^ ambiguous associated type `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 help: use fully-qualified syntax to disambiguate
| |
LL | let _: <Self as My>::Err; LL | let _: <Self as My>::Err;

View File

@ -15,13 +15,18 @@ LL | fn foo<T: Trait<m(): Send>>() {}
| | | |
| help: remove these parentheses | 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 --> $DIR/feature-gate-return_type_notation.rs:14:17
| |
LL | fn foo<T: Trait<m(): Send>>() {} 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 error: aborting due to 3 previous errors
Some errors have detailed explanations: E0220, E0658. For more information about this error, try `rustc --explain E0658`.
For more information about an error, try `rustc --explain E0220`.

View File

@ -14,7 +14,7 @@ trait Trait {
fn foo<T: Trait<m(): Send>>() {} fn foo<T: Trait<m(): Send>>() {}
//[cfg]~^ ERROR return type notation is experimental //[cfg]~^ ERROR return type notation is experimental
//[cfg]~| ERROR parenthesized generic arguments cannot be used in associated type constraints //[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 return type notation is experimental
//[no]~| WARN unstable syntax can change at any point in the future, causing a hard error! //[no]~| WARN unstable syntax can change at any point in the future, causing a hard error!