Break up long function in trait selection error reporting

- Move blocks of code into their own functions
- Replace a few function argument types with their type aliases
This commit is contained in:
Bryan Garza
2023-04-19 14:59:36 -07:00
parent 409661936f
commit d0d40d2a40
6 changed files with 659 additions and 430 deletions
+1 -2
View File
@@ -300,8 +300,7 @@ pub(super) fn obligation_for_method(
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_types: Option<&[Ty<'tcx>]>,
) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
{
) -> (traits::PredicateObligation<'tcx>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>) {
// Construct a trait-reference `self_ty : Trait<input_tys>`
let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
match param.kind {
+1 -1
View File
@@ -123,7 +123,7 @@ pub struct FulfillmentError<'tcx> {
#[derive(Clone)]
pub enum FulfillmentErrorCode<'tcx> {
/// Inherently impossible to fulfill; this trait is implemented if and only if it is already implemented.
CodeCycle(Vec<Obligation<'tcx, ty::Predicate<'tcx>>>),
CodeCycle(Vec<PredicateObligation<'tcx>>),
CodeSelectionError(SelectionError<'tcx>),
CodeProjectionError(MismatchedProjectionTypes<'tcx>),
CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
@@ -591,7 +591,7 @@ fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) ->
fn evaluate_nested_obligations(
&self,
ty: Ty<'_>,
nested: impl Iterator<Item = Obligation<'tcx, ty::Predicate<'tcx>>>,
nested: impl Iterator<Item = PredicateObligation<'tcx>>,
computed_preds: &mut FxIndexSet<ty::Predicate<'tcx>>,
fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
predicates: &mut VecDeque<ty::PolyTraitPredicate<'tcx>>,
@@ -713,29 +713,13 @@ fn report_selection_error(
(message, note, append_const_msg)
};
let err_msg = message
.and_then(|cannot_do_this| {
match (predicate_is_const, append_const_msg) {
// do nothing if predicate is not const
(false, _) => Some(cannot_do_this),
// suggested using default post message
(true, Some(None)) => {
Some(format!("{cannot_do_this} in const contexts"))
}
// overridden post message
(true, Some(Some(post_message))) => {
Some(format!("{cannot_do_this}{post_message}"))
}
// fallback to generic message
(true, None) => None,
}
})
.unwrap_or_else(|| {
format!(
"the trait bound `{}` is not satisfied{}",
trait_predicate, post_message,
)
});
let err_msg = self.get_standard_error_message(
&trait_predicate,
message,
predicate_is_const,
append_const_msg,
post_message,
);
let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id())
== self.tcx.lang_items().transmute_trait()
@@ -763,22 +747,10 @@ fn report_selection_error(
}
if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() {
match obligation.cause.code().peel_derives() {
ObligationCauseCode::RustCall => {
err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument");
}
ObligationCauseCode::BindingObligation(def_id, _)
| ObligationCauseCode::ItemObligation(def_id)
if tcx.is_fn_trait(*def_id) =>
{
err.code(rustc_errors::error_code!(E0059));
err.set_primary_message(format!(
"type parameter to bare `{}` trait must be a tuple",
tcx.def_path_str(*def_id)
));
}
_ => {}
}
self.add_tuple_trait_message(
&obligation.cause.code().peel_derives(),
&mut err,
);
}
if Some(trait_ref.def_id()) == tcx.lang_items().drop_trait()
@@ -788,33 +760,13 @@ fn report_selection_error(
err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details");
}
let explanation = if let ObligationCauseCode::MainFunctionType =
obligation.cause.code()
{
"consider using `()`, or a `Result`".to_owned()
} else {
let ty_desc = match trait_ref.skip_binder().self_ty().kind() {
ty::FnDef(_, _) => Some("fn item"),
ty::Closure(_, _) => Some("closure"),
_ => None,
};
let explanation = get_explanation_based_on_obligation(
&obligation,
trait_ref,
&trait_predicate,
pre_message,
);
match ty_desc {
Some(desc) => format!(
"{}the trait `{}` is not implemented for {} `{}`",
pre_message,
trait_predicate.print_modifiers_and_trait_path(),
desc,
trait_ref.skip_binder().self_ty(),
),
None => format!(
"{}the trait `{}` is not implemented for `{}`",
pre_message,
trait_predicate.print_modifiers_and_trait_path(),
trait_ref.skip_binder().self_ty(),
),
}
};
self.check_for_binding_assigned_block_without_tail_expression(
&obligation,
&mut err,
@@ -851,28 +803,14 @@ fn report_selection_error(
self.suggest_borrowing_for_object_cast(&mut err, &root_obligation, *concrete_ty, *obj_ty);
}
let mut unsatisfied_const = false;
if trait_predicate.is_const_if_const() && obligation.param_env.is_const() {
let non_const_predicate = trait_ref.without_const();
let non_const_obligation = Obligation {
cause: obligation.cause.clone(),
param_env: obligation.param_env.without_const(),
predicate: non_const_predicate.to_predicate(tcx),
recursion_depth: obligation.recursion_depth,
};
if self.predicate_may_hold(&non_const_obligation) {
unsatisfied_const = true;
err.span_note(
span,
&format!(
"the trait `{}` is implemented for `{}`, \
but that implementation is not `const`",
non_const_predicate.print_modifiers_and_trait_path(),
trait_ref.skip_binder().self_ty(),
),
);
}
}
let UnsatisfiedConst(unsatisfied_const) = self
.maybe_add_note_for_unsatisfied_const(
&obligation,
trait_ref,
&trait_predicate,
&mut err,
span,
);
if let Some((msg, span)) = type_def {
err.span_label(span, &msg);
@@ -970,137 +908,16 @@ fn report_selection_error(
);
}
let body_def_id = obligation.cause.body_id;
// Try to report a help message
if is_fn_trait
&& let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
obligation.param_env,
trait_ref.self_ty(),
trait_predicate.skip_binder().constness,
trait_predicate.skip_binder().polarity,
)
{
// If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following
// suggestion to add trait bounds for the type, since we only typically implement
// these traits once.
// Note if the `FnMut` or `FnOnce` is less general than the trait we're trying
// to implement.
let selected_kind =
self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id())
.expect("expected to map DefId to ClosureKind");
if !implemented_kind.extends(selected_kind) {
err.note(
&format!(
"`{}` implements `{}`, but it must implement `{}`, which is more general",
trait_ref.skip_binder().self_ty(),
implemented_kind,
selected_kind
)
);
}
// Note any argument mismatches
let given_ty = params.skip_binder();
let expected_ty = trait_ref.skip_binder().substs.type_at(1);
if let ty::Tuple(given) = given_ty.kind()
&& let ty::Tuple(expected) = expected_ty.kind()
{
if expected.len() != given.len() {
// Note number of types that were expected and given
err.note(
&format!(
"expected a closure taking {} argument{}, but one taking {} argument{} was given",
given.len(),
pluralize!(given.len()),
expected.len(),
pluralize!(expected.len()),
)
);
} else if !self.same_type_modulo_infer(given_ty, expected_ty) {
// Print type mismatch
let (expected_args, given_args) =
self.cmp(given_ty, expected_ty);
err.note_expected_found(
&"a closure with arguments",
expected_args,
&"a closure with arguments",
given_args,
);
}
}
} else if !trait_ref.has_non_region_infer()
&& self.predicate_can_apply(obligation.param_env, trait_predicate)
{
// If a where-clause may be useful, remind the
// user that they can add it.
//
// don't display an on-unimplemented note, as
// these notes will often be of the form
// "the type `T` can't be frobnicated"
// which is somewhat confusing.
self.suggest_restricting_param_bound(
&mut err,
trait_predicate,
None,
obligation.cause.body_id,
);
} else if !suggested && !unsatisfied_const {
// Can't show anything else useful, try to find similar impls.
let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
if !self.report_similar_impl_candidates(
&impl_candidates,
trait_ref,
body_def_id,
&mut err,
true,
) {
// This is *almost* equivalent to
// `obligation.cause.code().peel_derives()`, but it gives us the
// trait predicate for that corresponding root obligation. This
// lets us get a derived obligation from a type parameter, like
// when calling `string.strip_suffix(p)` where `p` is *not* an
// implementer of `Pattern<'_>`.
let mut code = obligation.cause.code();
let mut trait_pred = trait_predicate;
let mut peeled = false;
while let Some((parent_code, parent_trait_pred)) = code.parent() {
code = parent_code;
if let Some(parent_trait_pred) = parent_trait_pred {
trait_pred = parent_trait_pred;
peeled = true;
}
}
let def_id = trait_pred.def_id();
// Mention *all* the `impl`s for the *top most* obligation, the
// user might have meant to use one of them, if any found. We skip
// auto-traits or fundamental traits that might not be exactly what
// the user might expect to be presented with. Instead this is
// useful for less general traits.
if peeled
&& !self.tcx.trait_is_auto(def_id)
&& !self.tcx.lang_items().iter().any(|(_, id)| id == def_id)
{
let trait_ref = trait_pred.to_poly_trait_ref();
let impl_candidates =
self.find_similar_impl_candidates(trait_pred);
self.report_similar_impl_candidates(
&impl_candidates,
trait_ref,
body_def_id,
&mut err,
true,
);
}
}
self.maybe_suggest_convert_to_slice(
&mut err,
trait_ref,
impl_candidates.as_slice(),
span,
);
}
self.try_to_add_help_message(
&obligation,
trait_ref,
&trait_predicate,
&mut err,
span,
is_fn_trait,
suggested,
unsatisfied_const,
);
// Changing mutability doesn't make a difference to whether we have
// an `Unsize` impl (Fixes ICE in #71036)
@@ -1195,59 +1012,7 @@ fn report_selection_error(
ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => {
let found_kind = self.closure_kind(closure_substs).unwrap();
let closure_span = self.tcx.def_span(closure_def_id);
let mut err = struct_span_err!(
self.tcx.sess,
closure_span,
E0525,
"expected a closure that implements the `{}` trait, \
but this closure only implements `{}`",
kind,
found_kind
);
err.span_label(
closure_span,
format!("this closure implements `{}`, not `{}`", found_kind, kind),
);
err.span_label(
obligation.cause.span,
format!("the requirement to implement `{}` derives from here", kind),
);
// Additional context information explaining why the closure only implements
// a particular trait.
if let Some(typeck_results) = &self.typeck_results {
let hir_id = self
.tcx
.hir()
.local_def_id_to_hir_id(closure_def_id.expect_local());
match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) {
(ty::ClosureKind::FnOnce, Some((span, place))) => {
err.span_label(
*span,
format!(
"closure is `FnOnce` because it moves the \
variable `{}` out of its environment",
ty::place_to_string_for_capture(tcx, place)
),
);
}
(ty::ClosureKind::FnMut, Some((span, place))) => {
err.span_label(
*span,
format!(
"closure is `FnMut` because it mutates the \
variable `{}` here",
ty::place_to_string_for_capture(tcx, place)
),
);
}
_ => {}
}
}
err
self.report_closure_error(&obligation, closure_def_id, found_kind, kind)
}
ty::PredicateKind::WellFormed(ty) => {
@@ -1328,117 +1093,21 @@ fn report_selection_error(
found_trait_ref,
expected_trait_ref,
terr @ TypeError::CyclicTy(_),
) => {
let self_ty = found_trait_ref.self_ty().skip_binder();
let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() {
(
ObligationCause::dummy_with_span(tcx.def_span(def_id)),
TypeError::CyclicTy(self_ty),
)
} else {
(obligation.cause.clone(), terr)
};
self.report_and_explain_type_error(
TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref),
terr,
)
}
) => self.report_type_parameter_mismatch_cyclic_type_error(
&obligation,
found_trait_ref,
expected_trait_ref,
terr,
),
OutputTypeParameterMismatch(found_trait_ref, expected_trait_ref, _) => {
let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref);
let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref);
if expected_trait_ref.self_ty().references_error() {
return;
}
let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else {
return;
};
let found_did = match *found_trait_ty.kind() {
ty::Closure(did, _)
| ty::Foreign(did)
| ty::FnDef(did, _)
| ty::Generator(did, ..) => Some(did),
ty::Adt(def, _) => Some(def.did()),
_ => None,
};
let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did));
let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did));
if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) {
// We check closures twice, with obligations flowing in different directions,
// but we want to complain about them only once.
return;
}
self.reported_closure_mismatch.borrow_mut().insert((span, found_span));
let mut not_tupled = false;
let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() {
ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()],
_ => {
not_tupled = true;
vec![ArgKind::empty()]
}
};
let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1);
let expected = match expected_ty.kind() {
ty::Tuple(ref tys) => {
tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect()
}
_ => {
not_tupled = true;
vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())]
}
};
// If this is a `Fn` family trait and either the expected or found
// is not tupled, then fall back to just a regular mismatch error.
// This shouldn't be common unless manually implementing one of the
// traits manually, but don't make it more confusing when it does
// happen.
if Some(expected_trait_ref.def_id()) != tcx.lang_items().gen_trait() && not_tupled {
self.report_and_explain_type_error(
TypeTrace::poly_trait_refs(
&obligation.cause,
true,
expected_trait_ref,
found_trait_ref,
),
ty::error::TypeError::Mismatch,
)
} else if found.len() == expected.len() {
self.report_closure_arg_mismatch(
span,
found_span,
found_trait_ref,
expected_trait_ref,
obligation.cause.code(),
found_node,
obligation.param_env,
)
} else {
let (closure_span, closure_arg_span, found) = found_did
.and_then(|did| {
let node = self.tcx.hir().get_if_local(did)?;
let (found_span, closure_arg_span, found) =
self.get_fn_like_arguments(node)?;
Some((Some(found_span), closure_arg_span, found))
})
.unwrap_or((found_span, None, found));
self.report_arg_count_mismatch(
span,
closure_span,
expected,
found,
found_trait_ty.is_closure(),
closure_arg_span,
)
match self.report_type_parameter_mismatch_error(
&obligation,
span,
found_trait_ref,
expected_trait_ref,
) {
Some(err) => err,
None => return,
}
}
@@ -1453,45 +1122,9 @@ fn report_selection_error(
)
}
SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => {
if !self.tcx.features().generic_const_exprs {
let mut err = self.tcx.sess.struct_span_err(
span,
"constant expression depends on a generic parameter",
);
// FIXME(const_generics): we should suggest to the user how they can resolve this
// issue. However, this is currently not actually possible
// (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
//
// Note that with `feature(generic_const_exprs)` this case should not
// be reachable.
err.note("this may fail depending on what value the parameter takes");
err.emit();
return;
}
match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(ct) => {
let ty::ConstKind::Unevaluated(uv) = ct.kind() else {
bug!("const evaluatable failed for non-unevaluated const `{ct:?}`");
};
let mut err =
self.tcx.sess.struct_span_err(span, "unconstrained generic constant");
let const_span = self.tcx.def_span(uv.def);
match self.tcx.sess.source_map().span_to_snippet(const_span) {
Ok(snippet) => err.help(&format!(
"try adding a `where` bound using this expression: `where [(); {}]:`",
snippet
)),
_ => err.help("consider adding a `where` bound using this expression"),
};
err
}
_ => {
span_bug!(
span,
"unexpected non-ConstEvaluatable predicate, this should not be reachable"
)
}
match self.report_not_const_evaluatable_error(&obligation, span) {
Some(err) => err,
None => return,
}
}
@@ -1563,6 +1196,14 @@ fn report_similar_impl_candidates(
other: bool,
) -> bool;
fn report_similar_impl_candidates_for_root_obligation(
&self,
obligation: &PredicateObligation<'tcx>,
trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
body_def_id: LocalDefId,
err: &mut Diagnostic,
);
/// Gets the parent trait chain start
fn get_parent_trait_ref(
&self,
@@ -1626,12 +1267,86 @@ fn is_recursive_obligation(
cause_code: &ObligationCauseCode<'tcx>,
) -> bool;
fn get_standard_error_message(
&self,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
message: Option<String>,
predicate_is_const: bool,
append_const_msg: Option<Option<rustc_span::Symbol>>,
post_message: String,
) -> String;
fn get_safe_transmute_error_and_reason(
&self,
obligation: Obligation<'tcx, ty::Predicate<'tcx>>,
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
obligation: PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
) -> (String, Option<String>);
fn add_tuple_trait_message(
&self,
obligation_cause_code: &ObligationCauseCode<'tcx>,
err: &mut Diagnostic,
);
fn try_to_add_help_message(
&self,
obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
err: &mut Diagnostic,
span: Span,
is_fn_trait: bool,
suggested: bool,
unsatisfied_const: bool,
);
fn add_help_message_for_fn_trait(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
err: &mut Diagnostic,
implemented_kind: ty::ClosureKind,
params: ty::Binder<'tcx, Ty<'tcx>>,
);
fn maybe_add_note_for_unsatisfied_const(
&self,
obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
err: &mut Diagnostic,
span: Span,
) -> UnsatisfiedConst;
fn report_closure_error(
&self,
obligation: &PredicateObligation<'tcx>,
closure_def_id: DefId,
found_kind: ty::ClosureKind,
kind: ty::ClosureKind,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
fn report_type_parameter_mismatch_cyclic_type_error(
&self,
obligation: &PredicateObligation<'tcx>,
found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
terr: TypeError<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
fn report_type_parameter_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
span: Span,
found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>;
fn report_not_const_evaluatable_error(
&self,
obligation: &PredicateObligation<'tcx>,
span: Span,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>;
}
impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
@@ -2195,6 +1910,51 @@ fn report_similar_impl_candidates(
report(normalized_impl_candidates, err)
}
fn report_similar_impl_candidates_for_root_obligation(
&self,
obligation: &PredicateObligation<'tcx>,
trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
body_def_id: LocalDefId,
err: &mut Diagnostic,
) {
// This is *almost* equivalent to
// `obligation.cause.code().peel_derives()`, but it gives us the
// trait predicate for that corresponding root obligation. This
// lets us get a derived obligation from a type parameter, like
// when calling `string.strip_suffix(p)` where `p` is *not* an
// implementer of `Pattern<'_>`.
let mut code = obligation.cause.code();
let mut trait_pred = trait_predicate;
let mut peeled = false;
while let Some((parent_code, parent_trait_pred)) = code.parent() {
code = parent_code;
if let Some(parent_trait_pred) = parent_trait_pred {
trait_pred = parent_trait_pred;
peeled = true;
}
}
let def_id = trait_pred.def_id();
// Mention *all* the `impl`s for the *top most* obligation, the
// user might have meant to use one of them, if any found. We skip
// auto-traits or fundamental traits that might not be exactly what
// the user might expect to be presented with. Instead this is
// useful for less general traits.
if peeled
&& !self.tcx.trait_is_auto(def_id)
&& !self.tcx.lang_items().iter().any(|(_, id)| id == def_id)
{
let trait_ref = trait_pred.to_poly_trait_ref();
let impl_candidates = self.find_similar_impl_candidates(trait_pred);
self.report_similar_impl_candidates(
&impl_candidates,
trait_ref,
body_def_id,
err,
true,
);
}
}
/// Gets the parent trait chain start
fn get_parent_trait_ref(
&self,
@@ -2917,10 +2677,38 @@ fn is_recursive_obligation(
false
}
fn get_standard_error_message(
&self,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
message: Option<String>,
predicate_is_const: bool,
append_const_msg: Option<Option<rustc_span::Symbol>>,
post_message: String,
) -> String {
message
.and_then(|cannot_do_this| {
match (predicate_is_const, append_const_msg) {
// do nothing if predicate is not const
(false, _) => Some(cannot_do_this),
// suggested using default post message
(true, Some(None)) => Some(format!("{cannot_do_this} in const contexts")),
// overridden post message
(true, Some(Some(post_message))) => {
Some(format!("{cannot_do_this}{post_message}"))
}
// fallback to generic message
(true, None) => None,
}
})
.unwrap_or_else(|| {
format!("the trait bound `{}` is not satisfied{}", trait_predicate, post_message,)
})
}
fn get_safe_transmute_error_and_reason(
&self,
obligation: Obligation<'tcx, ty::Predicate<'tcx>>,
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
obligation: PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
) -> (String, Option<String>) {
// Erase regions because layout code doesn't particularly care about regions.
@@ -2978,8 +2766,449 @@ fn get_safe_transmute_error_and_reason(
_ => span_bug!(span, "Unsupported rustc_transmute::Reason variant"),
}
}
fn add_tuple_trait_message(
&self,
obligation_cause_code: &ObligationCauseCode<'tcx>,
err: &mut Diagnostic,
) {
match obligation_cause_code {
ObligationCauseCode::RustCall => {
err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument");
}
ObligationCauseCode::BindingObligation(def_id, _)
| ObligationCauseCode::ItemObligation(def_id)
if self.tcx.is_fn_trait(*def_id) =>
{
err.code(rustc_errors::error_code!(E0059));
err.set_primary_message(format!(
"type parameter to bare `{}` trait must be a tuple",
self.tcx.def_path_str(*def_id)
));
}
_ => {}
}
}
fn try_to_add_help_message(
&self,
obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
err: &mut Diagnostic,
span: Span,
is_fn_trait: bool,
suggested: bool,
unsatisfied_const: bool,
) {
let body_def_id = obligation.cause.body_id;
// Try to report a help message
if is_fn_trait
&& let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
obligation.param_env,
trait_ref.self_ty(),
trait_predicate.skip_binder().constness,
trait_predicate.skip_binder().polarity,
)
{
self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params);
} else if !trait_ref.has_non_region_infer()
&& self.predicate_can_apply(obligation.param_env, *trait_predicate)
{
// If a where-clause may be useful, remind the
// user that they can add it.
//
// don't display an on-unimplemented note, as
// these notes will often be of the form
// "the type `T` can't be frobnicated"
// which is somewhat confusing.
self.suggest_restricting_param_bound(
err,
*trait_predicate,
None,
obligation.cause.body_id,
);
} else if !suggested && !unsatisfied_const {
// Can't show anything else useful, try to find similar impls.
let impl_candidates = self.find_similar_impl_candidates(*trait_predicate);
if !self.report_similar_impl_candidates(
&impl_candidates,
trait_ref,
body_def_id,
err,
true,
) {
self.report_similar_impl_candidates_for_root_obligation(&obligation, *trait_predicate, body_def_id, err);
}
self.maybe_suggest_convert_to_slice(
err,
trait_ref,
impl_candidates.as_slice(),
span,
);
}
}
fn add_help_message_for_fn_trait(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
err: &mut Diagnostic,
implemented_kind: ty::ClosureKind,
params: ty::Binder<'tcx, Ty<'tcx>>,
) {
// If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following
// suggestion to add trait bounds for the type, since we only typically implement
// these traits once.
// Note if the `FnMut` or `FnOnce` is less general than the trait we're trying
// to implement.
let selected_kind = self
.tcx
.fn_trait_kind_from_def_id(trait_ref.def_id())
.expect("expected to map DefId to ClosureKind");
if !implemented_kind.extends(selected_kind) {
err.note(&format!(
"`{}` implements `{}`, but it must implement `{}`, which is more general",
trait_ref.skip_binder().self_ty(),
implemented_kind,
selected_kind
));
}
// Note any argument mismatches
let given_ty = params.skip_binder();
let expected_ty = trait_ref.skip_binder().substs.type_at(1);
if let ty::Tuple(given) = given_ty.kind()
&& let ty::Tuple(expected) = expected_ty.kind()
{
if expected.len() != given.len() {
// Note number of types that were expected and given
err.note(
&format!(
"expected a closure taking {} argument{}, but one taking {} argument{} was given",
given.len(),
pluralize!(given.len()),
expected.len(),
pluralize!(expected.len()),
)
);
} else if !self.same_type_modulo_infer(given_ty, expected_ty) {
// Print type mismatch
let (expected_args, given_args) =
self.cmp(given_ty, expected_ty);
err.note_expected_found(
&"a closure with arguments",
expected_args,
&"a closure with arguments",
given_args,
);
}
}
}
fn maybe_add_note_for_unsatisfied_const(
&self,
obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
err: &mut Diagnostic,
span: Span,
) -> UnsatisfiedConst {
let mut unsatisfied_const = UnsatisfiedConst(false);
if trait_predicate.is_const_if_const() && obligation.param_env.is_const() {
let non_const_predicate = trait_ref.without_const();
let non_const_obligation = Obligation {
cause: obligation.cause.clone(),
param_env: obligation.param_env.without_const(),
predicate: non_const_predicate.to_predicate(self.tcx),
recursion_depth: obligation.recursion_depth,
};
if self.predicate_may_hold(&non_const_obligation) {
unsatisfied_const = UnsatisfiedConst(true);
err.span_note(
span,
&format!(
"the trait `{}` is implemented for `{}`, \
but that implementation is not `const`",
non_const_predicate.print_modifiers_and_trait_path(),
trait_ref.skip_binder().self_ty(),
),
);
}
}
unsatisfied_const
}
fn report_closure_error(
&self,
obligation: &PredicateObligation<'tcx>,
closure_def_id: DefId,
found_kind: ty::ClosureKind,
kind: ty::ClosureKind,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let closure_span = self.tcx.def_span(closure_def_id);
let mut err = struct_span_err!(
self.tcx.sess,
closure_span,
E0525,
"expected a closure that implements the `{}` trait, \
but this closure only implements `{}`",
kind,
found_kind
);
err.span_label(
closure_span,
format!("this closure implements `{}`, not `{}`", found_kind, kind),
);
err.span_label(
obligation.cause.span,
format!("the requirement to implement `{}` derives from here", kind),
);
// Additional context information explaining why the closure only implements
// a particular trait.
if let Some(typeck_results) = &self.typeck_results {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local());
match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) {
(ty::ClosureKind::FnOnce, Some((span, place))) => {
err.span_label(
*span,
format!(
"closure is `FnOnce` because it moves the \
variable `{}` out of its environment",
ty::place_to_string_for_capture(self.tcx, place)
),
);
}
(ty::ClosureKind::FnMut, Some((span, place))) => {
err.span_label(
*span,
format!(
"closure is `FnMut` because it mutates the \
variable `{}` here",
ty::place_to_string_for_capture(self.tcx, place)
),
);
}
_ => {}
}
}
err
}
fn report_type_parameter_mismatch_cyclic_type_error(
&self,
obligation: &PredicateObligation<'tcx>,
found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
terr: TypeError<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let self_ty = found_trait_ref.self_ty().skip_binder();
let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() {
(
ObligationCause::dummy_with_span(self.tcx.def_span(def_id)),
TypeError::CyclicTy(self_ty),
)
} else {
(obligation.cause.clone(), terr)
};
self.report_and_explain_type_error(
TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref),
terr,
)
}
fn report_type_parameter_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
span: Span,
found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref);
let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref);
if expected_trait_ref.self_ty().references_error() {
return None;
}
let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else {
return None;
};
let found_did = match *found_trait_ty.kind() {
ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) | ty::Generator(did, ..) => {
Some(did)
}
ty::Adt(def, _) => Some(def.did()),
_ => None,
};
let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did));
let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did));
if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) {
// We check closures twice, with obligations flowing in different directions,
// but we want to complain about them only once.
return None;
}
self.reported_closure_mismatch.borrow_mut().insert((span, found_span));
let mut not_tupled = false;
let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() {
ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()],
_ => {
not_tupled = true;
vec![ArgKind::empty()]
}
};
let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1);
let expected = match expected_ty.kind() {
ty::Tuple(ref tys) => {
tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect()
}
_ => {
not_tupled = true;
vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())]
}
};
// If this is a `Fn` family trait and either the expected or found
// is not tupled, then fall back to just a regular mismatch error.
// This shouldn't be common unless manually implementing one of the
// traits manually, but don't make it more confusing when it does
// happen.
Some(
if Some(expected_trait_ref.def_id()) != self.tcx.lang_items().gen_trait() && not_tupled
{
self.report_and_explain_type_error(
TypeTrace::poly_trait_refs(
&obligation.cause,
true,
expected_trait_ref,
found_trait_ref,
),
ty::error::TypeError::Mismatch,
)
} else if found.len() == expected.len() {
self.report_closure_arg_mismatch(
span,
found_span,
found_trait_ref,
expected_trait_ref,
obligation.cause.code(),
found_node,
obligation.param_env,
)
} else {
let (closure_span, closure_arg_span, found) = found_did
.and_then(|did| {
let node = self.tcx.hir().get_if_local(did)?;
let (found_span, closure_arg_span, found) =
self.get_fn_like_arguments(node)?;
Some((Some(found_span), closure_arg_span, found))
})
.unwrap_or((found_span, None, found));
self.report_arg_count_mismatch(
span,
closure_span,
expected,
found,
found_trait_ty.is_closure(),
closure_arg_span,
)
},
)
}
fn report_not_const_evaluatable_error(
&self,
obligation: &PredicateObligation<'tcx>,
span: Span,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
if !self.tcx.features().generic_const_exprs {
let mut err = self
.tcx
.sess
.struct_span_err(span, "constant expression depends on a generic parameter");
// FIXME(const_generics): we should suggest to the user how they can resolve this
// issue. However, this is currently not actually possible
// (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
//
// Note that with `feature(generic_const_exprs)` this case should not
// be reachable.
err.note("this may fail depending on what value the parameter takes");
err.emit();
return None;
}
match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(ct) => {
let ty::ConstKind::Unevaluated(uv) = ct.kind() else {
bug!("const evaluatable failed for non-unevaluated const `{ct:?}`");
};
let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant");
let const_span = self.tcx.def_span(uv.def);
match self.tcx.sess.source_map().span_to_snippet(const_span) {
Ok(snippet) => err.help(&format!(
"try adding a `where` bound using this expression: `where [(); {}]:`",
snippet
)),
_ => err.help("consider adding a `where` bound using this expression"),
};
Some(err)
}
_ => {
span_bug!(
span,
"unexpected non-ConstEvaluatable predicate, this should not be reachable"
)
}
}
}
}
struct UnsatisfiedConst(pub bool);
fn get_explanation_based_on_obligation<'tcx>(
obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
pre_message: String,
) -> String {
if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
"consider using `()`, or a `Result`".to_owned()
} else {
let ty_desc = match trait_ref.skip_binder().self_ty().kind() {
ty::FnDef(_, _) => Some("fn item"),
ty::Closure(_, _) => Some("closure"),
_ => None,
};
match ty_desc {
Some(desc) => format!(
"{}the trait `{}` is not implemented for {} `{}`",
pre_message,
trait_predicate.print_modifiers_and_trait_path(),
desc,
trait_ref.skip_binder().self_ty(),
),
None => format!(
"{}the trait `{}` is not implemented for `{}`",
pre_message,
trait_predicate.print_modifiers_and_trait_path(),
trait_ref.skip_binder().self_ty(),
),
}
}
}
/// Crude way of getting back an `Expr` from a `Span`.
pub struct FindExprBySpan<'hir> {
pub span: Span,
@@ -341,6 +341,7 @@ pub struct OnUnimplementedNote {
/// should fallback to a generic message, `Some(None)` suggests using the default
/// appended message, `Some(Some(s))` suggests use the `s` message instead of the
/// default one..
/// FIXME(bryangarza): Change this to an enum with the 3 variants described above.
pub append_const_msg: Option<Option<Symbol>>,
}
@@ -386,7 +386,7 @@ fn probe_assoc_types_at_expr(
fn maybe_suggest_convert_to_slice(
&self,
err: &mut Diagnostic,
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
trait_ref: ty::PolyTraitRef<'tcx>,
candidate_impls: &[ImplCandidate<'tcx>],
span: Span,
);
@@ -3848,7 +3848,7 @@ fn probe_assoc_types_at_expr(
fn maybe_suggest_convert_to_slice(
&self,
err: &mut Diagnostic,
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
trait_ref: ty::PolyTraitRef<'tcx>,
candidate_impls: &[ImplCandidate<'tcx>],
span: Span,
) {