Rework point-at-arg
This commit is contained in:
parent
fb80d2bfe4
commit
c005e760f5
155 changed files with 1276 additions and 789 deletions
|
@ -740,12 +740,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
err.help("...or use `match` instead of `let...else`");
|
||||
}
|
||||
_ => {
|
||||
if let ObligationCauseCode::BindingObligation(_, binding_span) =
|
||||
cause.code().peel_derives()
|
||||
if let ObligationCauseCode::BindingObligation(_, span)
|
||||
| ObligationCauseCode::ExprBindingObligation(_, span, ..)
|
||||
= cause.code().peel_derives()
|
||||
&& let TypeError::RegionsPlaceholderMismatch = terr
|
||||
{
|
||||
if matches!(terr, TypeError::RegionsPlaceholderMismatch) {
|
||||
err.span_note(*binding_span, "the lifetime requirement is introduced here");
|
||||
}
|
||||
err.span_note(*span, "the lifetime requirement is introduced here");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
|||
let ObligationCauseCode::MatchImpl(parent, impl_def_id) = code else {
|
||||
return None;
|
||||
};
|
||||
let ObligationCauseCode::BindingObligation(_def_id, binding_span) = *parent.code() else {
|
||||
let (ObligationCauseCode::BindingObligation(_, binding_span) | ObligationCauseCode::ExprBindingObligation(_, binding_span, ..))
|
||||
= *parent.code() else {
|
||||
return None;
|
||||
};
|
||||
let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type");
|
||||
|
|
|
@ -211,7 +211,10 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
|
|||
);
|
||||
let mut err = self.tcx().sess.struct_span_err(span, &msg);
|
||||
|
||||
let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) = *cause.code() {
|
||||
let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id)
|
||||
| ObligationCauseCode::ExprItemObligation(def_id, ..) =
|
||||
*cause.code()
|
||||
{
|
||||
err.span_label(span, "doesn't satisfy where-clause");
|
||||
err.span_label(
|
||||
self.tcx().def_span(def_id),
|
||||
|
|
|
@ -232,7 +232,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
|||
ObligationCauseCode::MatchImpl(parent, ..) => parent.code(),
|
||||
_ => cause.code(),
|
||||
}
|
||||
&& let (&ObligationCauseCode::ItemObligation(item_def_id), None) = (code, override_error_code)
|
||||
&& let (&ObligationCauseCode::ItemObligation(item_def_id) | &ObligationCauseCode::ExprItemObligation(item_def_id, ..), None) = (code, override_error_code)
|
||||
{
|
||||
// Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
|
||||
// lifetime as above, but called using a fully-qualified path to the method:
|
||||
|
|
|
@ -390,10 +390,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
if matches!(
|
||||
&trace.cause.code().peel_derives(),
|
||||
ObligationCauseCode::BindingObligation(..)
|
||||
| ObligationCauseCode::ExprBindingObligation(..)
|
||||
) =>
|
||||
{
|
||||
// Hack to get around the borrow checker because trace.cause has an `Rc`.
|
||||
if let ObligationCauseCode::BindingObligation(_, span) =
|
||||
if let ObligationCauseCode::BindingObligation(_, span)
|
||||
| ObligationCauseCode::ExprBindingObligation(_, span, ..) =
|
||||
&trace.cause.code().peel_derives()
|
||||
{
|
||||
let span = *span;
|
||||
|
|
|
@ -97,7 +97,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
|||
cause.span,
|
||||
sup_type,
|
||||
match cause.code().peel_derives() {
|
||||
ObligationCauseCode::BindingObligation(_, span) => Some(*span),
|
||||
ObligationCauseCode::BindingObligation(_, span)
|
||||
| ObligationCauseCode::ExprBindingObligation(_, span, ..) => Some(*span),
|
||||
_ => None,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -238,9 +238,13 @@ pub enum ObligationCauseCode<'tcx> {
|
|||
/// also implement all supertraits of `X`.
|
||||
ItemObligation(DefId),
|
||||
|
||||
ExprItemObligation(DefId, rustc_hir::HirId, usize),
|
||||
|
||||
/// Like `ItemObligation`, but with extra detail on the source of the obligation.
|
||||
BindingObligation(DefId, Span),
|
||||
|
||||
ExprBindingObligation(DefId, Span, rustc_hir::HirId, usize),
|
||||
|
||||
/// A type like `&'a T` is WF only if `T: 'a`.
|
||||
ReferenceOutlivesReferent(Ty<'tcx>),
|
||||
|
||||
|
|
|
@ -1564,6 +1564,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
obligation.cause.code().peel_derives(),
|
||||
ObligationCauseCode::ItemObligation(_)
|
||||
| ObligationCauseCode::BindingObligation(_, _)
|
||||
| ObligationCauseCode::ExprItemObligation(..)
|
||||
| ObligationCauseCode::ExprBindingObligation(..)
|
||||
| ObligationCauseCode::ObjectCastObligation(..)
|
||||
| ObligationCauseCode::OpaqueType
|
||||
);
|
||||
|
@ -2091,13 +2093,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
if let ObligationCauseCode::ItemObligation(def_id) = *obligation.cause.code() {
|
||||
if let ObligationCauseCode::ItemObligation(def_id) | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code() {
|
||||
self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
|
||||
} else if let (
|
||||
Ok(ref snippet),
|
||||
&ObligationCauseCode::BindingObligation(def_id, _),
|
||||
) =
|
||||
(self.tcx.sess.source_map().span_to_snippet(span), obligation.cause.code())
|
||||
} else if let Ok(snippet) = &self.tcx.sess.source_map().span_to_snippet(span)
|
||||
&& let ObligationCauseCode::BindingObligation(def_id, _) | ObligationCauseCode::ExprBindingObligation(def_id, ..)
|
||||
= *obligation.cause.code()
|
||||
{
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
if generics.params.iter().any(|p| p.name != kw::SelfUpper)
|
||||
|
@ -2520,15 +2520,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
err: &mut Diagnostic,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
) {
|
||||
let (
|
||||
ty::PredicateKind::Trait(pred),
|
||||
&ObligationCauseCode::BindingObligation(item_def_id, span),
|
||||
) = (
|
||||
obligation.predicate.kind().skip_binder(),
|
||||
obligation.cause.code().peel_derives(),
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
let ty::PredicateKind::Trait(pred) = obligation.predicate.kind().skip_binder() else { return; };
|
||||
let (ObligationCauseCode::BindingObligation(item_def_id, span)
|
||||
| ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..))
|
||||
= *obligation.cause.code().peel_derives() else { return; };
|
||||
debug!(?pred, ?item_def_id, ?span);
|
||||
|
||||
let (Some(node), true) = (
|
||||
|
|
|
@ -143,7 +143,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
if let ObligationCauseCode::ItemObligation(item)
|
||||
| ObligationCauseCode::BindingObligation(item, _) = *obligation.cause.code()
|
||||
| ObligationCauseCode::BindingObligation(item, _)
|
||||
| ObligationCauseCode::ExprItemObligation(item, ..)
|
||||
| ObligationCauseCode::ExprBindingObligation(item, ..) = *obligation.cause.code()
|
||||
{
|
||||
// FIXME: maybe also have some way of handling methods
|
||||
// from other traits? That would require name resolution,
|
||||
|
|
|
@ -1022,7 +1022,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code {
|
||||
try_borrowing(cause.derived.parent_trait_pred, &[])
|
||||
} else if let ObligationCauseCode::BindingObligation(_, _)
|
||||
| ObligationCauseCode::ItemObligation(..) = code
|
||||
| ObligationCauseCode::ItemObligation(_)
|
||||
| ObligationCauseCode::ExprItemObligation(..)
|
||||
| ObligationCauseCode::ExprBindingObligation(..) = code
|
||||
{
|
||||
try_borrowing(poly_trait_pred, &never_suggest_borrow)
|
||||
} else {
|
||||
|
@ -2244,11 +2246,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
region, object_ty,
|
||||
));
|
||||
}
|
||||
ObligationCauseCode::ItemObligation(_item_def_id) => {
|
||||
ObligationCauseCode::ItemObligation(_)
|
||||
| ObligationCauseCode::ExprItemObligation(..) => {
|
||||
// We hold the `DefId` of the item introducing the obligation, but displaying it
|
||||
// doesn't add user usable information. It always point at an associated item.
|
||||
}
|
||||
ObligationCauseCode::BindingObligation(item_def_id, span) => {
|
||||
ObligationCauseCode::BindingObligation(item_def_id, span)
|
||||
| ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..) => {
|
||||
let item_name = tcx.def_path_str(item_def_id);
|
||||
let mut multispan = MultiSpan::from(span);
|
||||
if let Some(ident) = tcx.opt_item_ident(item_def_id) {
|
||||
|
|
|
@ -117,11 +117,21 @@ pub enum TraitQueryMode {
|
|||
|
||||
/// Creates predicate obligations from the generic bounds.
|
||||
pub fn predicates_for_generics<'tcx>(
|
||||
cause: ObligationCause<'tcx>,
|
||||
cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
generic_bounds: ty::InstantiatedPredicates<'tcx>,
|
||||
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
|
||||
util::predicates_for_generics(cause, 0, param_env, generic_bounds)
|
||||
let generic_bounds = generic_bounds;
|
||||
debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds);
|
||||
|
||||
std::iter::zip(generic_bounds.predicates, generic_bounds.spans).enumerate().map(
|
||||
move |(idx, (predicate, span))| Obligation {
|
||||
cause: cause(idx, span),
|
||||
recursion_depth: 0,
|
||||
param_env: param_env,
|
||||
predicate,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Determines whether the type `ty` is known to meet `bound` and
|
||||
|
|
|
@ -11,8 +11,6 @@ use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitable
|
|||
use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
|
||||
pub use rustc_infer::traits::{self, util::*};
|
||||
|
||||
use std::iter;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// `TraitAliasExpander` iterator
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -210,7 +208,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>(
|
|||
let Normalized { value: predicates, obligations: normalization_obligations2 } =
|
||||
super::normalize(selcx, param_env, ObligationCause::dummy(), predicates);
|
||||
let impl_obligations =
|
||||
predicates_for_generics(ObligationCause::dummy(), 0, param_env, predicates);
|
||||
super::predicates_for_generics(|_, _| ObligationCause::dummy(), param_env, predicates);
|
||||
|
||||
let impl_obligations = impl_obligations
|
||||
.chain(normalization_obligations1.into_iter())
|
||||
|
@ -219,27 +217,6 @@ pub fn impl_subject_and_oblig<'a, 'tcx>(
|
|||
(subject, impl_obligations)
|
||||
}
|
||||
|
||||
pub fn predicates_for_generics<'tcx>(
|
||||
cause: ObligationCause<'tcx>,
|
||||
recursion_depth: usize,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
generic_bounds: ty::InstantiatedPredicates<'tcx>,
|
||||
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
|
||||
debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds);
|
||||
|
||||
iter::zip(generic_bounds.predicates, generic_bounds.spans).map(move |(predicate, span)| {
|
||||
let cause = match *cause.code() {
|
||||
traits::ItemObligation(def_id) if !span.is_dummy() => traits::ObligationCause::new(
|
||||
cause.span,
|
||||
cause.body_id,
|
||||
traits::BindingObligation(def_id, span),
|
||||
),
|
||||
_ => cause.clone(),
|
||||
};
|
||||
Obligation { cause, recursion_depth, param_env, predicate }
|
||||
})
|
||||
}
|
||||
|
||||
pub fn predicate_for_trait_ref<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
|
|
|
@ -711,7 +711,7 @@ impl<'tcx> WfPredicates<'tcx> {
|
|||
iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev())
|
||||
.map(|((mut pred, span), origin_def_id)| {
|
||||
let code = if span.is_dummy() {
|
||||
traits::MiscObligation
|
||||
traits::ItemObligation(origin_def_id)
|
||||
} else {
|
||||
traits::BindingObligation(origin_def_id, span)
|
||||
};
|
||||
|
|
|
@ -1463,7 +1463,7 @@ pub fn check_type_bounds<'tcx>(
|
|||
);
|
||||
let mk_cause = |span: Span| {
|
||||
let code = if span.is_dummy() {
|
||||
traits::MiscObligation
|
||||
traits::ItemObligation(trait_ty.def_id)
|
||||
} else {
|
||||
traits::BindingObligation(trait_ty.def_id, span)
|
||||
};
|
||||
|
|
|
@ -607,9 +607,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(in super::super) fn select_all_obligations_or_error(&self) {
|
||||
let errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self);
|
||||
let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self);
|
||||
|
||||
if !errors.is_empty() {
|
||||
self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
|
||||
self.report_fulfillment_errors(&errors, self.inh.body_id, false);
|
||||
}
|
||||
}
|
||||
|
@ -623,6 +624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
|
||||
if !result.is_empty() {
|
||||
mutate_fulfillment_errors(&mut result);
|
||||
self.adjust_fulfillment_errors_for_expr_obligation(&mut result);
|
||||
self.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred);
|
||||
}
|
||||
}
|
||||
|
@ -820,23 +822,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let ty = item_ty.subst(self.tcx, substs);
|
||||
|
||||
self.write_resolution(hir_id, Ok((def_kind, def_id)));
|
||||
self.add_required_obligations_with_code(
|
||||
span,
|
||||
def_id,
|
||||
&substs,
|
||||
match lang_item {
|
||||
hir::LangItem::IntoFutureIntoFuture => {
|
||||
ObligationCauseCode::AwaitableExpr(expr_hir_id)
|
||||
}
|
||||
hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => {
|
||||
ObligationCauseCode::ForLoopIterator
|
||||
}
|
||||
hir::LangItem::TryTraitFromOutput
|
||||
| hir::LangItem::TryTraitFromResidual
|
||||
| hir::LangItem::TryTraitBranch => ObligationCauseCode::QuestionMark,
|
||||
_ => traits::ItemObligation(def_id),
|
||||
},
|
||||
);
|
||||
|
||||
let code = match lang_item {
|
||||
hir::LangItem::IntoFutureIntoFuture => {
|
||||
Some(ObligationCauseCode::AwaitableExpr(expr_hir_id))
|
||||
}
|
||||
hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => {
|
||||
Some(ObligationCauseCode::ForLoopIterator)
|
||||
}
|
||||
hir::LangItem::TryTraitFromOutput
|
||||
| hir::LangItem::TryTraitFromResidual
|
||||
| hir::LangItem::TryTraitBranch => Some(ObligationCauseCode::QuestionMark),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(code) = code {
|
||||
self.add_required_obligations_with_code(span, def_id, substs, move |_, _| code.clone());
|
||||
} else {
|
||||
self.add_required_obligations_for_hir(span, def_id, substs, hir_id);
|
||||
}
|
||||
|
||||
(Res::Def(def_kind, def_id), ty)
|
||||
}
|
||||
|
||||
|
@ -1348,7 +1352,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// First, store the "user substs" for later.
|
||||
self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
|
||||
|
||||
self.add_required_obligations(span, def_id, &substs);
|
||||
self.add_required_obligations_for_hir(span, def_id, &substs, hir_id);
|
||||
|
||||
// Substitute the values for the type parameters into the type of
|
||||
// the referenced item.
|
||||
|
@ -1385,32 +1389,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Add all the obligations that are required, substituting and normalized appropriately.
|
||||
pub(crate) fn add_required_obligations(
|
||||
pub(crate) fn add_required_obligations_for_hir(
|
||||
&self,
|
||||
span: Span,
|
||||
def_id: DefId,
|
||||
substs: &SubstsRef<'tcx>,
|
||||
substs: SubstsRef<'tcx>,
|
||||
hir_id: hir::HirId,
|
||||
) {
|
||||
self.add_required_obligations_with_code(
|
||||
span,
|
||||
def_id,
|
||||
substs,
|
||||
traits::ItemObligation(def_id),
|
||||
)
|
||||
self.add_required_obligations_with_code(span, def_id, substs, |idx, span| {
|
||||
if span.is_dummy() {
|
||||
ObligationCauseCode::ExprItemObligation(def_id, hir_id, idx)
|
||||
} else {
|
||||
ObligationCauseCode::ExprBindingObligation(def_id, span, hir_id, idx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, span, def_id, substs))]
|
||||
#[tracing::instrument(level = "debug", skip(self, code, span, def_id, substs))]
|
||||
fn add_required_obligations_with_code(
|
||||
&self,
|
||||
span: Span,
|
||||
def_id: DefId,
|
||||
substs: &SubstsRef<'tcx>,
|
||||
code: ObligationCauseCode<'tcx>,
|
||||
substs: SubstsRef<'tcx>,
|
||||
code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>,
|
||||
) {
|
||||
let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
|
||||
|
||||
for obligation in traits::predicates_for_generics(
|
||||
traits::ObligationCause::new(span, self.body_id, code),
|
||||
|idx, predicate_span| {
|
||||
traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span))
|
||||
},
|
||||
self.param_env,
|
||||
bounds,
|
||||
) {
|
||||
|
|
|
@ -247,17 +247,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Cause selection errors caused by resolving a single argument to point at the
|
||||
// argument and not the call. This lets us customize the span pointed to in the
|
||||
// fulfillment error to be more accurate.
|
||||
let coerced_ty =
|
||||
self.resolve_vars_with_obligations_and_mutate_fulfillment(coerced_ty, |errors| {
|
||||
self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr);
|
||||
self.point_at_arg_instead_of_call_if_possible(
|
||||
errors,
|
||||
call_expr,
|
||||
call_span,
|
||||
provided_args,
|
||||
&expected_input_tys,
|
||||
);
|
||||
});
|
||||
let coerced_ty = self.resolve_vars_with_obligations(coerced_ty);
|
||||
|
||||
let coerce_error = self
|
||||
.try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None)
|
||||
|
@ -312,16 +302,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// an "opportunistic" trait resolution of any trait bounds on
|
||||
// the call. This helps coercions.
|
||||
if check_closures {
|
||||
self.select_obligations_where_possible(false, |errors| {
|
||||
self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr);
|
||||
self.point_at_arg_instead_of_call_if_possible(
|
||||
errors,
|
||||
call_expr,
|
||||
call_span,
|
||||
&provided_args,
|
||||
&expected_input_tys,
|
||||
);
|
||||
})
|
||||
self.select_obligations_where_possible(false, |_| {})
|
||||
}
|
||||
|
||||
// Check each argument, to satisfy the input it was provided for
|
||||
|
@ -1183,7 +1164,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
|
||||
|
||||
// Check bounds on type arguments used in the path.
|
||||
self.add_required_obligations(path_span, did, substs);
|
||||
self.add_required_obligations_for_hir(path_span, did, substs, hir_id);
|
||||
|
||||
Some((variant, ty))
|
||||
} else {
|
||||
|
@ -1626,179 +1607,174 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// can be not easily comparable with predicate type (because of coercion). If the types match
|
||||
/// for either checked or coerced type, and there's only *one* argument that does, we point at
|
||||
/// the corresponding argument's expression span instead of the `fn` call path span.
|
||||
fn point_at_arg_instead_of_call_if_possible(
|
||||
pub(super) fn adjust_fulfillment_errors_for_expr_obligation(
|
||||
&self,
|
||||
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
call_sp: Span,
|
||||
args: &'tcx [hir::Expr<'tcx>],
|
||||
expected_tys: &[Ty<'tcx>],
|
||||
) {
|
||||
// We *do not* do this for desugared call spans to keep good diagnostics when involving
|
||||
// the `?` operator.
|
||||
if call_sp.desugaring_kind().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
'outer: for error in errors {
|
||||
// Only if the cause is somewhere inside the expression we want try to point at arg.
|
||||
// Otherwise, it means that the cause is somewhere else and we should not change
|
||||
// anything because we can break the correct span.
|
||||
if !call_sp.contains(error.obligation.cause.span) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Peel derived obligation, because it's the type that originally
|
||||
// started this inference chain that matters, not the one we wound
|
||||
// up with at the end.
|
||||
fn unpeel_to_top<'a, 'tcx>(
|
||||
mut code: &'a ObligationCauseCode<'tcx>,
|
||||
) -> &'a ObligationCauseCode<'tcx> {
|
||||
let mut result_code = code;
|
||||
loop {
|
||||
let parent = match code {
|
||||
ObligationCauseCode::ImplDerivedObligation(c) => &c.derived.parent_code,
|
||||
ObligationCauseCode::BuiltinDerivedObligation(c)
|
||||
| ObligationCauseCode::DerivedObligation(c) => &c.parent_code,
|
||||
_ => break result_code,
|
||||
};
|
||||
(result_code, code) = (code, parent);
|
||||
}
|
||||
}
|
||||
let self_: ty::subst::GenericArg<'_> =
|
||||
match unpeel_to_top(error.obligation.cause.code()) {
|
||||
ObligationCauseCode::BuiltinDerivedObligation(code)
|
||||
| ObligationCauseCode::DerivedObligation(code) => {
|
||||
code.parent_trait_pred.self_ty().skip_binder().into()
|
||||
}
|
||||
ObligationCauseCode::ImplDerivedObligation(code) => {
|
||||
code.derived.parent_trait_pred.self_ty().skip_binder().into()
|
||||
}
|
||||
_ => match error.obligation.predicate.kind().skip_binder() {
|
||||
ty::PredicateKind::Trait(predicate) => predicate.self_ty().into(),
|
||||
ty::PredicateKind::Projection(predicate) => {
|
||||
predicate.projection_ty.self_ty().into()
|
||||
}
|
||||
_ => continue,
|
||||
},
|
||||
};
|
||||
let self_ = self.resolve_vars_if_possible(self_);
|
||||
let ty_matches_self = |ty: Ty<'tcx>| ty.walk().any(|arg| arg == self_);
|
||||
|
||||
let typeck_results = self.typeck_results.borrow();
|
||||
|
||||
for (idx, arg) in args.iter().enumerate() {
|
||||
// Don't adjust the span if we already have a more precise span
|
||||
// within one of the args.
|
||||
if arg.span.contains(error.obligation.cause.span) {
|
||||
let references_arg =
|
||||
typeck_results.expr_ty_opt(arg).map_or(false, &ty_matches_self)
|
||||
|| expected_tys.get(idx).copied().map_or(false, &ty_matches_self);
|
||||
if references_arg && !arg.span.from_expansion() {
|
||||
error.obligation.cause.map_code(|parent_code| {
|
||||
ObligationCauseCode::FunctionArgumentObligation {
|
||||
arg_hir_id: args[idx].hir_id,
|
||||
call_hir_id: expr.hir_id,
|
||||
parent_code,
|
||||
}
|
||||
})
|
||||
}
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect the argument position for all arguments that could have caused this
|
||||
// `FulfillmentError`.
|
||||
let mut referenced_in: Vec<_> = std::iter::zip(expected_tys, args)
|
||||
.enumerate()
|
||||
.flat_map(|(idx, (expected_ty, arg))| {
|
||||
if let Some(arg_ty) = typeck_results.expr_ty_opt(arg) {
|
||||
vec![(idx, arg_ty), (idx, *expected_ty)]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
.filter_map(|(i, ty)| {
|
||||
let ty = self.resolve_vars_if_possible(ty);
|
||||
// We walk the argument type because the argument's type could have
|
||||
// been `Option<T>`, but the `FulfillmentError` references `T`.
|
||||
if ty_matches_self(ty) { Some(i) } else { None }
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Both checked and coerced types could have matched, thus we need to remove
|
||||
// duplicates.
|
||||
|
||||
// We sort primitive type usize here and can use unstable sort
|
||||
referenced_in.sort_unstable();
|
||||
referenced_in.dedup();
|
||||
|
||||
if let &[idx] = &referenced_in[..] {
|
||||
// Do not point at the inside of a macro.
|
||||
// That would often result in poor error messages.
|
||||
if args[idx].span.from_expansion() {
|
||||
continue;
|
||||
}
|
||||
// We make sure that only *one* argument matches the obligation failure
|
||||
// and we assign the obligation's span to its expression's.
|
||||
error.obligation.cause.span = args[idx].span;
|
||||
error.obligation.cause.map_code(|parent_code| {
|
||||
ObligationCauseCode::FunctionArgumentObligation {
|
||||
arg_hir_id: args[idx].hir_id,
|
||||
call_hir_id: expr.hir_id,
|
||||
parent_code,
|
||||
}
|
||||
});
|
||||
} else if error.obligation.cause.span == call_sp {
|
||||
// Make function calls point at the callee, not the whole thing.
|
||||
if let hir::ExprKind::Call(callee, _) = expr.kind {
|
||||
error.obligation.cause.span = callee.span;
|
||||
}
|
||||
}
|
||||
for error in errors {
|
||||
self.adjust_fulfillment_error_for_expr_obligation(error);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
|
||||
/// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
|
||||
/// were caused by them. If they were, we point at the corresponding type argument's span
|
||||
/// instead of the `fn` call path span.
|
||||
fn point_at_type_arg_instead_of_call_if_possible(
|
||||
fn adjust_fulfillment_error_for_expr_obligation(
|
||||
&self,
|
||||
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
error: &mut traits::FulfillmentError<'tcx>,
|
||||
) {
|
||||
if let hir::ExprKind::Call(path, _) = &call_expr.kind {
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &path.kind {
|
||||
for error in errors {
|
||||
let self_ty = match error.obligation.predicate.kind().skip_binder() {
|
||||
ty::PredicateKind::Trait(predicate) => predicate.self_ty(),
|
||||
ty::PredicateKind::Projection(predicate) => {
|
||||
predicate.projection_ty.self_ty()
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
// If any of the type arguments in this path segment caused the
|
||||
// `FulfillmentError`, point at its span (#61860).
|
||||
for arg in path
|
||||
.segments
|
||||
.iter()
|
||||
.filter_map(|seg| seg.args.as_ref())
|
||||
.flat_map(|a| a.args.iter())
|
||||
let (traits::ExprItemObligation(def_id, hir_id, idx) | traits::ExprBindingObligation(def_id, _, hir_id, idx))
|
||||
= *error.obligation.cause.code().peel_derives() else { return; };
|
||||
let Some(unsubstituted_pred) =
|
||||
self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx) else { return; };
|
||||
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
let predicate_substs = match unsubstituted_pred.kind().skip_binder() {
|
||||
ty::PredicateKind::Trait(pred) => pred.trait_ref.substs,
|
||||
ty::PredicateKind::Projection(pred) => pred.projection_ty.substs,
|
||||
_ => ty::List::empty(),
|
||||
};
|
||||
let param_to_point_at = predicate_substs.types().find_map(|ty| {
|
||||
ty.walk().find_map(|arg| {
|
||||
if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Param(param_ty) = ty.kind()
|
||||
// Look for a param ty that is local to this method/fn
|
||||
// and not inherited from an impl, for example.
|
||||
&& self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) == def_id
|
||||
{
|
||||
Some(arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let fallback_param_to_point_at = predicate_substs.types().find_map(|ty| {
|
||||
ty.walk().find_map(|arg| {
|
||||
if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Param(param_ty) = ty.kind()
|
||||
&& self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) != def_id
|
||||
{
|
||||
Some(arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let hir = self.tcx.hir();
|
||||
match hir.get(hir_id) {
|
||||
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Path(hir::QPath::Resolved(_, path)), hir_id, .. }) => {
|
||||
if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(callee, args), hir_id: call_hir_id, .. })
|
||||
= hir.get(hir.get_parent_node(*hir_id))
|
||||
&& callee.hir_id == *hir_id
|
||||
{
|
||||
if let Some(param_to_point_at) = param_to_point_at
|
||||
&& self.point_at_args_if_possible(error, def_id, param_to_point_at, *call_hir_id, callee.span, args) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(fallback_param_to_point_at) = fallback_param_to_point_at
|
||||
&& self.point_at_args_if_possible(error, def_id, fallback_param_to_point_at, *call_hir_id, callee.span, args)
|
||||
{
|
||||
if let hir::GenericArg::Type(hir_ty) = &arg
|
||||
&& let Some(ty) =
|
||||
self.typeck_results.borrow().node_type_opt(hir_ty.hir_id)
|
||||
&& self.resolve_vars_if_possible(ty) == self_ty
|
||||
{
|
||||
error.obligation.cause.span = hir_ty.span;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(param_to_point_at) = param_to_point_at
|
||||
&& let Some(segment) = path.segments.last()
|
||||
&& self.point_at_generics_if_possible(error, def_id, param_to_point_at, segment)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::MethodCall(segment, args, ..), .. }) => {
|
||||
if let Some(param_to_point_at) = param_to_point_at
|
||||
&& self.point_at_args_if_possible(error, def_id, param_to_point_at, hir_id, segment.ident.span, args)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(fallback_param_to_point_at) = fallback_param_to_point_at
|
||||
&& self.point_at_args_if_possible(error, def_id, fallback_param_to_point_at, hir_id, segment.ident.span, args)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(param_to_point_at) = param_to_point_at
|
||||
&& self.point_at_generics_if_possible(error, def_id, param_to_point_at, segment)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(..), .. }) => {
|
||||
// fixme
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn point_at_args_if_possible(
|
||||
&self,
|
||||
error: &mut traits::FulfillmentError<'tcx>,
|
||||
def_id: DefId,
|
||||
param_to_point_at: ty::GenericArg<'tcx>,
|
||||
call_hir_id: hir::HirId,
|
||||
callee_span: Span,
|
||||
args: &[hir::Expr<'tcx>],
|
||||
) -> bool {
|
||||
let sig = self.tcx.fn_sig(def_id).skip_binder();
|
||||
let args_referencing_param: Vec<_> = sig
|
||||
.inputs()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, ty)| ty.walk().any(|arg| arg == param_to_point_at))
|
||||
.collect();
|
||||
if let [(idx, _)] = args_referencing_param.as_slice()
|
||||
&& let Some(arg) = args.get(*idx)
|
||||
{
|
||||
error.obligation.cause.span = arg.span;
|
||||
error.obligation.cause.map_code(|parent_code| {
|
||||
ObligationCauseCode::FunctionArgumentObligation {
|
||||
arg_hir_id: arg.hir_id,
|
||||
call_hir_id,
|
||||
parent_code,
|
||||
}
|
||||
});
|
||||
true
|
||||
} else if args_referencing_param.len() > 0 {
|
||||
// If more than one argument applies, then point to the callee
|
||||
// We have chance to fix this up further in `point_at_generics_if_possible`
|
||||
error.obligation.cause.span = callee_span;
|
||||
false
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn point_at_generics_if_possible(
|
||||
&self,
|
||||
error: &mut traits::FulfillmentError<'tcx>,
|
||||
def_id: DefId,
|
||||
param_to_point_at: ty::GenericArg<'tcx>,
|
||||
segment: &hir::PathSegment<'tcx>,
|
||||
) -> bool {
|
||||
let own_substs = self
|
||||
.tcx
|
||||
.generics_of(def_id)
|
||||
.own_substs(ty::InternalSubsts::identity_for_item(self.tcx, def_id));
|
||||
let Some((index, _)) = own_substs
|
||||
.iter()
|
||||
.filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_)))
|
||||
.enumerate()
|
||||
.find(|(_, arg)| **arg == param_to_point_at) else { return false };
|
||||
let Some(arg) = segment
|
||||
.args()
|
||||
.args
|
||||
.iter()
|
||||
.filter(|arg| matches!(arg, hir::GenericArg::Type(_)))
|
||||
.nth(index) else { return false; };
|
||||
error.obligation.cause.span = arg.span();
|
||||
true
|
||||
}
|
||||
|
||||
fn label_fn_like(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
|
|
|
@ -491,7 +491,19 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
|||
// so we just call `predicates_for_generics` directly to avoid redoing work.
|
||||
// `self.add_required_obligations(self.span, def_id, &all_substs);`
|
||||
for obligation in traits::predicates_for_generics(
|
||||
traits::ObligationCause::new(self.span, self.body_id, traits::ItemObligation(def_id)),
|
||||
|idx, span| {
|
||||
let code = if span.is_dummy() {
|
||||
ObligationCauseCode::ExprItemObligation(def_id, self.call_expr.hir_id, idx)
|
||||
} else {
|
||||
ObligationCauseCode::ExprBindingObligation(
|
||||
def_id,
|
||||
span,
|
||||
self.call_expr.hir_id,
|
||||
idx,
|
||||
)
|
||||
};
|
||||
traits::ObligationCause::new(self.span, self.body_id, code)
|
||||
},
|
||||
self.param_env,
|
||||
method_predicates,
|
||||
) {
|
||||
|
|
|
@ -534,7 +534,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
} else {
|
||||
traits::ObligationCause::misc(span, self.body_id)
|
||||
};
|
||||
obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds));
|
||||
let predicates_cause = cause.clone();
|
||||
obligations.extend(traits::predicates_for_generics(
|
||||
move |_, _| predicates_cause.clone(),
|
||||
self.param_env,
|
||||
bounds,
|
||||
));
|
||||
|
||||
// Also add an obligation for the method type being well-formed.
|
||||
let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig));
|
||||
|
|
|
@ -1514,8 +1514,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds);
|
||||
|
||||
// Convert the bounds into obligations.
|
||||
let impl_obligations =
|
||||
traits::predicates_for_generics(cause, self.param_env, impl_bounds);
|
||||
let impl_obligations = traits::predicates_for_generics(
|
||||
move |_, _| cause.clone(),
|
||||
self.param_env,
|
||||
impl_bounds,
|
||||
);
|
||||
|
||||
let candidate_obligations = impl_obligations
|
||||
.chain(norm_obligations.into_iter())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue