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::intravisit::Visitor as _;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_infer::infer::error_reporting::{TypeAnnotationNeeded, TypeErrCtxt};
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
|
||||
use rustc_infer::traits::util::elaborate;
|
||||
use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation};
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_infer::traits::{
|
||||
Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation,
|
||||
};
|
||||
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::ObligationCtxt;
|
||||
|
||||
@ -134,3 +151,548 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>(
|
||||
|
||||
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::{self, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
pub use self::infer_ctxt_ext::*;
|
||||
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
|
||||
#[derive(Clone)]
|
||||
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)]
|
||||
pub enum DefIdOrName {
|
||||
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.
|
||||
@ -5126,3 +5252,46 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
|
||||
|
||||
(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::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
|
||||
use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt;
|
||||
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::{
|
||||
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::{self, InferCtxt};
|
||||
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::unord::UnordSet;
|
||||
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_hir::def::{DefKind, Namespace, Res};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::Node;
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_hir::{GenericParam, Item, Node};
|
||||
use rustc_infer::infer::error_reporting::TypeErrCtxt;
|
||||
use rustc_infer::infer::{InferOk, TypeTrace};
|
||||
use rustc_macros::extension;
|
||||
@ -43,8 +40,7 @@ use rustc_middle::ty::print::{
|
||||
PrintTraitRefExt as _,
|
||||
};
|
||||
use rustc_middle::ty::{
|
||||
self, SubtypePredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable,
|
||||
TypeVisitableExt, Upcast,
|
||||
self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::symbol::sym;
|
||||
@ -53,8 +49,7 @@ use std::borrow::Cow;
|
||||
use std::iter;
|
||||
|
||||
use super::{
|
||||
ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, GetSafeTransmuteErrorAndReason,
|
||||
HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst,
|
||||
ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst,
|
||||
};
|
||||
|
||||
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()?`,
|
||||
/// 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 `?`.
|
||||
fn try_conversion_context(
|
||||
&self,
|
||||
@ -2172,536 +2167,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
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
|
||||
/// to the type parameters.
|
||||
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(
|
||||
&self,
|
||||
obligated_types: &mut Vec<Ty<'tcx>>,
|
||||
|
Loading…
x
Reference in New Issue
Block a user