mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
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:
commit
5ea62560f2
@ -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
|
||||||
|
@ -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`
|
||||||
|
@ -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()
|
||||||
|
@ -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",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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]
|
||||||
|
19
tests/ui/associated-consts/assoc-const-eq-ambiguity.rs
Normal file
19
tests/ui/associated-consts/assoc-const-eq-ambiguity.rs
Normal 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() {}
|
38
tests/ui/associated-consts/assoc-const-eq-ambiguity.stderr
Normal file
38
tests/ui/associated-consts/assoc-const-eq-ambiguity.stderr
Normal 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`.
|
@ -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>();
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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() {}
|
@ -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>();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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() {}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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() {}
|
||||||
|
@ -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`.
|
||||||
|
@ -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`
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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`.
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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`.
|
|
||||||
|
@ -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!
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user