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:
@@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user