Resolve assoc item bindings by namespace

If a const is expected, resolve a const.
If a type is expected, resolve a type.
Don't try to resolve a type first falling back to consts.
This commit is contained in:
León Orell Valerian Liehr 2023-11-24 09:53:10 +01:00
parent dd6126ef56
commit 55559d93e7
No known key found for this signature in database
GPG Key ID: D17A07215F68E713
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!