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