Auto merge of #92441 - cjgillot:resolve-trait-impl-item, r=matthewjasper
Link impl items to corresponding trait items in late resolver. Hygienically linking trait impl items to declarations in the trait can be done directly by the late resolver. In fact, it is already done to diagnose unknown items. This PR uses this resolution work and stores the `DefId` of the trait item in the HIR. This avoids having to do this resolution manually later. r? `@matthewjasper` Related to #90639. The added `trait_item_id` field can be moved to `ImplItemRef` to be used directly by your PR.
This commit is contained in:
commit
ec4bcaac45
26 changed files with 478 additions and 515 deletions
|
@ -93,62 +93,96 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
span: Span,
|
||||
trait_def_id: DefId,
|
||||
trait_segment: &'_ hir::PathSegment<'_>,
|
||||
is_impl: bool,
|
||||
) {
|
||||
let trait_def = self.tcx().trait_def(trait_def_id);
|
||||
if self.tcx().features().unboxed_closures {
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.tcx().features().unboxed_closures
|
||||
&& trait_segment.args().parenthesized != trait_def.paren_sugar
|
||||
{
|
||||
let sess = &self.tcx().sess.parse_sess;
|
||||
let trait_def = self.tcx().trait_def(trait_def_id);
|
||||
if !trait_def.paren_sugar {
|
||||
if trait_segment.args().parenthesized {
|
||||
// For now, require that parenthetical notation be used only with `Fn()` etc.
|
||||
let mut err = feature_err(
|
||||
&self.tcx().sess.parse_sess,
|
||||
sym::unboxed_closures,
|
||||
span,
|
||||
"parenthetical notation is only stable when used with `Fn`-family traits",
|
||||
);
|
||||
err.emit();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let sess = self.tcx().sess;
|
||||
|
||||
if !trait_segment.args().parenthesized {
|
||||
// For now, require that parenthetical notation be used only with `Fn()` etc.
|
||||
let (msg, sugg) = if trait_def.paren_sugar {
|
||||
(
|
||||
"the precise format of `Fn`-family traits' type parameters is subject to \
|
||||
change",
|
||||
Some(format!(
|
||||
"{}{} -> {}",
|
||||
trait_segment.ident,
|
||||
trait_segment
|
||||
.args
|
||||
.as_ref()
|
||||
.and_then(|args| args.args.get(0))
|
||||
.and_then(|arg| match arg {
|
||||
hir::GenericArg::Type(ty) => match ty.kind {
|
||||
hir::TyKind::Tup(t) => t
|
||||
.iter()
|
||||
.map(|e| sess.source_map().span_to_snippet(e.span))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map(|a| a.join(", ")),
|
||||
_ => sess.source_map().span_to_snippet(ty.span),
|
||||
}
|
||||
.map(|s| format!("({})", s))
|
||||
.ok(),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_else(|| "()".to_string()),
|
||||
trait_segment
|
||||
.args()
|
||||
.bindings
|
||||
.iter()
|
||||
.find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
|
||||
(true, hir::TypeBindingKind::Equality { ty }) => {
|
||||
sess.source_map().span_to_snippet(ty.span).ok()
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_else(|| "()".to_string()),
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
("parenthetical notation is only stable when used with `Fn`-family traits", None)
|
||||
};
|
||||
let mut err = feature_err(sess, sym::unboxed_closures, span, msg);
|
||||
if let Some(sugg) = sugg {
|
||||
let msg = "use parenthetical notation instead";
|
||||
err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect);
|
||||
let mut err = feature_err(
|
||||
&sess.parse_sess,
|
||||
sym::unboxed_closures,
|
||||
span,
|
||||
"the precise format of `Fn`-family traits' type parameters is subject to change",
|
||||
);
|
||||
// Do not suggest the other syntax if we are in trait impl:
|
||||
// the desugaring would contain an associated type constrait.
|
||||
if !is_impl {
|
||||
let args = trait_segment
|
||||
.args
|
||||
.as_ref()
|
||||
.and_then(|args| args.args.get(0))
|
||||
.and_then(|arg| match arg {
|
||||
hir::GenericArg::Type(ty) => match ty.kind {
|
||||
hir::TyKind::Tup(t) => t
|
||||
.iter()
|
||||
.map(|e| sess.source_map().span_to_snippet(e.span))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map(|a| a.join(", ")),
|
||||
_ => sess.source_map().span_to_snippet(ty.span),
|
||||
}
|
||||
.map(|s| format!("({})", s))
|
||||
.ok(),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_else(|| "()".to_string());
|
||||
let ret = trait_segment
|
||||
.args()
|
||||
.bindings
|
||||
.iter()
|
||||
.find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
|
||||
(true, hir::TypeBindingKind::Equality { ty }) => {
|
||||
sess.source_map().span_to_snippet(ty.span).ok()
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_else(|| "()".to_string());
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"use parenthetical notation instead",
|
||||
format!("{}{} -> {}", trait_segment.ident, args, ret),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
|
||||
if is_impl {
|
||||
let trait_name = self.tcx().def_path_str(trait_def_id);
|
||||
struct_span_err!(
|
||||
self.tcx().sess,
|
||||
span,
|
||||
E0183,
|
||||
"manual implementations of `{}` are experimental",
|
||||
trait_name,
|
||||
)
|
||||
.span_label(
|
||||
span,
|
||||
format!("manual implementations of `{}` are experimental", trait_name),
|
||||
)
|
||||
.help("add `#![feature(unboxed_closures)]` to the crate attributes to enable")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn complain_about_assoc_type_not_found<I>(
|
||||
|
|
|
@ -669,6 +669,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()),
|
||||
self_ty,
|
||||
trait_ref.path.segments.last().unwrap(),
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -765,7 +766,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let infer_args = trait_segment.infer_args;
|
||||
|
||||
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1);
|
||||
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment);
|
||||
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
|
||||
|
||||
self.instantiate_poly_trait_ref_inner(
|
||||
hir_id,
|
||||
|
@ -822,9 +823,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_segment: &hir::PathSegment<'_>,
|
||||
is_impl: bool,
|
||||
) -> ty::TraitRef<'tcx> {
|
||||
let (substs, _) =
|
||||
self.create_substs_for_ast_trait_ref(span, trait_def_id, self_ty, trait_segment);
|
||||
let (substs, _) = self.create_substs_for_ast_trait_ref(
|
||||
span,
|
||||
trait_def_id,
|
||||
self_ty,
|
||||
trait_segment,
|
||||
is_impl,
|
||||
);
|
||||
let assoc_bindings = self.create_assoc_bindings_for_generic_args(trait_segment.args());
|
||||
if let Some(b) = assoc_bindings.first() {
|
||||
Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
|
||||
|
@ -839,8 +846,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_segment: &'a hir::PathSegment<'a>,
|
||||
is_impl: bool,
|
||||
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
|
||||
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment);
|
||||
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
|
||||
|
||||
self.create_substs_for_ast_path(
|
||||
span,
|
||||
|
@ -1932,7 +1940,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
|
||||
debug!("qpath_to_ty: self_type={:?}", self_ty);
|
||||
|
||||
let trait_ref = self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment);
|
||||
let trait_ref =
|
||||
self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false);
|
||||
|
||||
let item_substs = self.create_substs_for_associated_item(
|
||||
tcx,
|
||||
|
|
|
@ -371,7 +371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Trait must have a method named `m_name` and it should not have
|
||||
// type parameters or early-bound regions.
|
||||
let tcx = self.tcx;
|
||||
let method_item = match self.associated_item(trait_def_id, m_name, Namespace::ValueNS) {
|
||||
let method_item = match self.associated_value(trait_def_id, m_name) {
|
||||
Some(method_item) => method_item,
|
||||
None => {
|
||||
tcx.sess.delay_span_bug(
|
||||
|
@ -540,15 +540,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
/// Finds item with name `item_name` defined in impl/trait `def_id`
|
||||
/// and return it, or `None`, if no such item was defined there.
|
||||
pub fn associated_item(
|
||||
&self,
|
||||
def_id: DefId,
|
||||
item_name: Ident,
|
||||
ns: Namespace,
|
||||
) -> Option<ty::AssocItem> {
|
||||
pub fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option<ty::AssocItem> {
|
||||
self.tcx
|
||||
.associated_items(def_id)
|
||||
.find_by_name_and_namespace(self.tcx, item_name, ns, def_id)
|
||||
.find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id)
|
||||
.copied()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1915,7 +1915,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
.collect()
|
||||
} else {
|
||||
self.fcx
|
||||
.associated_item(def_id, name, Namespace::ValueNS)
|
||||
.associated_value(def_id, name)
|
||||
.map_or_else(SmallVec::new, |x| SmallVec::from_buf([x]))
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -5,7 +5,6 @@ use crate::check::FnCtxt;
|
|||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{ExprKind, Node, QPath};
|
||||
|
@ -99,16 +98,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
CandidateSource::ImplSource(impl_did) => {
|
||||
// Provide the best span we can. Use the item, if local to crate, else
|
||||
// the impl, if local to crate (item may be defaulted), else nothing.
|
||||
let item = match self
|
||||
.associated_item(impl_did, item_name, Namespace::ValueNS)
|
||||
.or_else(|| {
|
||||
let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?;
|
||||
self.associated_item(
|
||||
impl_trait_ref.def_id,
|
||||
item_name,
|
||||
Namespace::ValueNS,
|
||||
)
|
||||
}) {
|
||||
let item = match self.associated_value(impl_did, item_name).or_else(|| {
|
||||
let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?;
|
||||
self.associated_value(impl_trait_ref.def_id, item_name)
|
||||
}) {
|
||||
Some(item) => item,
|
||||
None => continue,
|
||||
};
|
||||
|
@ -187,11 +180,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
CandidateSource::TraitSource(trait_did) => {
|
||||
let item =
|
||||
match self.associated_item(trait_did, item_name, Namespace::ValueNS) {
|
||||
Some(item) => item,
|
||||
None => continue,
|
||||
};
|
||||
let item = match self.associated_value(trait_did, item_name) {
|
||||
Some(item) => item,
|
||||
None => continue,
|
||||
};
|
||||
let item_span = self
|
||||
.tcx
|
||||
.sess
|
||||
|
@ -271,16 +263,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Suggest clamping down the type if the method that is being attempted to
|
||||
// be used exists at all, and the type is an ambiguous numeric type
|
||||
// ({integer}/{float}).
|
||||
let mut candidates = all_traits(self.tcx).into_iter().filter_map(|info| {
|
||||
self.associated_item(info.def_id, item_name, Namespace::ValueNS)
|
||||
});
|
||||
let mut candidates = all_traits(self.tcx)
|
||||
.into_iter()
|
||||
.filter_map(|info| self.associated_value(info.def_id, item_name));
|
||||
// There are methods that are defined on the primitive types and won't be
|
||||
// found when exploring `all_traits`, but we also need them to be acurate on
|
||||
// our suggestions (#47759).
|
||||
let fund_assoc = |opt_def_id: Option<DefId>| {
|
||||
opt_def_id
|
||||
.and_then(|id| self.associated_item(id, item_name, Namespace::ValueNS))
|
||||
.is_some()
|
||||
opt_def_id.and_then(|id| self.associated_value(id, item_name)).is_some()
|
||||
};
|
||||
let lang_items = tcx.lang_items();
|
||||
let found_candidate = candidates.next().is_some()
|
||||
|
@ -398,11 +388,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.inherent_impls(adt_deref.did)
|
||||
.iter()
|
||||
.filter_map(|def_id| {
|
||||
self.associated_item(
|
||||
*def_id,
|
||||
item_name,
|
||||
Namespace::ValueNS,
|
||||
)
|
||||
self.associated_value(*def_id, item_name)
|
||||
})
|
||||
.count()
|
||||
>= 1
|
||||
|
@ -515,9 +501,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.iter()
|
||||
.copied()
|
||||
.filter(|def_id| {
|
||||
if let Some(assoc) =
|
||||
self.associated_item(*def_id, item_name, Namespace::ValueNS)
|
||||
{
|
||||
if let Some(assoc) = self.associated_value(*def_id, item_name) {
|
||||
// Check for both mode is the same so we avoid suggesting
|
||||
// incorrect associated item.
|
||||
match (mode, assoc.fn_has_self_parameter, source) {
|
||||
|
@ -1587,7 +1571,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}) && (type_is_local || info.def_id.is_local())
|
||||
&& self
|
||||
.associated_item(info.def_id, item_name, Namespace::ValueNS)
|
||||
.associated_value(info.def_id, item_name)
|
||||
.filter(|item| {
|
||||
if let ty::AssocKind::Fn = item.kind {
|
||||
let id = item
|
||||
|
|
|
@ -121,28 +121,6 @@ fn enforce_trait_manually_implementable(
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let trait_name = if did == li.fn_trait() {
|
||||
"Fn"
|
||||
} else if did == li.fn_mut_trait() {
|
||||
"FnMut"
|
||||
} else if did == li.fn_once_trait() {
|
||||
"FnOnce"
|
||||
} else {
|
||||
return; // everything OK
|
||||
};
|
||||
|
||||
let span = impl_header_span(tcx, impl_def_id);
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
span,
|
||||
E0183,
|
||||
"manual implementations of `{}` are experimental",
|
||||
trait_name
|
||||
)
|
||||
.span_label(span, format!("manual implementations of `{}` are experimental", trait_name))
|
||||
.help("add `#![feature(unboxed_closures)]` to the crate attributes to enable")
|
||||
.emit();
|
||||
}
|
||||
|
||||
/// We allow impls of marker traits to overlap, so they can't override impls
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue