mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-21 04:03:11 +00:00
Resolve assoc item bindings by namespace
If a const is expected, resolve a const. If a type is expected, resolve a type. Don't try to resolve a type first falling back to consts.
This commit is contained in:
parent
dd6126ef56
commit
55559d93e7
@ -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_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_lint_defs::Applicability;
|
||||
use rustc_middle::ty::{self as ty, Ty, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self as ty, Ty};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
use rustc_trait_selection::traits;
|
||||
@ -256,64 +255,49 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||
|
||||
let tcx = self.tcx();
|
||||
|
||||
let return_type_notation =
|
||||
binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation;
|
||||
|
||||
let candidate = if return_type_notation {
|
||||
if self.trait_defines_associated_item_named(
|
||||
trait_ref.def_id(),
|
||||
ty::AssocKind::Fn,
|
||||
binding.item_name,
|
||||
) {
|
||||
trait_ref
|
||||
let assoc_kind =
|
||||
if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
|
||||
ty::AssocKind::Fn
|
||||
} else if let ConvertedBindingKind::Equality(term) = binding.kind
|
||||
&& let ty::TermKind::Const(_) = term.node.unpack()
|
||||
{
|
||||
ty::AssocKind::Const
|
||||
} else {
|
||||
self.one_bound_for_assoc_method(
|
||||
traits::supertraits(tcx, trait_ref),
|
||||
trait_ref.print_only_trait_path(),
|
||||
binding.item_name,
|
||||
path_span,
|
||||
)?
|
||||
}
|
||||
} else if self.trait_defines_associated_item_named(
|
||||
ty::AssocKind::Type
|
||||
};
|
||||
|
||||
let candidate = if self.trait_defines_associated_item_named(
|
||||
trait_ref.def_id(),
|
||||
ty::AssocKind::Type,
|
||||
assoc_kind,
|
||||
binding.item_name,
|
||||
) {
|
||||
// Simple case: X is defined in the current trait.
|
||||
// Simple case: The assoc item is defined in the current trait.
|
||||
trait_ref
|
||||
} else {
|
||||
// Otherwise, we have to walk through the supertraits to find
|
||||
// those that do.
|
||||
self.one_bound_for_assoc_type(
|
||||
// one that does define it.
|
||||
self.one_bound_for_assoc_item(
|
||||
|| traits::supertraits(tcx, trait_ref),
|
||||
trait_ref.skip_binder().print_only_trait_name(),
|
||||
None,
|
||||
assoc_kind,
|
||||
binding.item_name,
|
||||
path_span,
|
||||
match binding.kind {
|
||||
ConvertedBindingKind::Equality(term) => Some(term),
|
||||
_ => None,
|
||||
},
|
||||
Some(&binding),
|
||||
)?
|
||||
};
|
||||
|
||||
let (assoc_ident, def_scope) =
|
||||
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
|
||||
|
||||
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
|
||||
// of calling `filter_by_name_and_kind`.
|
||||
let find_item_of_kind = |kind| {
|
||||
tcx.associated_items(candidate.def_id())
|
||||
.filter_by_name_unhygienic(assoc_ident.name)
|
||||
.find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
|
||||
};
|
||||
let assoc_item = if return_type_notation {
|
||||
find_item_of_kind(ty::AssocKind::Fn)
|
||||
} else {
|
||||
find_item_of_kind(ty::AssocKind::Type)
|
||||
.or_else(|| find_item_of_kind(ty::AssocKind::Const))
|
||||
}
|
||||
.expect("missing associated type");
|
||||
// We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
|
||||
// instead of calling `filter_by_name_and_kind` which would needlessly normalize the
|
||||
// `assoc_ident` again and again.
|
||||
let assoc_item = tcx
|
||||
.associated_items(candidate.def_id())
|
||||
.filter_by_name_unhygienic(assoc_ident.name)
|
||||
.find(|i| i.kind == assoc_kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
|
||||
.expect("missing associated item");
|
||||
|
||||
if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
|
||||
tcx.sess
|
||||
@ -340,7 +324,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||
.or_insert(binding.span);
|
||||
}
|
||||
|
||||
let projection_ty = if return_type_notation {
|
||||
let projection_ty = if let ty::AssocKind::Fn = assoc_kind {
|
||||
let mut emitted_bad_param_err = false;
|
||||
// If we have an method return type bound, then we need to substitute
|
||||
// the method's early bound params with suitable late-bound params.
|
||||
@ -467,7 +451,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||
let late_bound_in_trait_ref =
|
||||
tcx.collect_constrained_late_bound_regions(&projection_ty);
|
||||
let late_bound_in_ty =
|
||||
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
|
||||
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty.node));
|
||||
debug!(?late_bound_in_trait_ref);
|
||||
debug!(?late_bound_in_ty);
|
||||
|
||||
@ -492,77 +476,27 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||
}
|
||||
}
|
||||
|
||||
let assoc_item_def_id = projection_ty.skip_binder().def_id;
|
||||
let def_kind = tcx.def_kind(assoc_item_def_id);
|
||||
match binding.kind {
|
||||
ConvertedBindingKind::Equality(..) if return_type_notation => {
|
||||
ConvertedBindingKind::Equality(..) if let ty::AssocKind::Fn = assoc_kind => {
|
||||
return Err(self.tcx().sess.emit_err(
|
||||
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
|
||||
));
|
||||
}
|
||||
ConvertedBindingKind::Equality(mut term) => {
|
||||
ConvertedBindingKind::Equality(term) => {
|
||||
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
|
||||
// the "projection predicate" for:
|
||||
//
|
||||
// `<T as Iterator>::Item = u32`
|
||||
match (def_kind, term.unpack()) {
|
||||
(DefKind::AssocTy, ty::TermKind::Ty(_))
|
||||
| (DefKind::AssocConst, ty::TermKind::Const(_)) => (),
|
||||
(_, _) => {
|
||||
let got = if let Some(_) = term.ty() { "type" } else { "constant" };
|
||||
let expected = tcx.def_descr(assoc_item_def_id);
|
||||
let mut err = tcx.sess.struct_span_err(
|
||||
binding.span,
|
||||
format!("expected {expected} bound, found {got}"),
|
||||
);
|
||||
err.span_note(
|
||||
tcx.def_span(assoc_item_def_id),
|
||||
format!("{expected} defined here"),
|
||||
);
|
||||
|
||||
if let DefKind::AssocConst = def_kind
|
||||
&& let Some(t) = term.ty()
|
||||
&& (t.is_enum() || t.references_error())
|
||||
&& tcx.features().associated_const_equality
|
||||
{
|
||||
err.span_suggestion(
|
||||
binding.span,
|
||||
"if equating a const, try wrapping with braces",
|
||||
format!("{} = {{ const }}", binding.item_name),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
let reported = err.emit();
|
||||
term = match def_kind {
|
||||
DefKind::AssocTy => Ty::new_error(tcx, reported).into(),
|
||||
DefKind::AssocConst => ty::Const::new_error(
|
||||
tcx,
|
||||
reported,
|
||||
tcx.type_of(assoc_item_def_id)
|
||||
.instantiate(tcx, projection_ty.skip_binder().args),
|
||||
)
|
||||
.into(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
bounds.push_projection_bound(
|
||||
tcx,
|
||||
projection_ty
|
||||
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
|
||||
projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
|
||||
projection_ty,
|
||||
term: term.node,
|
||||
}),
|
||||
binding.span,
|
||||
);
|
||||
}
|
||||
ConvertedBindingKind::Constraint(ast_bounds) => {
|
||||
match def_kind {
|
||||
DefKind::AssocTy => {}
|
||||
_ => {
|
||||
return Err(tcx.sess.emit_err(errors::AssocBoundOnConst {
|
||||
span: assoc_ident.span,
|
||||
descr: tcx.def_descr(assoc_item_def_id),
|
||||
}));
|
||||
}
|
||||
}
|
||||
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
|
||||
//
|
||||
// `<T as Iterator>::Item: Debug`
|
||||
|
@ -1,15 +1,16 @@
|
||||
use crate::astconv::AstConv;
|
||||
use crate::astconv::{AstConv, ConvertedBindingKind};
|
||||
use crate::errors::{
|
||||
AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
|
||||
self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
|
||||
ParenthesizedFnTraitExpansion,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::traits::error_reporting::report_object_safety_error;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_infer::traits::FulfillmentError;
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_param, AssocItem, AssocKind, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
@ -97,83 +98,88 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn complain_about_assoc_type_not_found<I>(
|
||||
pub(super) fn complain_about_assoc_item_not_found<I>(
|
||||
&self,
|
||||
all_candidates: impl Fn() -> I,
|
||||
ty_param_name: &str,
|
||||
ty_param_def_id: Option<LocalDefId>,
|
||||
assoc_kind: ty::AssocKind,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
|
||||
) -> ErrorGuaranteed
|
||||
where
|
||||
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||
{
|
||||
let tcx = self.tcx();
|
||||
|
||||
// First and foremost, provide a more user-friendly & “intuitive” error on kind mismatches.
|
||||
if let Some(assoc_item) = all_candidates().find_map(|r| {
|
||||
tcx.associated_items(r.def_id())
|
||||
.filter_by_name_unhygienic(assoc_name.name)
|
||||
.find(|item| tcx.hygienic_eq(assoc_name, item.ident(tcx), r.def_id()))
|
||||
}) {
|
||||
return self.complain_about_assoc_kind_mismatch(
|
||||
assoc_item, assoc_kind, assoc_name, span, binding,
|
||||
);
|
||||
}
|
||||
|
||||
let assoc_kind_str = super::assoc_kind_str(assoc_kind);
|
||||
|
||||
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
|
||||
// valid span, so we point at the whole path segment instead.
|
||||
let is_dummy = assoc_name.span == DUMMY_SP;
|
||||
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx().sess,
|
||||
if is_dummy { span } else { assoc_name.span },
|
||||
E0220,
|
||||
"associated type `{}` not found for `{}`",
|
||||
let mut err = errors::AssocItemNotFound {
|
||||
span: if is_dummy { span } else { assoc_name.span },
|
||||
assoc_name,
|
||||
ty_param_name
|
||||
);
|
||||
assoc_kind: assoc_kind_str,
|
||||
ty_param_name,
|
||||
label: None,
|
||||
sugg: None,
|
||||
};
|
||||
|
||||
if is_dummy {
|
||||
err.span_label(span, format!("associated type `{assoc_name}` not found"));
|
||||
return err.emit();
|
||||
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span });
|
||||
return tcx.sess.emit_err(err);
|
||||
}
|
||||
|
||||
let all_candidate_names: Vec<_> = all_candidates()
|
||||
.flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
|
||||
.flat_map(|r| tcx.associated_items(r.def_id()).in_definition_order())
|
||||
.filter_map(|item| {
|
||||
if !item.is_impl_trait_in_trait() && item.kind == ty::AssocKind::Type {
|
||||
Some(item.name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name)
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Some(suggested_name) =
|
||||
find_best_match_for_name(&all_candidate_names, assoc_name.name, None)
|
||||
{
|
||||
err.span_suggestion(
|
||||
assoc_name.span,
|
||||
"there is an associated type with a similar name",
|
||||
err.sugg = Some(errors::AssocItemNotFoundSugg::Similar {
|
||||
span: assoc_name.span,
|
||||
assoc_kind: assoc_kind_str,
|
||||
suggested_name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return err.emit();
|
||||
});
|
||||
return tcx.sess.emit_err(err);
|
||||
}
|
||||
|
||||
// If we didn't find a good item in the supertraits (or couldn't get
|
||||
// the supertraits), like in ItemCtxt, then look more generally from
|
||||
// all visible traits. If there's one clear winner, just suggest that.
|
||||
|
||||
let visible_traits: Vec<_> = self
|
||||
.tcx()
|
||||
let visible_traits: Vec<_> = tcx
|
||||
.all_traits()
|
||||
.filter(|trait_def_id| {
|
||||
let viz = self.tcx().visibility(*trait_def_id);
|
||||
let viz = tcx.visibility(*trait_def_id);
|
||||
let def_id = self.item_def_id();
|
||||
viz.is_accessible_from(def_id, self.tcx())
|
||||
viz.is_accessible_from(def_id, tcx)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let wider_candidate_names: Vec<_> = visible_traits
|
||||
.iter()
|
||||
.flat_map(|trait_def_id| {
|
||||
self.tcx().associated_items(*trait_def_id).in_definition_order()
|
||||
})
|
||||
.flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order())
|
||||
.filter_map(|item| {
|
||||
if !item.is_impl_trait_in_trait() && item.kind == ty::AssocKind::Type {
|
||||
Some(item.name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -182,52 +188,51 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
{
|
||||
if let [best_trait] = visible_traits
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|trait_def_id| {
|
||||
self.tcx()
|
||||
.associated_items(*trait_def_id)
|
||||
tcx.associated_items(trait_def_id)
|
||||
.filter_by_name_unhygienic(suggested_name)
|
||||
.any(|item| item.kind == ty::AssocKind::Type)
|
||||
.any(|item| item.kind == assoc_kind)
|
||||
})
|
||||
.collect::<Vec<_>>()[..]
|
||||
{
|
||||
let trait_name = self.tcx().def_path_str(*best_trait);
|
||||
let an = if suggested_name != assoc_name.name { "a similarly named" } else { "an" };
|
||||
err.span_label(
|
||||
assoc_name.span,
|
||||
format!(
|
||||
"there is {an} associated type `{suggested_name}` in the \
|
||||
trait `{trait_name}`",
|
||||
),
|
||||
);
|
||||
let hir = self.tcx().hir();
|
||||
let trait_name = tcx.def_path_str(best_trait);
|
||||
err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait {
|
||||
span: assoc_name.span,
|
||||
assoc_kind: assoc_kind_str,
|
||||
trait_name: &trait_name,
|
||||
suggested_name,
|
||||
identically_named: suggested_name == assoc_name.name,
|
||||
});
|
||||
let hir = tcx.hir();
|
||||
if let Some(def_id) = ty_param_def_id
|
||||
&& let parent = hir.get_parent_item(self.tcx().local_def_id_to_hir_id(def_id))
|
||||
&& let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id))
|
||||
&& let Some(generics) = hir.get_generics(parent.def_id)
|
||||
{
|
||||
if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any(
|
||||
|b| match b {
|
||||
hir::GenericBound::Trait(t, ..) => {
|
||||
t.trait_ref.trait_def_id().as_ref() == Some(best_trait)
|
||||
t.trait_ref.trait_def_id() == Some(best_trait)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
) {
|
||||
// The type param already has a bound for `trait_name`, we just need to
|
||||
// change the associated type.
|
||||
err.span_suggestion_verbose(
|
||||
assoc_name.span,
|
||||
format!(
|
||||
"change the associated type name to use `{suggested_name}` from \
|
||||
`{trait_name}`",
|
||||
),
|
||||
suggested_name.to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else if suggest_constraining_type_param(
|
||||
self.tcx(),
|
||||
// change the associated item.
|
||||
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
|
||||
span: assoc_name.span,
|
||||
assoc_kind: assoc_kind_str,
|
||||
suggested_name,
|
||||
});
|
||||
return tcx.sess.emit_err(err);
|
||||
}
|
||||
|
||||
let mut err = tcx.sess.create_err(err);
|
||||
if suggest_constraining_type_param(
|
||||
tcx,
|
||||
generics,
|
||||
&mut err,
|
||||
ty_param_name,
|
||||
&ty_param_name,
|
||||
&trait_name,
|
||||
None,
|
||||
None,
|
||||
@ -237,39 +242,101 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
// was also not an exact match, so we also suggest changing it.
|
||||
err.span_suggestion_verbose(
|
||||
assoc_name.span,
|
||||
"and also change the associated type name",
|
||||
fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg,
|
||||
suggested_name.to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
return err.emit();
|
||||
}
|
||||
return err.emit();
|
||||
return tcx.sess.emit_err(err);
|
||||
}
|
||||
}
|
||||
|
||||
// If we still couldn't find any associated type, and only one associated type exists,
|
||||
// suggests using it.
|
||||
|
||||
if all_candidate_names.len() == 1 {
|
||||
if let [candidate_name] = all_candidate_names.as_slice() {
|
||||
// this should still compile, except on `#![feature(associated_type_defaults)]`
|
||||
// where it could suggests `type A = Self::A`, thus recursing infinitely
|
||||
let applicability = if self.tcx().features().associated_type_defaults {
|
||||
let applicability = if tcx.features().associated_type_defaults {
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
|
||||
err.span_suggestion(
|
||||
assoc_name.span,
|
||||
format!("`{ty_param_name}` has the following associated type"),
|
||||
all_candidate_names.first().unwrap().to_string(),
|
||||
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
|
||||
span: assoc_name.span,
|
||||
applicability,
|
||||
);
|
||||
ty_param_name,
|
||||
assoc_kind: assoc_kind_str,
|
||||
suggested_name: *candidate_name,
|
||||
});
|
||||
} else {
|
||||
err.span_label(assoc_name.span, format!("associated type `{assoc_name}` not found"));
|
||||
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span });
|
||||
}
|
||||
|
||||
err.emit()
|
||||
tcx.sess.emit_err(err)
|
||||
}
|
||||
|
||||
fn complain_about_assoc_kind_mismatch(
|
||||
&self,
|
||||
assoc_item: &ty::AssocItem,
|
||||
assoc_kind: ty::AssocKind,
|
||||
ident: Ident,
|
||||
span: Span,
|
||||
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
|
||||
) -> ErrorGuaranteed {
|
||||
let tcx = self.tcx();
|
||||
|
||||
let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind
|
||||
&& let Some(binding) = binding
|
||||
&& let ConvertedBindingKind::Constraint(_) = binding.kind
|
||||
{
|
||||
let lo = if binding.gen_args.span_ext.is_dummy() {
|
||||
ident.span
|
||||
} else {
|
||||
binding.gen_args.span_ext
|
||||
};
|
||||
Some(lo.between(span.shrink_to_hi()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// FIXME(associated_const_equality): This has quite a few false positives and negatives.
|
||||
let wrap_in_braces_sugg = if let Some(binding) = binding
|
||||
&& let ConvertedBindingKind::Equality(term) = binding.kind
|
||||
&& let ty::TermKind::Ty(ty) = term.node.unpack()
|
||||
&& (ty.is_enum() || ty.references_error())
|
||||
&& tcx.features().associated_const_equality
|
||||
{
|
||||
Some(errors::AssocKindMismatchWrapInBracesSugg {
|
||||
lo: term.span.shrink_to_lo(),
|
||||
hi: term.span.shrink_to_hi(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// For equality bounds, we want to blame the term (RHS) instead of the item (LHS) since
|
||||
// one can argue that that's more “untuitive” to the user.
|
||||
let (span, expected_because_label, expected, got) = if let Some(binding) = binding
|
||||
&& let ConvertedBindingKind::Equality(term) = binding.kind
|
||||
{
|
||||
(term.span, Some(ident.span), assoc_item.kind, assoc_kind)
|
||||
} else {
|
||||
(ident.span, None, assoc_kind, assoc_item.kind)
|
||||
};
|
||||
|
||||
tcx.sess.emit_err(errors::AssocKindMismatch {
|
||||
span,
|
||||
expected: super::assoc_kind_str(expected),
|
||||
got: super::assoc_kind_str(got),
|
||||
expected_because_label,
|
||||
assoc_kind: super::assoc_kind_str(assoc_item.kind),
|
||||
def_span: tcx.def_span(assoc_item.def_id),
|
||||
bound_on_assoc_const_label,
|
||||
wrap_in_braces_sugg,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn complain_about_ambiguous_inherent_assoc_type(
|
||||
@ -598,7 +665,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind(
|
||||
tcx,
|
||||
ident,
|
||||
AssocKind::Type,
|
||||
ty::AssocKind::Type,
|
||||
trait_def,
|
||||
);
|
||||
|
||||
@ -606,7 +673,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
}))
|
||||
})
|
||||
.flatten()
|
||||
.collect::<FxHashMap<Symbol, &AssocItem>>();
|
||||
.collect::<FxHashMap<Symbol, &ty::AssocItem>>();
|
||||
|
||||
let mut names = names
|
||||
.into_iter()
|
||||
|
@ -18,8 +18,8 @@ use crate::require_c_abi_if_c_variadic;
|
||||
use rustc_ast::TraitObjectSyntax;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::{
|
||||
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError,
|
||||
MultiSpan,
|
||||
error_code, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
||||
FatalError, MultiSpan,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
|
||||
@ -36,6 +36,7 @@ use rustc_middle::ty::{
|
||||
};
|
||||
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::source_map::{respan, Spanned};
|
||||
use rustc_span::symbol::{kw, Ident, Symbol};
|
||||
use rustc_span::{sym, BytePos, Span, DUMMY_SP};
|
||||
use rustc_target::spec::abi;
|
||||
@ -162,7 +163,7 @@ struct ConvertedBinding<'a, 'tcx> {
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ConvertedBindingKind<'a, 'tcx> {
|
||||
Equality(ty::Term<'tcx>),
|
||||
Equality(Spanned<ty::Term<'tcx>>),
|
||||
Constraint(&'a [hir::GenericBound<'a>]),
|
||||
}
|
||||
|
||||
@ -595,12 +596,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
.map(|binding| {
|
||||
let kind = match &binding.kind {
|
||||
hir::TypeBindingKind::Equality { term } => match term {
|
||||
hir::Term::Ty(ty) => {
|
||||
ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into())
|
||||
}
|
||||
hir::Term::Ty(ty) => ConvertedBindingKind::Equality(respan(
|
||||
ty.span,
|
||||
self.ast_ty_to_ty(ty).into(),
|
||||
)),
|
||||
hir::Term::Const(c) => {
|
||||
let span = self.tcx().def_span(c.def_id);
|
||||
let c = Const::from_anon_const(self.tcx(), c.def_id);
|
||||
ConvertedBindingKind::Equality(c.into())
|
||||
ConvertedBindingKind::Equality(respan(span, c.into()))
|
||||
}
|
||||
},
|
||||
hir::TypeBindingKind::Constraint { bounds } => {
|
||||
@ -1056,7 +1059,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
debug!("find_bound_for_assoc_item: predicates={:#?}", predicates);
|
||||
|
||||
let param_name = tcx.hir().ty_param_name(ty_param_def_id);
|
||||
self.one_bound_for_assoc_type(
|
||||
self.one_bound_for_assoc_item(
|
||||
|| {
|
||||
traits::transitive_bounds_that_define_assoc_item(
|
||||
tcx,
|
||||
@ -1068,6 +1071,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
},
|
||||
param_name,
|
||||
Some(ty_param_def_id),
|
||||
ty::AssocKind::Type,
|
||||
assoc_name,
|
||||
span,
|
||||
None,
|
||||
@ -1076,48 +1080,45 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
|
||||
// Checks that `bounds` contains exactly one element and reports appropriate
|
||||
// errors otherwise.
|
||||
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)]
|
||||
fn one_bound_for_assoc_type<I>(
|
||||
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, binding), ret)]
|
||||
fn one_bound_for_assoc_item<I>(
|
||||
&self,
|
||||
all_candidates: impl Fn() -> I,
|
||||
ty_param_name: impl Display,
|
||||
ty_param_def_id: Option<LocalDefId>,
|
||||
assoc_kind: ty::AssocKind,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
is_equality: Option<ty::Term<'tcx>>,
|
||||
binding: Option<&ConvertedBinding<'_, 'tcx>>,
|
||||
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed>
|
||||
where
|
||||
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||
{
|
||||
let tcx = self.tcx();
|
||||
|
||||
let mut matching_candidates = all_candidates().filter(|r| {
|
||||
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Type, assoc_name)
|
||||
});
|
||||
let mut const_candidates = all_candidates().filter(|r| {
|
||||
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Const, assoc_name)
|
||||
self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name)
|
||||
});
|
||||
|
||||
let (mut bound, mut next_cand) = match (matching_candidates.next(), const_candidates.next())
|
||||
{
|
||||
(Some(bound), _) => (bound, matching_candidates.next()),
|
||||
(None, Some(bound)) => (bound, const_candidates.next()),
|
||||
(None, None) => {
|
||||
let reported = self.complain_about_assoc_type_not_found(
|
||||
all_candidates,
|
||||
&ty_param_name.to_string(),
|
||||
ty_param_def_id,
|
||||
assoc_name,
|
||||
span,
|
||||
);
|
||||
return Err(reported);
|
||||
}
|
||||
let Some(mut bound) = matching_candidates.next() else {
|
||||
let reported = self.complain_about_assoc_item_not_found(
|
||||
all_candidates,
|
||||
&ty_param_name.to_string(),
|
||||
ty_param_def_id,
|
||||
assoc_kind,
|
||||
assoc_name,
|
||||
span,
|
||||
binding,
|
||||
);
|
||||
return Err(reported);
|
||||
};
|
||||
debug!(?bound);
|
||||
|
||||
// look for a candidate that is not the same as our first bound, disregarding
|
||||
// whether the bound is const.
|
||||
let mut next_cand = matching_candidates.next();
|
||||
while let Some(mut bound2) = next_cand {
|
||||
debug!(?bound2);
|
||||
let tcx = self.tcx();
|
||||
if bound2.bound_vars() != bound.bound_vars() {
|
||||
break;
|
||||
}
|
||||
@ -1138,7 +1139,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
.map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg });
|
||||
|
||||
if unconsted_args.eq(bound2.skip_binder().args.iter()) {
|
||||
next_cand = matching_candidates.next().or_else(|| const_candidates.next());
|
||||
next_cand = matching_candidates.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -1147,48 +1148,53 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
if let Some(bound2) = next_cand {
|
||||
debug!(?bound2);
|
||||
|
||||
let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates);
|
||||
let mut err = if is_equality.is_some() {
|
||||
// More specific Error Index entry.
|
||||
struct_span_err!(
|
||||
self.tcx().sess,
|
||||
span,
|
||||
E0222,
|
||||
"ambiguous associated type `{}` in bounds of `{}`",
|
||||
assoc_name,
|
||||
ty_param_name
|
||||
)
|
||||
} else {
|
||||
struct_span_err!(
|
||||
self.tcx().sess,
|
||||
span,
|
||||
E0221,
|
||||
"ambiguous associated type `{}` in bounds of `{}`",
|
||||
assoc_name,
|
||||
ty_param_name
|
||||
)
|
||||
};
|
||||
err.span_label(span, format!("ambiguous associated type `{assoc_name}`"));
|
||||
let assoc_kind_str = assoc_kind_str(assoc_kind);
|
||||
let ty_param_name = &ty_param_name.to_string();
|
||||
let mut err = tcx.sess.create_err(crate::errors::AmbiguousAssocItem {
|
||||
span,
|
||||
assoc_kind: assoc_kind_str,
|
||||
assoc_name,
|
||||
ty_param_name,
|
||||
});
|
||||
// Provide a more specific error code index entry for equality bindings.
|
||||
err.code(
|
||||
if let Some(binding) = binding
|
||||
&& let ConvertedBindingKind::Equality(_) = binding.kind
|
||||
{
|
||||
error_code!(E0222)
|
||||
} else {
|
||||
error_code!(E0221)
|
||||
},
|
||||
);
|
||||
|
||||
// FIXME(#97583): Resugar equality bounds to type/const bindings.
|
||||
// FIXME: Turn this into a structured, translateable & more actionable suggestion.
|
||||
let mut where_bounds = vec![];
|
||||
for bound in bounds {
|
||||
for bound in [bound, bound2].into_iter().chain(matching_candidates) {
|
||||
let bound_id = bound.def_id();
|
||||
let bound_span = self
|
||||
.tcx()
|
||||
let bound_span = tcx
|
||||
.associated_items(bound_id)
|
||||
.find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, bound_id)
|
||||
.and_then(|item| self.tcx().hir().span_if_local(item.def_id));
|
||||
.find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id)
|
||||
.and_then(|item| tcx.hir().span_if_local(item.def_id));
|
||||
|
||||
if let Some(bound_span) = bound_span {
|
||||
err.span_label(
|
||||
bound_span,
|
||||
format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),),
|
||||
);
|
||||
if let Some(constraint) = &is_equality {
|
||||
where_bounds.push(format!(
|
||||
" T: {trait}::{assoc_name} = {constraint}",
|
||||
trait=bound.print_only_trait_path(),
|
||||
));
|
||||
if let Some(binding) = binding {
|
||||
match binding.kind {
|
||||
ConvertedBindingKind::Equality(term) => {
|
||||
// FIXME(#97583): This isn't syntactically well-formed!
|
||||
where_bounds.push(format!(
|
||||
" T: {trait}::{assoc_name} = {term}",
|
||||
trait = bound.print_only_trait_path(),
|
||||
term = term.node,
|
||||
));
|
||||
}
|
||||
// FIXME: Provide a suggestion.
|
||||
ConvertedBindingKind::Constraint(_bounds) => {}
|
||||
}
|
||||
} else {
|
||||
err.span_suggestion_verbose(
|
||||
span.with_hi(assoc_name.span.lo()),
|
||||
@ -1199,7 +1205,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
}
|
||||
} else {
|
||||
err.note(format!(
|
||||
"associated type `{ty_param_name}` could derive from `{}`",
|
||||
"associated {assoc_kind_str} `{assoc_name}` could derive from `{}`",
|
||||
bound.print_only_trait_path(),
|
||||
));
|
||||
}
|
||||
@ -1220,46 +1226,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
Ok(bound)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, all_candidates, ty_name), ret)]
|
||||
fn one_bound_for_assoc_method(
|
||||
&self,
|
||||
all_candidates: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||
ty_name: impl Display,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
|
||||
let mut matching_candidates = all_candidates.filter(|r| {
|
||||
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Fn, assoc_name)
|
||||
});
|
||||
|
||||
let candidate = match matching_candidates.next() {
|
||||
Some(candidate) => candidate,
|
||||
None => {
|
||||
return Err(self.tcx().sess.emit_err(
|
||||
crate::errors::ReturnTypeNotationMissingMethod {
|
||||
span,
|
||||
ty_name: ty_name.to_string(),
|
||||
assoc_name: assoc_name.name,
|
||||
},
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(conflicting_candidate) = matching_candidates.next() {
|
||||
return Err(self.tcx().sess.emit_err(
|
||||
crate::errors::ReturnTypeNotationConflictingBound {
|
||||
span,
|
||||
ty_name: ty_name.to_string(),
|
||||
assoc_name: assoc_name.name,
|
||||
first_bound: candidate.print_only_trait_path(),
|
||||
second_bound: conflicting_candidate.print_only_trait_path(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
Ok(candidate)
|
||||
}
|
||||
|
||||
// Create a type from a path to an associated type or to an enum variant.
|
||||
// For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
|
||||
// and item_segment is the path segment for `D`. We return a type and a def for
|
||||
@ -1421,7 +1387,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
return Err(guar);
|
||||
};
|
||||
|
||||
self.one_bound_for_assoc_type(
|
||||
self.one_bound_for_assoc_item(
|
||||
|| {
|
||||
traits::supertraits(
|
||||
tcx,
|
||||
@ -1430,6 +1396,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
},
|
||||
kw::SelfUpper,
|
||||
None,
|
||||
ty::AssocKind::Type,
|
||||
assoc_ident,
|
||||
span,
|
||||
None,
|
||||
@ -1510,15 +1477,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
};
|
||||
|
||||
let trait_did = bound.def_id();
|
||||
let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did)
|
||||
else {
|
||||
// Assume that if it's not matched, there must be a const defined with the same name
|
||||
// but it was used in a type position.
|
||||
let msg = format!("found associated const `{assoc_ident}` when type was expected");
|
||||
let guar = tcx.sess.struct_span_err(span, msg).emit();
|
||||
return Err(guar);
|
||||
};
|
||||
|
||||
let assoc_ty_did = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap();
|
||||
let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound);
|
||||
|
||||
if let Some(variant_def_id) = variant_resolution {
|
||||
@ -1731,8 +1690,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
let tcx = self.tcx();
|
||||
let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block);
|
||||
|
||||
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
|
||||
// of calling `find_by_name_and_kind`.
|
||||
// We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
|
||||
// instead of calling `filter_by_name_and_kind` which would needlessly normalize the
|
||||
// `ident` again and again.
|
||||
let item = tcx.associated_items(scope).in_definition_order().find(|i| {
|
||||
i.kind.namespace() == Namespace::TypeNS
|
||||
&& i.ident(tcx).normalize_to_macros_2_0() == ident
|
||||
@ -2858,3 +2818,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
|
||||
fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
|
||||
match kind {
|
||||
ty::AssocKind::Fn => "function",
|
||||
ty::AssocKind::Const => "constant",
|
||||
ty::AssocKind::Type => "type",
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,121 @@ use rustc_errors::{
|
||||
MultiSpan,
|
||||
};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::{self, print::TraitRefPrintOnlyTraitPath, Ty};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::{symbol::Ident, Span, Symbol};
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_ambiguous_assoc_item)]
|
||||
pub struct AmbiguousAssocItem<'a> {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub assoc_kind: &'static str,
|
||||
pub assoc_name: Ident,
|
||||
pub ty_param_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_assoc_kind_mismatch)]
|
||||
pub struct AssocKindMismatch {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub expected: &'static str,
|
||||
pub got: &'static str,
|
||||
#[label(hir_analysis_expected_because_label)]
|
||||
pub expected_because_label: Option<Span>,
|
||||
pub assoc_kind: &'static str,
|
||||
#[note]
|
||||
pub def_span: Span,
|
||||
#[label(hir_analysis_bound_on_assoc_const_label)]
|
||||
pub bound_on_assoc_const_label: Option<Span>,
|
||||
#[subdiagnostic]
|
||||
pub wrap_in_braces_sugg: Option<AssocKindMismatchWrapInBracesSugg>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg,
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub struct AssocKindMismatchWrapInBracesSugg {
|
||||
#[suggestion_part(code = "{{ ")]
|
||||
pub lo: Span,
|
||||
#[suggestion_part(code = " }}")]
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_assoc_item_not_found, code = "E0220")]
|
||||
pub struct AssocItemNotFound<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub assoc_name: Ident,
|
||||
pub assoc_kind: &'static str,
|
||||
pub ty_param_name: &'a str,
|
||||
#[subdiagnostic]
|
||||
pub label: Option<AssocItemNotFoundLabel<'a>>,
|
||||
#[subdiagnostic]
|
||||
pub sugg: Option<AssocItemNotFoundSugg<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub enum AssocItemNotFoundLabel<'a> {
|
||||
#[label(hir_analysis_assoc_item_not_found_label)]
|
||||
NotFound {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[label(hir_analysis_assoc_item_not_found_found_in_other_trait_label)]
|
||||
FoundInOtherTrait {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
assoc_kind: &'static str,
|
||||
trait_name: &'a str,
|
||||
suggested_name: Symbol,
|
||||
identically_named: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
||||
pub enum AssocItemNotFoundSugg<'a> {
|
||||
#[suggestion(
|
||||
hir_analysis_assoc_item_not_found_similar_sugg,
|
||||
code = "{suggested_name}",
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
Similar {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
assoc_kind: &'static str,
|
||||
suggested_name: Symbol,
|
||||
},
|
||||
#[suggestion(
|
||||
hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg,
|
||||
code = "{suggested_name}",
|
||||
style = "verbose",
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
SimilarInOtherTrait {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
assoc_kind: &'static str,
|
||||
suggested_name: Symbol,
|
||||
},
|
||||
#[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")]
|
||||
Other {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
#[applicability]
|
||||
applicability: Applicability,
|
||||
ty_param_name: &'a str,
|
||||
assoc_kind: &'static str,
|
||||
suggested_name: Symbol,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_unrecognized_atomic_operation, code = "E0092")]
|
||||
pub struct UnrecognizedAtomicOperation<'a> {
|
||||
@ -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 @@ trait Trait {
|
||||
}
|
||||
|
||||
fn bar<T: Trait<methid(): Send>>() {}
|
||||
//~^ ERROR cannot find associated function `methid` for `Trait`
|
||||
//~^ ERROR associated function `methid` not found for `Trait`
|
||||
|
||||
fn main() {}
|
||||
|
@ -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