Move some stuff into the ambiguity and suggestion modules
This commit is contained in:
parent
7af825fea4
commit
cd68a28daa
@ -1,10 +1,27 @@
|
|||||||
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
use rustc_errors::{
|
||||||
|
struct_span_code_err, Applicability, Diag, MultiSpan, StashKey, E0283, E0284, E0790,
|
||||||
|
};
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
use rustc_hir::intravisit::Visitor as _;
|
||||||
|
use rustc_hir::LangItem;
|
||||||
|
use rustc_infer::infer::error_reporting::{TypeAnnotationNeeded, TypeErrCtxt};
|
||||||
use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
|
use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
|
||||||
use rustc_infer::traits::util::elaborate;
|
use rustc_infer::traits::util::elaborate;
|
||||||
use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation};
|
use rustc_infer::traits::{
|
||||||
use rustc_middle::ty;
|
Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation,
|
||||||
use rustc_span::{Span, DUMMY_SP};
|
};
|
||||||
|
use rustc_macros::extension;
|
||||||
|
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _};
|
||||||
|
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
|
||||||
|
|
||||||
|
use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
|
||||||
|
use crate::error_reporting::traits::{
|
||||||
|
to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _,
|
||||||
|
};
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||||
use crate::traits::ObligationCtxt;
|
use crate::traits::ObligationCtxt;
|
||||||
|
|
||||||
@ -134,3 +151,548 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>(
|
|||||||
|
|
||||||
ambiguities
|
ambiguities
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)]
|
||||||
|
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||||
|
#[instrument(skip(self), level = "debug")]
|
||||||
|
fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed {
|
||||||
|
// Unable to successfully determine, probably means
|
||||||
|
// insufficient type information, but could mean
|
||||||
|
// ambiguous impls. The latter *ought* to be a
|
||||||
|
// coherence violation, so we don't report it here.
|
||||||
|
|
||||||
|
let predicate = self.resolve_vars_if_possible(obligation.predicate);
|
||||||
|
let span = obligation.cause.span;
|
||||||
|
|
||||||
|
debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
|
||||||
|
|
||||||
|
// Ambiguity errors are often caused as fallout from earlier errors.
|
||||||
|
// We ignore them if this `infcx` is tainted in some cases below.
|
||||||
|
|
||||||
|
let bound_predicate = predicate.kind();
|
||||||
|
let mut err = match bound_predicate.skip_binder() {
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
|
||||||
|
let trait_ref = bound_predicate.rebind(data.trait_ref);
|
||||||
|
debug!(?trait_ref);
|
||||||
|
|
||||||
|
if let Err(e) = predicate.error_reported() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) {
|
||||||
|
// Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
|
||||||
|
// other `Foo` impls are incoherent.
|
||||||
|
return guar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is kind of a hack: it frequently happens that some earlier
|
||||||
|
// error prevents types from being fully inferred, and then we get
|
||||||
|
// a bunch of uninteresting errors saying something like "<generic
|
||||||
|
// #0> doesn't implement Sized". It may even be true that we
|
||||||
|
// could just skip over all checks where the self-ty is an
|
||||||
|
// inference variable, but I was afraid that there might be an
|
||||||
|
// inference variable created, registered as an obligation, and
|
||||||
|
// then never forced by writeback, and hence by skipping here we'd
|
||||||
|
// be ignoring the fact that we don't KNOW the type works
|
||||||
|
// out. Though even that would probably be harmless, given that
|
||||||
|
// we're only talking about builtin traits, which are known to be
|
||||||
|
// inhabited. We used to check for `self.tcx.sess.has_errors()` to
|
||||||
|
// avoid inundating the user with unnecessary errors, but we now
|
||||||
|
// check upstream for type errors and don't add the obligations to
|
||||||
|
// begin with in those cases.
|
||||||
|
if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
|
||||||
|
match self.tainted_by_errors() {
|
||||||
|
None => {
|
||||||
|
let err = self.emit_inference_failure_err(
|
||||||
|
obligation.cause.body_id,
|
||||||
|
span,
|
||||||
|
trait_ref.self_ty().skip_binder().into(),
|
||||||
|
TypeAnnotationNeeded::E0282,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
return err.stash(span, StashKey::MaybeForgetReturn).unwrap();
|
||||||
|
}
|
||||||
|
Some(e) => return e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Typically, this ambiguity should only happen if
|
||||||
|
// there are unresolved type inference variables
|
||||||
|
// (otherwise it would suggest a coherence
|
||||||
|
// failure). But given #21974 that is not necessarily
|
||||||
|
// the case -- we can have multiple where clauses that
|
||||||
|
// are only distinguished by a region, which results
|
||||||
|
// in an ambiguity even when all types are fully
|
||||||
|
// known, since we don't dispatch based on region
|
||||||
|
// relationships.
|
||||||
|
|
||||||
|
// Pick the first generic parameter that still contains inference variables as the one
|
||||||
|
// we're going to emit an error for. If there are none (see above), fall back to
|
||||||
|
// a more general error.
|
||||||
|
let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer());
|
||||||
|
|
||||||
|
let mut err = if let Some(arg) = arg {
|
||||||
|
self.emit_inference_failure_err(
|
||||||
|
obligation.cause.body_id,
|
||||||
|
span,
|
||||||
|
arg,
|
||||||
|
TypeAnnotationNeeded::E0283,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
struct_span_code_err!(
|
||||||
|
self.dcx(),
|
||||||
|
span,
|
||||||
|
E0283,
|
||||||
|
"type annotations needed: cannot satisfy `{}`",
|
||||||
|
predicate,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ambiguities = compute_applicable_impls_for_diagnostics(
|
||||||
|
self.infcx,
|
||||||
|
&obligation.with(self.tcx, trait_ref),
|
||||||
|
);
|
||||||
|
let has_non_region_infer =
|
||||||
|
trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer());
|
||||||
|
// It doesn't make sense to talk about applicable impls if there are more than a
|
||||||
|
// handful of them. If there are a lot of them, but only a few of them have no type
|
||||||
|
// params, we only show those, as they are more likely to be useful/intended.
|
||||||
|
if ambiguities.len() > 5 {
|
||||||
|
let infcx = self.infcx;
|
||||||
|
if !ambiguities.iter().all(|option| match option {
|
||||||
|
CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
|
||||||
|
CandidateSource::ParamEnv(_) => true,
|
||||||
|
}) {
|
||||||
|
// If not all are blanket impls, we filter blanked impls out.
|
||||||
|
ambiguities.retain(|option| match option {
|
||||||
|
CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
|
||||||
|
CandidateSource::ParamEnv(_) => true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
|
||||||
|
if let Some(e) = self.tainted_by_errors()
|
||||||
|
&& arg.is_none()
|
||||||
|
{
|
||||||
|
// If `arg.is_none()`, then this is probably two param-env
|
||||||
|
// candidates or impl candidates that are equal modulo lifetimes.
|
||||||
|
// Therefore, if we've already emitted an error, just skip this
|
||||||
|
// one, since it's not particularly actionable.
|
||||||
|
err.cancel();
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
|
||||||
|
} else {
|
||||||
|
if let Some(e) = self.tainted_by_errors() {
|
||||||
|
err.cancel();
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
err.note(format!("cannot satisfy `{predicate}`"));
|
||||||
|
let impl_candidates =
|
||||||
|
self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap());
|
||||||
|
if impl_candidates.len() < 40 {
|
||||||
|
self.report_similar_impl_candidates(
|
||||||
|
impl_candidates.as_slice(),
|
||||||
|
trait_ref,
|
||||||
|
obligation.cause.body_id,
|
||||||
|
&mut err,
|
||||||
|
false,
|
||||||
|
obligation.param_env,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ObligationCauseCode::WhereClause(def_id, _)
|
||||||
|
| ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code()
|
||||||
|
{
|
||||||
|
self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack())
|
||||||
|
&& let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
|
||||||
|
{
|
||||||
|
let mut expr_finder = FindExprBySpan::new(span, self.tcx);
|
||||||
|
expr_finder.visit_expr(&body.value);
|
||||||
|
|
||||||
|
if let Some(hir::Expr {
|
||||||
|
kind:
|
||||||
|
hir::ExprKind::Call(
|
||||||
|
hir::Expr {
|
||||||
|
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
)
|
||||||
|
| hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
|
||||||
|
..
|
||||||
|
}) = expr_finder.result
|
||||||
|
&& let [
|
||||||
|
..,
|
||||||
|
trait_path_segment @ hir::PathSegment {
|
||||||
|
res: Res::Def(DefKind::Trait, trait_id),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
hir::PathSegment {
|
||||||
|
ident: assoc_item_name,
|
||||||
|
res: Res::Def(_, item_id),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
] = path.segments
|
||||||
|
&& data.trait_ref.def_id == *trait_id
|
||||||
|
&& self.tcx.trait_of_item(*item_id) == Some(*trait_id)
|
||||||
|
&& let None = self.tainted_by_errors()
|
||||||
|
{
|
||||||
|
let (verb, noun) = match self.tcx.associated_item(item_id).kind {
|
||||||
|
ty::AssocKind::Const => ("refer to the", "constant"),
|
||||||
|
ty::AssocKind::Fn => ("call", "function"),
|
||||||
|
// This is already covered by E0223, but this following single match
|
||||||
|
// arm doesn't hurt here.
|
||||||
|
ty::AssocKind::Type => ("refer to the", "type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Replace the more general E0283 with a more specific error
|
||||||
|
err.cancel();
|
||||||
|
err = self.dcx().struct_span_err(
|
||||||
|
span,
|
||||||
|
format!(
|
||||||
|
"cannot {verb} associated {noun} on trait without specifying the \
|
||||||
|
corresponding `impl` type",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
err.code(E0790);
|
||||||
|
|
||||||
|
if let Some(local_def_id) = data.trait_ref.def_id.as_local()
|
||||||
|
&& let hir::Node::Item(hir::Item {
|
||||||
|
ident: trait_name,
|
||||||
|
kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs),
|
||||||
|
..
|
||||||
|
}) = self.tcx.hir_node_by_def_id(local_def_id)
|
||||||
|
&& let Some(method_ref) = trait_item_refs
|
||||||
|
.iter()
|
||||||
|
.find(|item_ref| item_ref.ident == *assoc_item_name)
|
||||||
|
{
|
||||||
|
err.span_label(
|
||||||
|
method_ref.span,
|
||||||
|
format!("`{trait_name}::{assoc_item_name}` defined here"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
|
||||||
|
|
||||||
|
let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
|
||||||
|
|
||||||
|
if let Some(impl_def_id) =
|
||||||
|
trait_impls.non_blanket_impls().values().flatten().next()
|
||||||
|
{
|
||||||
|
let non_blanket_impl_count =
|
||||||
|
trait_impls.non_blanket_impls().values().flatten().count();
|
||||||
|
// If there is only one implementation of the trait, suggest using it.
|
||||||
|
// Otherwise, use a placeholder comment for the implementation.
|
||||||
|
let (message, self_type) = if non_blanket_impl_count == 1 {
|
||||||
|
(
|
||||||
|
"use the fully-qualified path to the only available \
|
||||||
|
implementation",
|
||||||
|
format!(
|
||||||
|
"{}",
|
||||||
|
self.tcx.type_of(impl_def_id).instantiate_identity()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
"use a fully-qualified path to a specific available \
|
||||||
|
implementation",
|
||||||
|
"/* self type */".to_string(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let mut suggestions =
|
||||||
|
vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))];
|
||||||
|
if let Some(generic_arg) = trait_path_segment.args {
|
||||||
|
let between_span =
|
||||||
|
trait_path_segment.ident.span.between(generic_arg.span_ext);
|
||||||
|
// get rid of :: between Trait and <type>
|
||||||
|
// must be '::' between them, otherwise the parser won't accept the code
|
||||||
|
suggestions.push((between_span, "".to_string()));
|
||||||
|
suggestions
|
||||||
|
.push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
|
||||||
|
} else {
|
||||||
|
suggestions.push((
|
||||||
|
trait_path_segment.ident.span.shrink_to_hi(),
|
||||||
|
">".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
err.multipart_suggestion(
|
||||||
|
message,
|
||||||
|
suggestions,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
err
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
|
||||||
|
// Same hacky approach as above to avoid deluging user
|
||||||
|
// with error messages.
|
||||||
|
|
||||||
|
if let Err(e) = arg.error_reported() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
if let Some(e) = self.tainted_by_errors() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.emit_inference_failure_err(
|
||||||
|
obligation.cause.body_id,
|
||||||
|
span,
|
||||||
|
arg,
|
||||||
|
TypeAnnotationNeeded::E0282,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::PredicateKind::Subtype(data) => {
|
||||||
|
if let Err(e) = data.error_reported() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
if let Some(e) = self.tainted_by_errors() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
let ty::SubtypePredicate { a_is_expected: _, a, b } = data;
|
||||||
|
// both must be type variables, or the other would've been instantiated
|
||||||
|
assert!(a.is_ty_var() && b.is_ty_var());
|
||||||
|
self.emit_inference_failure_err(
|
||||||
|
obligation.cause.body_id,
|
||||||
|
span,
|
||||||
|
a.into(),
|
||||||
|
TypeAnnotationNeeded::E0282,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
|
||||||
|
if let Err(e) = predicate.error_reported() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
if let Some(e) = self.tainted_by_errors() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(guar) =
|
||||||
|
self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id))
|
||||||
|
{
|
||||||
|
// Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
|
||||||
|
// other `Foo` impls are incoherent.
|
||||||
|
return guar;
|
||||||
|
}
|
||||||
|
let arg = data
|
||||||
|
.projection_term
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.chain(Some(data.term.into_arg()))
|
||||||
|
.find(|g| g.has_non_region_infer());
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
self.emit_inference_failure_err(
|
||||||
|
obligation.cause.body_id,
|
||||||
|
span,
|
||||||
|
arg,
|
||||||
|
TypeAnnotationNeeded::E0284,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.with_note(format!("cannot satisfy `{predicate}`"))
|
||||||
|
} else {
|
||||||
|
// If we can't find a generic parameter, just print a generic error
|
||||||
|
struct_span_code_err!(
|
||||||
|
self.dcx(),
|
||||||
|
span,
|
||||||
|
E0284,
|
||||||
|
"type annotations needed: cannot satisfy `{}`",
|
||||||
|
predicate,
|
||||||
|
)
|
||||||
|
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
|
||||||
|
if let Err(e) = predicate.error_reported() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
if let Some(e) = self.tainted_by_errors() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
let arg = data.walk().find(|g| g.is_non_region_infer());
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
let err = self.emit_inference_failure_err(
|
||||||
|
obligation.cause.body_id,
|
||||||
|
span,
|
||||||
|
arg,
|
||||||
|
TypeAnnotationNeeded::E0284,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
err
|
||||||
|
} else {
|
||||||
|
// If we can't find a generic parameter, just print a generic error
|
||||||
|
struct_span_code_err!(
|
||||||
|
self.dcx(),
|
||||||
|
span,
|
||||||
|
E0284,
|
||||||
|
"type annotations needed: cannot satisfy `{}`",
|
||||||
|
predicate,
|
||||||
|
)
|
||||||
|
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self
|
||||||
|
.emit_inference_failure_err(
|
||||||
|
obligation.cause.body_id,
|
||||||
|
span,
|
||||||
|
ct.into(),
|
||||||
|
TypeAnnotationNeeded::E0284,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })
|
||||||
|
if term.is_infer() =>
|
||||||
|
{
|
||||||
|
if let Some(e) = self.tainted_by_errors() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
struct_span_code_err!(
|
||||||
|
self.dcx(),
|
||||||
|
span,
|
||||||
|
E0284,
|
||||||
|
"type annotations needed: cannot normalize `{alias}`",
|
||||||
|
)
|
||||||
|
.with_span_label(span, format!("cannot normalize `{alias}`"))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
if let Some(e) = self.tainted_by_errors() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
struct_span_code_err!(
|
||||||
|
self.dcx(),
|
||||||
|
span,
|
||||||
|
E0284,
|
||||||
|
"type annotations needed: cannot satisfy `{}`",
|
||||||
|
predicate,
|
||||||
|
)
|
||||||
|
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.note_obligation_cause(&mut err, obligation);
|
||||||
|
err.emit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn annotate_source_of_ambiguity(
|
||||||
|
&self,
|
||||||
|
err: &mut Diag<'_>,
|
||||||
|
ambiguities: &[CandidateSource],
|
||||||
|
predicate: ty::Predicate<'tcx>,
|
||||||
|
) {
|
||||||
|
let mut spans = vec![];
|
||||||
|
let mut crates = vec![];
|
||||||
|
let mut post = vec![];
|
||||||
|
let mut has_param_env = false;
|
||||||
|
for ambiguity in ambiguities {
|
||||||
|
match ambiguity {
|
||||||
|
CandidateSource::DefId(impl_def_id) => match self.tcx.span_of_impl(*impl_def_id) {
|
||||||
|
Ok(span) => spans.push(span),
|
||||||
|
Err(name) => {
|
||||||
|
crates.push(name);
|
||||||
|
if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
|
||||||
|
post.push(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CandidateSource::ParamEnv(span) => {
|
||||||
|
has_param_env = true;
|
||||||
|
spans.push(*span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect();
|
||||||
|
crate_names.sort();
|
||||||
|
crate_names.dedup();
|
||||||
|
post.sort();
|
||||||
|
post.dedup();
|
||||||
|
|
||||||
|
if self.tainted_by_errors().is_some()
|
||||||
|
&& (crate_names.len() == 1
|
||||||
|
&& spans.len() == 0
|
||||||
|
&& ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str())
|
||||||
|
|| predicate.visit_with(&mut HasNumericInferVisitor).is_break())
|
||||||
|
{
|
||||||
|
// Avoid complaining about other inference issues for expressions like
|
||||||
|
// `42 >> 1`, where the types are still `{integer}`, but we want to
|
||||||
|
// Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too?
|
||||||
|
// NOTE(eddyb) this was `.cancel()`, but `err`
|
||||||
|
// is borrowed, so we can't fully defuse it.
|
||||||
|
err.downgrade_to_delayed_bug();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = format!(
|
||||||
|
"multiple `impl`s{} satisfying `{}` found",
|
||||||
|
if has_param_env { " or `where` clauses" } else { "" },
|
||||||
|
predicate
|
||||||
|
);
|
||||||
|
let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
|
||||||
|
format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),)
|
||||||
|
} else if post.len() == 1 {
|
||||||
|
format!(": `{}`", post[0])
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
match (spans.len(), crates.len(), crate_names.len()) {
|
||||||
|
(0, 0, 0) => {
|
||||||
|
err.note(format!("cannot satisfy `{predicate}`"));
|
||||||
|
}
|
||||||
|
(0, _, 1) => {
|
||||||
|
err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,));
|
||||||
|
}
|
||||||
|
(0, _, _) => {
|
||||||
|
err.note(format!(
|
||||||
|
"{} in the following crates: {}{}",
|
||||||
|
msg,
|
||||||
|
crate_names.join(", "),
|
||||||
|
post,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
(_, 0, 0) => {
|
||||||
|
let span: MultiSpan = spans.into();
|
||||||
|
err.span_note(span, msg);
|
||||||
|
}
|
||||||
|
(_, 1, 1) => {
|
||||||
|
let span: MultiSpan = spans.into();
|
||||||
|
err.span_note(span, msg);
|
||||||
|
err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let span: MultiSpan = spans.into();
|
||||||
|
err.span_note(span, msg);
|
||||||
|
err.note(format!(
|
||||||
|
"and more `impl`s found in the following crates: {}{}",
|
||||||
|
crate_names.join(", "),
|
||||||
|
post,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HasNumericInferVisitor;
|
||||||
|
|
||||||
|
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
|
||||||
|
type Result = ControlFlow<()>;
|
||||||
|
|
||||||
|
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||||
|
if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
|
||||||
|
ControlFlow::Break(())
|
||||||
|
} else {
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -15,7 +15,6 @@ use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode, Pred
|
|||||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use std::ops::ControlFlow;
|
|
||||||
|
|
||||||
pub use self::infer_ctxt_ext::*;
|
pub use self::infer_ctxt_ext::*;
|
||||||
pub use self::overflow::*;
|
pub use self::overflow::*;
|
||||||
@ -91,49 +90,6 @@ impl<'v> Visitor<'v> for FindExprBySpan<'v> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
|
|
||||||
/// `param: ?Sized` would be a valid constraint.
|
|
||||||
struct FindTypeParam {
|
|
||||||
param: rustc_span::Symbol,
|
|
||||||
invalid_spans: Vec<Span>,
|
|
||||||
nested: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'v> Visitor<'v> for FindTypeParam {
|
|
||||||
fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) {
|
|
||||||
// Skip where-clauses, to avoid suggesting indirection for type parameters found there.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
|
|
||||||
// We collect the spans of all uses of the "bare" type param, like in `field: T` or
|
|
||||||
// `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
|
|
||||||
// valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
|
|
||||||
// obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
|
|
||||||
// and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
|
|
||||||
// in that case should make what happened clear enough.
|
|
||||||
match ty.kind {
|
|
||||||
hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
|
|
||||||
hir::TyKind::Path(hir::QPath::Resolved(None, path))
|
|
||||||
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
|
|
||||||
{
|
|
||||||
if !self.nested {
|
|
||||||
debug!(?ty, "FindTypeParam::visit_ty");
|
|
||||||
self.invalid_spans.push(ty.span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hir::TyKind::Path(_) => {
|
|
||||||
let prev = self.nested;
|
|
||||||
self.nested = true;
|
|
||||||
hir::intravisit::walk_ty(self, ty);
|
|
||||||
self.nested = prev;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
hir::intravisit::walk_ty(self, ty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Summarizes information
|
/// Summarizes information
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ArgKind {
|
pub enum ArgKind {
|
||||||
@ -165,20 +121,6 @@ impl ArgKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HasNumericInferVisitor;
|
|
||||||
|
|
||||||
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
|
|
||||||
type Result = ControlFlow<()>;
|
|
||||||
|
|
||||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
|
||||||
if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
|
|
||||||
ControlFlow::Break(())
|
|
||||||
} else {
|
|
||||||
ControlFlow::Continue(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum DefIdOrName {
|
pub enum DefIdOrName {
|
||||||
DefId(DefId),
|
DefId(DefId),
|
||||||
|
@ -4623,6 +4623,132 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all)]
|
||||||
|
fn suggest_unsized_bound_if_applicable(
|
||||||
|
&self,
|
||||||
|
err: &mut Diag<'_>,
|
||||||
|
obligation: &PredicateObligation<'tcx>,
|
||||||
|
) {
|
||||||
|
let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
|
||||||
|
obligation.predicate.kind().skip_binder()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let (ObligationCauseCode::WhereClause(item_def_id, span)
|
||||||
|
| ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) =
|
||||||
|
*obligation.cause.code().peel_derives()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if span.is_dummy() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debug!(?pred, ?item_def_id, ?span);
|
||||||
|
|
||||||
|
let (Some(node), true) = (
|
||||||
|
self.tcx.hir().get_if_local(item_def_id),
|
||||||
|
self.tcx.is_lang_item(pred.def_id(), LangItem::Sized),
|
||||||
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(generics) = node.generics() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let sized_trait = self.tcx.lang_items().sized_trait();
|
||||||
|
debug!(?generics.params);
|
||||||
|
debug!(?generics.predicates);
|
||||||
|
let Some(param) = generics.params.iter().find(|param| param.span == span) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
|
||||||
|
// `Sized` bound is there intentionally and we don't need to suggest relaxing it.
|
||||||
|
let explicitly_sized = generics
|
||||||
|
.bounds_for_param(param.def_id)
|
||||||
|
.flat_map(|bp| bp.bounds)
|
||||||
|
.any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
|
||||||
|
if explicitly_sized {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debug!(?param);
|
||||||
|
match node {
|
||||||
|
hir::Node::Item(
|
||||||
|
item @ hir::Item {
|
||||||
|
// Only suggest indirection for uses of type parameters in ADTs.
|
||||||
|
kind:
|
||||||
|
hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if self.suggest_indirection_for_unsized(err, item, param) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
|
||||||
|
let (span, separator, open_paren_sp) =
|
||||||
|
if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) {
|
||||||
|
(s, " +", open_paren_sp)
|
||||||
|
} else {
|
||||||
|
(param.name.ident().span.shrink_to_hi(), ":", None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut suggs = vec![];
|
||||||
|
let suggestion = format!("{separator} ?Sized");
|
||||||
|
|
||||||
|
if let Some(open_paren_sp) = open_paren_sp {
|
||||||
|
suggs.push((open_paren_sp, "(".to_string()));
|
||||||
|
suggs.push((span, format!("){suggestion}")));
|
||||||
|
} else {
|
||||||
|
suggs.push((span, suggestion));
|
||||||
|
}
|
||||||
|
|
||||||
|
err.multipart_suggestion_verbose(
|
||||||
|
"consider relaxing the implicit `Sized` restriction",
|
||||||
|
suggs,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suggest_indirection_for_unsized(
|
||||||
|
&self,
|
||||||
|
err: &mut Diag<'_>,
|
||||||
|
item: &hir::Item<'tcx>,
|
||||||
|
param: &hir::GenericParam<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
// Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
|
||||||
|
// borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
|
||||||
|
// is not. Look for invalid "bare" parameter uses, and suggest using indirection.
|
||||||
|
let mut visitor =
|
||||||
|
FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
|
||||||
|
visitor.visit_item(item);
|
||||||
|
if visitor.invalid_spans.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut multispan: MultiSpan = param.span.into();
|
||||||
|
multispan.push_span_label(
|
||||||
|
param.span,
|
||||||
|
format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
|
||||||
|
);
|
||||||
|
for sp in visitor.invalid_spans {
|
||||||
|
multispan.push_span_label(
|
||||||
|
sp,
|
||||||
|
format!("...if indirection were used here: `Box<{}>`", param.name.ident()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
err.span_help(
|
||||||
|
multispan,
|
||||||
|
format!(
|
||||||
|
"you could relax the implicit `Sized` bound on `{T}` if it were \
|
||||||
|
used through indirection like `&{T}` or `Box<{T}>`",
|
||||||
|
T = param.name.ident(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a hint to add a missing borrow or remove an unnecessary one.
|
/// Add a hint to add a missing borrow or remove an unnecessary one.
|
||||||
@ -5126,3 +5252,46 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
|
|||||||
|
|
||||||
(ty, refs)
|
(ty, refs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
|
||||||
|
/// `param: ?Sized` would be a valid constraint.
|
||||||
|
struct FindTypeParam {
|
||||||
|
param: rustc_span::Symbol,
|
||||||
|
invalid_spans: Vec<Span>,
|
||||||
|
nested: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'v> Visitor<'v> for FindTypeParam {
|
||||||
|
fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) {
|
||||||
|
// Skip where-clauses, to avoid suggesting indirection for type parameters found there.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
|
||||||
|
// We collect the spans of all uses of the "bare" type param, like in `field: T` or
|
||||||
|
// `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
|
||||||
|
// valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
|
||||||
|
// obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
|
||||||
|
// and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
|
||||||
|
// in that case should make what happened clear enough.
|
||||||
|
match ty.kind {
|
||||||
|
hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
|
||||||
|
hir::TyKind::Path(hir::QPath::Resolved(None, path))
|
||||||
|
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
|
||||||
|
{
|
||||||
|
if !self.nested {
|
||||||
|
debug!(?ty, "FindTypeParam::visit_ty");
|
||||||
|
self.invalid_spans.push(ty.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hir::TyKind::Path(_) => {
|
||||||
|
let prev = self.nested;
|
||||||
|
self.nested = true;
|
||||||
|
hir::intravisit::walk_ty(self, ty);
|
||||||
|
self.nested = prev;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
hir::intravisit::walk_ty(self, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
// ignore-tidy-filelength :(
|
use super::ambiguity::TypeErrCtxtAmbiguityExt as _;
|
||||||
|
|
||||||
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
|
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
|
||||||
use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
|
use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
|
||||||
use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt;
|
use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt;
|
||||||
use crate::error_reporting::traits::overflow::TypeErrCtxtOverflowExt;
|
use crate::error_reporting::traits::overflow::TypeErrCtxtOverflowExt;
|
||||||
use crate::error_reporting::traits::to_pretty_impl_header;
|
|
||||||
use crate::error_reporting::traits::{ambiguity, ambiguity::CandidateSource::*};
|
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
|
AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
|
||||||
};
|
};
|
||||||
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
|
use crate::infer::error_reporting::TyCategory;
|
||||||
use crate::infer::InferCtxtExt as _;
|
use crate::infer::InferCtxtExt as _;
|
||||||
use crate::infer::{self, InferCtxt};
|
use crate::infer::{self, InferCtxt};
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||||
@ -23,13 +20,13 @@ use core::ops::ControlFlow;
|
|||||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||||
use rustc_data_structures::unord::UnordSet;
|
use rustc_data_structures::unord::UnordSet;
|
||||||
use rustc_errors::codes::*;
|
use rustc_errors::codes::*;
|
||||||
use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart};
|
use rustc_errors::{pluralize, struct_span_code_err, Applicability, StringPart};
|
||||||
use rustc_errors::{Diag, ErrorGuaranteed, StashKey};
|
use rustc_errors::{Diag, ErrorGuaranteed, StashKey};
|
||||||
use rustc_hir::def::{DefKind, Namespace, Res};
|
use rustc_hir::def::Namespace;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::intravisit::Visitor;
|
use rustc_hir::intravisit::Visitor;
|
||||||
|
use rustc_hir::Node;
|
||||||
use rustc_hir::{self as hir, LangItem};
|
use rustc_hir::{self as hir, LangItem};
|
||||||
use rustc_hir::{GenericParam, Item, Node};
|
|
||||||
use rustc_infer::infer::error_reporting::TypeErrCtxt;
|
use rustc_infer::infer::error_reporting::TypeErrCtxt;
|
||||||
use rustc_infer::infer::{InferOk, TypeTrace};
|
use rustc_infer::infer::{InferOk, TypeTrace};
|
||||||
use rustc_macros::extension;
|
use rustc_macros::extension;
|
||||||
@ -43,8 +40,7 @@ use rustc_middle::ty::print::{
|
|||||||
PrintTraitRefExt as _,
|
PrintTraitRefExt as _,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, SubtypePredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable,
|
self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast,
|
||||||
TypeVisitableExt, Upcast,
|
|
||||||
};
|
};
|
||||||
use rustc_middle::{bug, span_bug};
|
use rustc_middle::{bug, span_bug};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
@ -53,8 +49,7 @@ use std::borrow::Cow;
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, GetSafeTransmuteErrorAndReason,
|
ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst,
|
||||||
HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use rustc_infer::traits::error_reporting::*;
|
pub use rustc_infer::traits::error_reporting::*;
|
||||||
@ -956,7 +951,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`,
|
/// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`,
|
||||||
/// identify thoe method chain sub-expressions that could or could not have been annotated
|
/// identify those method chain sub-expressions that could or could not have been annotated
|
||||||
/// with `?`.
|
/// with `?`.
|
||||||
fn try_conversion_context(
|
fn try_conversion_context(
|
||||||
&self,
|
&self,
|
||||||
@ -2172,536 +2167,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||||||
Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred)
|
Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self), level = "debug")]
|
|
||||||
fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed {
|
|
||||||
// Unable to successfully determine, probably means
|
|
||||||
// insufficient type information, but could mean
|
|
||||||
// ambiguous impls. The latter *ought* to be a
|
|
||||||
// coherence violation, so we don't report it here.
|
|
||||||
|
|
||||||
let predicate = self.resolve_vars_if_possible(obligation.predicate);
|
|
||||||
let span = obligation.cause.span;
|
|
||||||
|
|
||||||
debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
|
|
||||||
|
|
||||||
// Ambiguity errors are often caused as fallout from earlier errors.
|
|
||||||
// We ignore them if this `infcx` is tainted in some cases below.
|
|
||||||
|
|
||||||
let bound_predicate = predicate.kind();
|
|
||||||
let mut err = match bound_predicate.skip_binder() {
|
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
|
|
||||||
let trait_ref = bound_predicate.rebind(data.trait_ref);
|
|
||||||
debug!(?trait_ref);
|
|
||||||
|
|
||||||
if let Err(e) = predicate.error_reported() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) {
|
|
||||||
// Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
|
|
||||||
// other `Foo` impls are incoherent.
|
|
||||||
return guar;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is kind of a hack: it frequently happens that some earlier
|
|
||||||
// error prevents types from being fully inferred, and then we get
|
|
||||||
// a bunch of uninteresting errors saying something like "<generic
|
|
||||||
// #0> doesn't implement Sized". It may even be true that we
|
|
||||||
// could just skip over all checks where the self-ty is an
|
|
||||||
// inference variable, but I was afraid that there might be an
|
|
||||||
// inference variable created, registered as an obligation, and
|
|
||||||
// then never forced by writeback, and hence by skipping here we'd
|
|
||||||
// be ignoring the fact that we don't KNOW the type works
|
|
||||||
// out. Though even that would probably be harmless, given that
|
|
||||||
// we're only talking about builtin traits, which are known to be
|
|
||||||
// inhabited. We used to check for `self.tcx.sess.has_errors()` to
|
|
||||||
// avoid inundating the user with unnecessary errors, but we now
|
|
||||||
// check upstream for type errors and don't add the obligations to
|
|
||||||
// begin with in those cases.
|
|
||||||
if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
|
|
||||||
match self.tainted_by_errors() {
|
|
||||||
None => {
|
|
||||||
let err = self.emit_inference_failure_err(
|
|
||||||
obligation.cause.body_id,
|
|
||||||
span,
|
|
||||||
trait_ref.self_ty().skip_binder().into(),
|
|
||||||
ErrorCode::E0282,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
return err.stash(span, StashKey::MaybeForgetReturn).unwrap();
|
|
||||||
}
|
|
||||||
Some(e) => return e,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typically, this ambiguity should only happen if
|
|
||||||
// there are unresolved type inference variables
|
|
||||||
// (otherwise it would suggest a coherence
|
|
||||||
// failure). But given #21974 that is not necessarily
|
|
||||||
// the case -- we can have multiple where clauses that
|
|
||||||
// are only distinguished by a region, which results
|
|
||||||
// in an ambiguity even when all types are fully
|
|
||||||
// known, since we don't dispatch based on region
|
|
||||||
// relationships.
|
|
||||||
|
|
||||||
// Pick the first generic parameter that still contains inference variables as the one
|
|
||||||
// we're going to emit an error for. If there are none (see above), fall back to
|
|
||||||
// a more general error.
|
|
||||||
let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer());
|
|
||||||
|
|
||||||
let mut err = if let Some(arg) = arg {
|
|
||||||
self.emit_inference_failure_err(
|
|
||||||
obligation.cause.body_id,
|
|
||||||
span,
|
|
||||||
arg,
|
|
||||||
ErrorCode::E0283,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
struct_span_code_err!(
|
|
||||||
self.dcx(),
|
|
||||||
span,
|
|
||||||
E0283,
|
|
||||||
"type annotations needed: cannot satisfy `{}`",
|
|
||||||
predicate,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ambiguities = ambiguity::compute_applicable_impls_for_diagnostics(
|
|
||||||
self.infcx,
|
|
||||||
&obligation.with(self.tcx, trait_ref),
|
|
||||||
);
|
|
||||||
let has_non_region_infer =
|
|
||||||
trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer());
|
|
||||||
// It doesn't make sense to talk about applicable impls if there are more than a
|
|
||||||
// handful of them. If there are a lot of them, but only a few of them have no type
|
|
||||||
// params, we only show those, as they are more likely to be useful/intended.
|
|
||||||
if ambiguities.len() > 5 {
|
|
||||||
let infcx = self.infcx;
|
|
||||||
if !ambiguities.iter().all(|option| match option {
|
|
||||||
DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
|
|
||||||
ParamEnv(_) => true,
|
|
||||||
}) {
|
|
||||||
// If not all are blanket impls, we filter blanked impls out.
|
|
||||||
ambiguities.retain(|option| match option {
|
|
||||||
DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
|
|
||||||
ParamEnv(_) => true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
|
|
||||||
if let Some(e) = self.tainted_by_errors()
|
|
||||||
&& arg.is_none()
|
|
||||||
{
|
|
||||||
// If `arg.is_none()`, then this is probably two param-env
|
|
||||||
// candidates or impl candidates that are equal modulo lifetimes.
|
|
||||||
// Therefore, if we've already emitted an error, just skip this
|
|
||||||
// one, since it's not particularly actionable.
|
|
||||||
err.cancel();
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
|
|
||||||
} else {
|
|
||||||
if let Some(e) = self.tainted_by_errors() {
|
|
||||||
err.cancel();
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
err.note(format!("cannot satisfy `{predicate}`"));
|
|
||||||
let impl_candidates =
|
|
||||||
self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap());
|
|
||||||
if impl_candidates.len() < 40 {
|
|
||||||
self.report_similar_impl_candidates(
|
|
||||||
impl_candidates.as_slice(),
|
|
||||||
trait_ref,
|
|
||||||
obligation.cause.body_id,
|
|
||||||
&mut err,
|
|
||||||
false,
|
|
||||||
obligation.param_env,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ObligationCauseCode::WhereClause(def_id, _)
|
|
||||||
| ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code()
|
|
||||||
{
|
|
||||||
self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack())
|
|
||||||
&& let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
|
|
||||||
{
|
|
||||||
let mut expr_finder = FindExprBySpan::new(span, self.tcx);
|
|
||||||
expr_finder.visit_expr(&body.value);
|
|
||||||
|
|
||||||
if let Some(hir::Expr {
|
|
||||||
kind:
|
|
||||||
hir::ExprKind::Call(
|
|
||||||
hir::Expr {
|
|
||||||
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
_,
|
|
||||||
)
|
|
||||||
| hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
|
|
||||||
..
|
|
||||||
}) = expr_finder.result
|
|
||||||
&& let [
|
|
||||||
..,
|
|
||||||
trait_path_segment @ hir::PathSegment {
|
|
||||||
res: Res::Def(DefKind::Trait, trait_id),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
hir::PathSegment {
|
|
||||||
ident: assoc_item_name,
|
|
||||||
res: Res::Def(_, item_id),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
] = path.segments
|
|
||||||
&& data.trait_ref.def_id == *trait_id
|
|
||||||
&& self.tcx.trait_of_item(*item_id) == Some(*trait_id)
|
|
||||||
&& let None = self.tainted_by_errors()
|
|
||||||
{
|
|
||||||
let (verb, noun) = match self.tcx.associated_item(item_id).kind {
|
|
||||||
ty::AssocKind::Const => ("refer to the", "constant"),
|
|
||||||
ty::AssocKind::Fn => ("call", "function"),
|
|
||||||
// This is already covered by E0223, but this following single match
|
|
||||||
// arm doesn't hurt here.
|
|
||||||
ty::AssocKind::Type => ("refer to the", "type"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Replace the more general E0283 with a more specific error
|
|
||||||
err.cancel();
|
|
||||||
err = self.dcx().struct_span_err(
|
|
||||||
span,
|
|
||||||
format!(
|
|
||||||
"cannot {verb} associated {noun} on trait without specifying the \
|
|
||||||
corresponding `impl` type",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
err.code(E0790);
|
|
||||||
|
|
||||||
if let Some(local_def_id) = data.trait_ref.def_id.as_local()
|
|
||||||
&& let hir::Node::Item(hir::Item {
|
|
||||||
ident: trait_name,
|
|
||||||
kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs),
|
|
||||||
..
|
|
||||||
}) = self.tcx.hir_node_by_def_id(local_def_id)
|
|
||||||
&& let Some(method_ref) = trait_item_refs
|
|
||||||
.iter()
|
|
||||||
.find(|item_ref| item_ref.ident == *assoc_item_name)
|
|
||||||
{
|
|
||||||
err.span_label(
|
|
||||||
method_ref.span,
|
|
||||||
format!("`{trait_name}::{assoc_item_name}` defined here"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
|
|
||||||
|
|
||||||
let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
|
|
||||||
|
|
||||||
if let Some(impl_def_id) =
|
|
||||||
trait_impls.non_blanket_impls().values().flatten().next()
|
|
||||||
{
|
|
||||||
let non_blanket_impl_count =
|
|
||||||
trait_impls.non_blanket_impls().values().flatten().count();
|
|
||||||
// If there is only one implementation of the trait, suggest using it.
|
|
||||||
// Otherwise, use a placeholder comment for the implementation.
|
|
||||||
let (message, self_type) = if non_blanket_impl_count == 1 {
|
|
||||||
(
|
|
||||||
"use the fully-qualified path to the only available \
|
|
||||||
implementation",
|
|
||||||
format!(
|
|
||||||
"{}",
|
|
||||||
self.tcx.type_of(impl_def_id).instantiate_identity()
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
"use a fully-qualified path to a specific available \
|
|
||||||
implementation",
|
|
||||||
"/* self type */".to_string(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let mut suggestions =
|
|
||||||
vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))];
|
|
||||||
if let Some(generic_arg) = trait_path_segment.args {
|
|
||||||
let between_span =
|
|
||||||
trait_path_segment.ident.span.between(generic_arg.span_ext);
|
|
||||||
// get rid of :: between Trait and <type>
|
|
||||||
// must be '::' between them, otherwise the parser won't accept the code
|
|
||||||
suggestions.push((between_span, "".to_string()));
|
|
||||||
suggestions
|
|
||||||
.push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
|
|
||||||
} else {
|
|
||||||
suggestions.push((
|
|
||||||
trait_path_segment.ident.span.shrink_to_hi(),
|
|
||||||
">".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
err.multipart_suggestion(
|
|
||||||
message,
|
|
||||||
suggestions,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
err
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
|
|
||||||
// Same hacky approach as above to avoid deluging user
|
|
||||||
// with error messages.
|
|
||||||
|
|
||||||
if let Err(e) = arg.error_reported() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
if let Some(e) = self.tainted_by_errors() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.emit_inference_failure_err(
|
|
||||||
obligation.cause.body_id,
|
|
||||||
span,
|
|
||||||
arg,
|
|
||||||
ErrorCode::E0282,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::PredicateKind::Subtype(data) => {
|
|
||||||
if let Err(e) = data.error_reported() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
if let Some(e) = self.tainted_by_errors() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
let SubtypePredicate { a_is_expected: _, a, b } = data;
|
|
||||||
// both must be type variables, or the other would've been instantiated
|
|
||||||
assert!(a.is_ty_var() && b.is_ty_var());
|
|
||||||
self.emit_inference_failure_err(
|
|
||||||
obligation.cause.body_id,
|
|
||||||
span,
|
|
||||||
a.into(),
|
|
||||||
ErrorCode::E0282,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
|
|
||||||
if let Err(e) = predicate.error_reported() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
if let Some(e) = self.tainted_by_errors() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(guar) =
|
|
||||||
self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id))
|
|
||||||
{
|
|
||||||
// Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
|
|
||||||
// other `Foo` impls are incoherent.
|
|
||||||
return guar;
|
|
||||||
}
|
|
||||||
let arg = data
|
|
||||||
.projection_term
|
|
||||||
.args
|
|
||||||
.iter()
|
|
||||||
.chain(Some(data.term.into_arg()))
|
|
||||||
.find(|g| g.has_non_region_infer());
|
|
||||||
if let Some(arg) = arg {
|
|
||||||
self.emit_inference_failure_err(
|
|
||||||
obligation.cause.body_id,
|
|
||||||
span,
|
|
||||||
arg,
|
|
||||||
ErrorCode::E0284,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.with_note(format!("cannot satisfy `{predicate}`"))
|
|
||||||
} else {
|
|
||||||
// If we can't find a generic parameter, just print a generic error
|
|
||||||
struct_span_code_err!(
|
|
||||||
self.dcx(),
|
|
||||||
span,
|
|
||||||
E0284,
|
|
||||||
"type annotations needed: cannot satisfy `{}`",
|
|
||||||
predicate,
|
|
||||||
)
|
|
||||||
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
|
|
||||||
if let Err(e) = predicate.error_reported() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
if let Some(e) = self.tainted_by_errors() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
let arg = data.walk().find(|g| g.is_non_region_infer());
|
|
||||||
if let Some(arg) = arg {
|
|
||||||
let err = self.emit_inference_failure_err(
|
|
||||||
obligation.cause.body_id,
|
|
||||||
span,
|
|
||||||
arg,
|
|
||||||
ErrorCode::E0284,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
err
|
|
||||||
} else {
|
|
||||||
// If we can't find a generic parameter, just print a generic error
|
|
||||||
struct_span_code_err!(
|
|
||||||
self.dcx(),
|
|
||||||
span,
|
|
||||||
E0284,
|
|
||||||
"type annotations needed: cannot satisfy `{}`",
|
|
||||||
predicate,
|
|
||||||
)
|
|
||||||
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self
|
|
||||||
.emit_inference_failure_err(
|
|
||||||
obligation.cause.body_id,
|
|
||||||
span,
|
|
||||||
ct.into(),
|
|
||||||
ErrorCode::E0284,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })
|
|
||||||
if term.is_infer() =>
|
|
||||||
{
|
|
||||||
if let Some(e) = self.tainted_by_errors() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
struct_span_code_err!(
|
|
||||||
self.dcx(),
|
|
||||||
span,
|
|
||||||
E0284,
|
|
||||||
"type annotations needed: cannot normalize `{alias}`",
|
|
||||||
)
|
|
||||||
.with_span_label(span, format!("cannot normalize `{alias}`"))
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
if let Some(e) = self.tainted_by_errors() {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
struct_span_code_err!(
|
|
||||||
self.dcx(),
|
|
||||||
span,
|
|
||||||
E0284,
|
|
||||||
"type annotations needed: cannot satisfy `{}`",
|
|
||||||
predicate,
|
|
||||||
)
|
|
||||||
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.note_obligation_cause(&mut err, obligation);
|
|
||||||
err.emit()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn annotate_source_of_ambiguity(
|
|
||||||
&self,
|
|
||||||
err: &mut Diag<'_>,
|
|
||||||
ambiguities: &[ambiguity::CandidateSource],
|
|
||||||
predicate: ty::Predicate<'tcx>,
|
|
||||||
) {
|
|
||||||
let mut spans = vec![];
|
|
||||||
let mut crates = vec![];
|
|
||||||
let mut post = vec![];
|
|
||||||
let mut has_param_env = false;
|
|
||||||
for ambiguity in ambiguities {
|
|
||||||
match ambiguity {
|
|
||||||
ambiguity::CandidateSource::DefId(impl_def_id) => {
|
|
||||||
match self.tcx.span_of_impl(*impl_def_id) {
|
|
||||||
Ok(span) => spans.push(span),
|
|
||||||
Err(name) => {
|
|
||||||
crates.push(name);
|
|
||||||
if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
|
|
||||||
post.push(header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ambiguity::CandidateSource::ParamEnv(span) => {
|
|
||||||
has_param_env = true;
|
|
||||||
spans.push(*span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect();
|
|
||||||
crate_names.sort();
|
|
||||||
crate_names.dedup();
|
|
||||||
post.sort();
|
|
||||||
post.dedup();
|
|
||||||
|
|
||||||
if self.tainted_by_errors().is_some()
|
|
||||||
&& (crate_names.len() == 1
|
|
||||||
&& spans.len() == 0
|
|
||||||
&& ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str())
|
|
||||||
|| predicate.visit_with(&mut HasNumericInferVisitor).is_break())
|
|
||||||
{
|
|
||||||
// Avoid complaining about other inference issues for expressions like
|
|
||||||
// `42 >> 1`, where the types are still `{integer}`, but we want to
|
|
||||||
// Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too?
|
|
||||||
// NOTE(eddyb) this was `.cancel()`, but `err`
|
|
||||||
// is borrowed, so we can't fully defuse it.
|
|
||||||
err.downgrade_to_delayed_bug();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let msg = format!(
|
|
||||||
"multiple `impl`s{} satisfying `{}` found",
|
|
||||||
if has_param_env { " or `where` clauses" } else { "" },
|
|
||||||
predicate
|
|
||||||
);
|
|
||||||
let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
|
|
||||||
format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),)
|
|
||||||
} else if post.len() == 1 {
|
|
||||||
format!(": `{}`", post[0])
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
match (spans.len(), crates.len(), crate_names.len()) {
|
|
||||||
(0, 0, 0) => {
|
|
||||||
err.note(format!("cannot satisfy `{predicate}`"));
|
|
||||||
}
|
|
||||||
(0, _, 1) => {
|
|
||||||
err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,));
|
|
||||||
}
|
|
||||||
(0, _, _) => {
|
|
||||||
err.note(format!(
|
|
||||||
"{} in the following crates: {}{}",
|
|
||||||
msg,
|
|
||||||
crate_names.join(", "),
|
|
||||||
post,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
(_, 0, 0) => {
|
|
||||||
let span: MultiSpan = spans.into();
|
|
||||||
err.span_note(span, msg);
|
|
||||||
}
|
|
||||||
(_, 1, 1) => {
|
|
||||||
let span: MultiSpan = spans.into();
|
|
||||||
err.span_note(span, msg);
|
|
||||||
err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let span: MultiSpan = spans.into();
|
|
||||||
err.span_note(span, msg);
|
|
||||||
err.note(format!(
|
|
||||||
"and more `impl`s found in the following crates: {}{}",
|
|
||||||
crate_names.join(", "),
|
|
||||||
post,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the trait predicate may apply for *some* assignment
|
/// Returns `true` if the trait predicate may apply for *some* assignment
|
||||||
/// to the type parameters.
|
/// to the type parameters.
|
||||||
fn predicate_can_apply(
|
fn predicate_can_apply(
|
||||||
@ -2769,136 +2234,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
|
||||||
fn suggest_unsized_bound_if_applicable(
|
|
||||||
&self,
|
|
||||||
err: &mut Diag<'_>,
|
|
||||||
obligation: &PredicateObligation<'tcx>,
|
|
||||||
) {
|
|
||||||
let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
|
|
||||||
obligation.predicate.kind().skip_binder()
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let (ObligationCauseCode::WhereClause(item_def_id, span)
|
|
||||||
| ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) =
|
|
||||||
*obligation.cause.code().peel_derives()
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if span.is_dummy() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debug!(?pred, ?item_def_id, ?span);
|
|
||||||
|
|
||||||
let (Some(node), true) = (
|
|
||||||
self.tcx.hir().get_if_local(item_def_id),
|
|
||||||
self.tcx.is_lang_item(pred.def_id(), LangItem::Sized),
|
|
||||||
) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
self.maybe_suggest_unsized_generics(err, span, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
|
||||||
fn maybe_suggest_unsized_generics(&self, err: &mut Diag<'_>, span: Span, node: Node<'tcx>) {
|
|
||||||
let Some(generics) = node.generics() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let sized_trait = self.tcx.lang_items().sized_trait();
|
|
||||||
debug!(?generics.params);
|
|
||||||
debug!(?generics.predicates);
|
|
||||||
let Some(param) = generics.params.iter().find(|param| param.span == span) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
// Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
|
|
||||||
// `Sized` bound is there intentionally and we don't need to suggest relaxing it.
|
|
||||||
let explicitly_sized = generics
|
|
||||||
.bounds_for_param(param.def_id)
|
|
||||||
.flat_map(|bp| bp.bounds)
|
|
||||||
.any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
|
|
||||||
if explicitly_sized {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debug!(?param);
|
|
||||||
match node {
|
|
||||||
hir::Node::Item(
|
|
||||||
item @ hir::Item {
|
|
||||||
// Only suggest indirection for uses of type parameters in ADTs.
|
|
||||||
kind:
|
|
||||||
hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
if self.maybe_indirection_for_unsized(err, item, param) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
|
|
||||||
let (span, separator, open_paren_sp) =
|
|
||||||
if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) {
|
|
||||||
(s, " +", open_paren_sp)
|
|
||||||
} else {
|
|
||||||
(param.name.ident().span.shrink_to_hi(), ":", None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut suggs = vec![];
|
|
||||||
let suggestion = format!("{separator} ?Sized");
|
|
||||||
|
|
||||||
if let Some(open_paren_sp) = open_paren_sp {
|
|
||||||
suggs.push((open_paren_sp, "(".to_string()));
|
|
||||||
suggs.push((span, format!("){suggestion}")));
|
|
||||||
} else {
|
|
||||||
suggs.push((span, suggestion));
|
|
||||||
}
|
|
||||||
|
|
||||||
err.multipart_suggestion_verbose(
|
|
||||||
"consider relaxing the implicit `Sized` restriction",
|
|
||||||
suggs,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_indirection_for_unsized(
|
|
||||||
&self,
|
|
||||||
err: &mut Diag<'_>,
|
|
||||||
item: &Item<'tcx>,
|
|
||||||
param: &GenericParam<'tcx>,
|
|
||||||
) -> bool {
|
|
||||||
// Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
|
|
||||||
// borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
|
|
||||||
// is not. Look for invalid "bare" parameter uses, and suggest using indirection.
|
|
||||||
let mut visitor =
|
|
||||||
FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
|
|
||||||
visitor.visit_item(item);
|
|
||||||
if visitor.invalid_spans.is_empty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let mut multispan: MultiSpan = param.span.into();
|
|
||||||
multispan.push_span_label(
|
|
||||||
param.span,
|
|
||||||
format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
|
|
||||||
);
|
|
||||||
for sp in visitor.invalid_spans {
|
|
||||||
multispan.push_span_label(
|
|
||||||
sp,
|
|
||||||
format!("...if indirection were used here: `Box<{}>`", param.name.ident()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
err.span_help(
|
|
||||||
multispan,
|
|
||||||
format!(
|
|
||||||
"you could relax the implicit `Sized` bound on `{T}` if it were \
|
|
||||||
used through indirection like `&{T}` or `Box<{T}>`",
|
|
||||||
T = param.name.ident(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_recursive_obligation(
|
fn is_recursive_obligation(
|
||||||
&self,
|
&self,
|
||||||
obligated_types: &mut Vec<Ty<'tcx>>,
|
obligated_types: &mut Vec<Ty<'tcx>>,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user