mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-13 12:36:47 +00:00
Auto merge of #127495 - compiler-errors:more-trait-error-reworking, r=lcnr
More trait error reworking More work on #127492, specifically those sub-bullets under "Move trait error reporting to `error_reporting::traits`". Stacked on top of #127493. This does introduce new `TypeErrCtxt.*Ext` traits, but those will be deleted soon. Splitting this work into bite-sized pieces is the only way that it's gonna be feasible to both author and review ❤️ r? lcnr
This commit is contained in:
commit
956deab788
@ -59,7 +59,7 @@ use rustc_span::symbol::sym;
|
||||
use rustc_span::{BytePos, DesugaringKind, Span, DUMMY_SP};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt;
|
||||
use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
|
||||
use rustc_trait_selection::error_reporting::traits::TypeErrCtxtSelectionErrExt as _;
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,5 @@
|
||||
// FIXME(error_reporting): This should be made into private methods on `TypeErrCtxt`.
|
||||
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
|
||||
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag};
|
||||
@ -9,8 +11,6 @@ use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
use super::ArgKind;
|
||||
|
||||
pub use rustc_infer::traits::error_reporting::*;
|
||||
|
||||
#[extension(pub trait InferCtxtExt<'tcx>)]
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// Given some node representing a fn-like thing in the HIR map,
|
||||
|
@ -1,23 +1,34 @@
|
||||
// ignore-tidy-filelength :(
|
||||
|
||||
pub mod ambiguity;
|
||||
mod fulfillment_errors;
|
||||
mod infer_ctxt_ext;
|
||||
pub mod on_unimplemented;
|
||||
mod overflow;
|
||||
pub mod suggestions;
|
||||
mod type_err_ctxt_ext;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use std::iter;
|
||||
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_infer::infer::error_reporting::TypeErrCtxt;
|
||||
use rustc_infer::traits::{
|
||||
Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError,
|
||||
};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use std::ops::ControlFlow;
|
||||
use rustc_span::{ErrorGuaranteed, ExpnKind, Span};
|
||||
|
||||
use ambiguity::TypeErrCtxtAmbiguityExt as _;
|
||||
use fulfillment_errors::TypeErrCtxtExt as _;
|
||||
use suggestions::TypeErrCtxtExt as _;
|
||||
|
||||
use crate::traits::{FulfillmentError, FulfillmentErrorCode};
|
||||
|
||||
pub use self::fulfillment_errors::*;
|
||||
pub use self::infer_ctxt_ext::*;
|
||||
pub use self::type_err_ctxt_ext::*;
|
||||
pub use self::overflow::*;
|
||||
|
||||
// When outputting impl candidates, prefer showing those that are more similar.
|
||||
//
|
||||
@ -89,49 +100,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 {
|
||||
@ -163,26 +131,201 @@ 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),
|
||||
Name(&'static str),
|
||||
}
|
||||
|
||||
#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
fn report_fulfillment_errors(
|
||||
&self,
|
||||
mut errors: Vec<FulfillmentError<'tcx>>,
|
||||
) -> ErrorGuaranteed {
|
||||
self.sub_relations
|
||||
.borrow_mut()
|
||||
.add_constraints(self, errors.iter().map(|e| e.obligation.predicate));
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ErrorDescriptor<'tcx> {
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
index: Option<usize>, // None if this is an old error
|
||||
}
|
||||
|
||||
let mut error_map: FxIndexMap<_, Vec<_>> = self
|
||||
.reported_trait_errors
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|(&span, predicates)| {
|
||||
(
|
||||
span,
|
||||
predicates
|
||||
.0
|
||||
.iter()
|
||||
.map(|&predicate| ErrorDescriptor { predicate, index: None })
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics
|
||||
// with more relevant type information and hide redundant E0282 errors.
|
||||
errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
|
||||
if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) =>
|
||||
{
|
||||
1
|
||||
}
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
|
||||
ty::PredicateKind::Coerce(_) => 2,
|
||||
_ => 0,
|
||||
});
|
||||
|
||||
for (index, error) in errors.iter().enumerate() {
|
||||
// We want to ignore desugarings here: spans are equivalent even
|
||||
// if one is the result of a desugaring and the other is not.
|
||||
let mut span = error.obligation.cause.span;
|
||||
let expn_data = span.ctxt().outer_expn_data();
|
||||
if let ExpnKind::Desugaring(_) = expn_data.kind {
|
||||
span = expn_data.call_site;
|
||||
}
|
||||
|
||||
error_map.entry(span).or_default().push(ErrorDescriptor {
|
||||
predicate: error.obligation.predicate,
|
||||
index: Some(index),
|
||||
});
|
||||
}
|
||||
|
||||
// We do this in 2 passes because we want to display errors in order, though
|
||||
// maybe it *is* better to sort errors by span or something.
|
||||
let mut is_suppressed = vec![false; errors.len()];
|
||||
for (_, error_set) in error_map.iter() {
|
||||
// We want to suppress "duplicate" errors with the same span.
|
||||
for error in error_set {
|
||||
if let Some(index) = error.index {
|
||||
// Suppress errors that are either:
|
||||
// 1) strictly implied by another error.
|
||||
// 2) implied by an error with a smaller index.
|
||||
for error2 in error_set {
|
||||
if error2.index.is_some_and(|index2| is_suppressed[index2]) {
|
||||
// Avoid errors being suppressed by already-suppressed
|
||||
// errors, to prevent all errors from being suppressed
|
||||
// at once.
|
||||
continue;
|
||||
}
|
||||
|
||||
if self.error_implies(error2.predicate, error.predicate)
|
||||
&& !(error2.index >= error.index
|
||||
&& self.error_implies(error.predicate, error2.predicate))
|
||||
{
|
||||
info!("skipping {:?} (implied by {:?})", error, error2);
|
||||
is_suppressed[index] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut reported = None;
|
||||
|
||||
for from_expansion in [false, true] {
|
||||
for (error, suppressed) in iter::zip(&errors, &is_suppressed) {
|
||||
if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
|
||||
let guar = self.report_fulfillment_error(error);
|
||||
self.infcx.set_tainted_by_errors(guar);
|
||||
reported = Some(guar);
|
||||
// We want to ignore desugarings here: spans are equivalent even
|
||||
// if one is the result of a desugaring and the other is not.
|
||||
let mut span = error.obligation.cause.span;
|
||||
let expn_data = span.ctxt().outer_expn_data();
|
||||
if let ExpnKind::Desugaring(_) = expn_data.kind {
|
||||
span = expn_data.call_site;
|
||||
}
|
||||
self.reported_trait_errors
|
||||
.borrow_mut()
|
||||
.entry(span)
|
||||
.or_insert_with(|| (vec![], guar))
|
||||
.0
|
||||
.push(error.obligation.predicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It could be that we don't report an error because we have seen an `ErrorReported` from
|
||||
// another source. We should probably be able to fix most of these, but some are delayed
|
||||
// bugs that get a proper error after this function.
|
||||
reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors"))
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed {
|
||||
let mut error = FulfillmentError {
|
||||
obligation: error.obligation.clone(),
|
||||
code: error.code.clone(),
|
||||
root_obligation: error.root_obligation.clone(),
|
||||
};
|
||||
if matches!(
|
||||
error.code,
|
||||
FulfillmentErrorCode::Select(crate::traits::SelectionError::Unimplemented)
|
||||
| FulfillmentErrorCode::Project(_)
|
||||
) && self.apply_do_not_recommend(&mut error.obligation)
|
||||
{
|
||||
error.code = FulfillmentErrorCode::Select(SelectionError::Unimplemented);
|
||||
}
|
||||
|
||||
match error.code {
|
||||
FulfillmentErrorCode::Select(ref selection_error) => self.report_selection_error(
|
||||
error.obligation.clone(),
|
||||
&error.root_obligation,
|
||||
selection_error,
|
||||
),
|
||||
FulfillmentErrorCode::Project(ref e) => {
|
||||
self.report_projection_error(&error.obligation, e)
|
||||
}
|
||||
FulfillmentErrorCode::Ambiguity { overflow: None } => {
|
||||
self.maybe_report_ambiguity(&error.obligation)
|
||||
}
|
||||
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
|
||||
self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit)
|
||||
}
|
||||
FulfillmentErrorCode::Subtype(ref expected_found, ref err) => self
|
||||
.report_mismatched_types(
|
||||
&error.obligation.cause,
|
||||
expected_found.expected,
|
||||
expected_found.found,
|
||||
*err,
|
||||
)
|
||||
.emit(),
|
||||
FulfillmentErrorCode::ConstEquate(ref expected_found, ref err) => {
|
||||
let mut diag = self.report_mismatched_consts(
|
||||
&error.obligation.cause,
|
||||
expected_found.expected,
|
||||
expected_found.found,
|
||||
*err,
|
||||
);
|
||||
let code = error.obligation.cause.code().peel_derives().peel_match_impls();
|
||||
if let ObligationCauseCode::WhereClause(..)
|
||||
| ObligationCauseCode::WhereClauseInExpr(..) = code
|
||||
{
|
||||
self.note_obligation_cause_code(
|
||||
error.obligation.cause.body_id,
|
||||
&mut diag,
|
||||
error.obligation.predicate,
|
||||
error.obligation.param_env,
|
||||
code,
|
||||
&mut vec![],
|
||||
&mut Default::default(),
|
||||
);
|
||||
}
|
||||
diag.emit()
|
||||
}
|
||||
FulfillmentErrorCode::Cycle(ref cycle) => self.report_overflow_obligation_cycle(cycle),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a
|
||||
/// string.
|
||||
pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{ObligationCauseCode, PredicateObligation};
|
||||
use crate::error_reporting::traits::type_err_ctxt_ext::InferCtxtPrivExt;
|
||||
use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
|
||||
use crate::errors::{
|
||||
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
|
||||
};
|
||||
|
@ -0,0 +1,197 @@
|
||||
use std::fmt;
|
||||
|
||||
use rustc_errors::{
|
||||
struct_span_code_err, Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, E0275,
|
||||
};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_infer::infer::error_reporting::TypeErrCtxt;
|
||||
use rustc_infer::traits::{Obligation, PredicateObligation};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::ty::print::{FmtPrinter, Print};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::Limit;
|
||||
use rustc_span::Span;
|
||||
use rustc_type_ir::Upcast;
|
||||
|
||||
use super::InferCtxtPrivExt;
|
||||
use crate::error_reporting::traits::suggestions::TypeErrCtxtExt;
|
||||
|
||||
pub enum OverflowCause<'tcx> {
|
||||
DeeplyNormalize(ty::AliasTerm<'tcx>),
|
||||
TraitSolver(ty::Predicate<'tcx>),
|
||||
}
|
||||
|
||||
pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
err: &mut Diag<'_, G>,
|
||||
) {
|
||||
let suggested_limit = match tcx.recursion_limit() {
|
||||
Limit(0) => Limit(2),
|
||||
limit => limit * 2,
|
||||
};
|
||||
err.help(format!(
|
||||
"consider increasing the recursion limit by adding a \
|
||||
`#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
|
||||
suggested_limit,
|
||||
tcx.crate_name(LOCAL_CRATE),
|
||||
));
|
||||
}
|
||||
|
||||
#[extension(pub trait TypeErrCtxtOverflowExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
/// Reports that an overflow has occurred and halts compilation. We
|
||||
/// halt compilation unconditionally because it is important that
|
||||
/// overflows never be masked -- they basically represent computations
|
||||
/// whose result could not be truly determined and thus we can't say
|
||||
/// if the program type checks or not -- and they are unusual
|
||||
/// occurrences in any case.
|
||||
fn report_overflow_error(
|
||||
&self,
|
||||
cause: OverflowCause<'tcx>,
|
||||
span: Span,
|
||||
suggest_increasing_limit: bool,
|
||||
mutate: impl FnOnce(&mut Diag<'_>),
|
||||
) -> ! {
|
||||
let mut err = self.build_overflow_error(cause, span, suggest_increasing_limit);
|
||||
mutate(&mut err);
|
||||
err.emit();
|
||||
FatalError.raise();
|
||||
}
|
||||
|
||||
fn build_overflow_error(
|
||||
&self,
|
||||
cause: OverflowCause<'tcx>,
|
||||
span: Span,
|
||||
suggest_increasing_limit: bool,
|
||||
) -> Diag<'a> {
|
||||
fn with_short_path<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> String
|
||||
where
|
||||
T: fmt::Display + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
|
||||
{
|
||||
let s = value.to_string();
|
||||
if s.len() > 50 {
|
||||
// We don't need to save the type to a file, we will be talking about this type already
|
||||
// in a separate note when we explain the obligation, so it will be available that way.
|
||||
let mut cx: FmtPrinter<'_, '_> =
|
||||
FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, rustc_session::Limit(6));
|
||||
value.print(&mut cx).unwrap();
|
||||
cx.into_buffer()
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
let mut err = match cause {
|
||||
OverflowCause::DeeplyNormalize(alias_term) => {
|
||||
let alias_term = self.resolve_vars_if_possible(alias_term);
|
||||
let kind = alias_term.kind(self.tcx).descr();
|
||||
let alias_str = with_short_path(self.tcx, alias_term);
|
||||
struct_span_code_err!(
|
||||
self.dcx(),
|
||||
span,
|
||||
E0275,
|
||||
"overflow normalizing the {kind} `{alias_str}`",
|
||||
)
|
||||
}
|
||||
OverflowCause::TraitSolver(predicate) => {
|
||||
let predicate = self.resolve_vars_if_possible(predicate);
|
||||
match predicate.kind().skip_binder() {
|
||||
ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ })
|
||||
| ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
|
||||
struct_span_code_err!(
|
||||
self.dcx(),
|
||||
span,
|
||||
E0275,
|
||||
"overflow assigning `{a}` to `{b}`",
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let pred_str = with_short_path(self.tcx, predicate);
|
||||
struct_span_code_err!(
|
||||
self.dcx(),
|
||||
span,
|
||||
E0275,
|
||||
"overflow evaluating the requirement `{pred_str}`",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if suggest_increasing_limit {
|
||||
suggest_new_overflow_limit(self.tcx, &mut err);
|
||||
}
|
||||
|
||||
err
|
||||
}
|
||||
|
||||
/// Reports that an overflow has occurred and halts compilation. We
|
||||
/// halt compilation unconditionally because it is important that
|
||||
/// overflows never be masked -- they basically represent computations
|
||||
/// whose result could not be truly determined and thus we can't say
|
||||
/// if the program type checks or not -- and they are unusual
|
||||
/// occurrences in any case.
|
||||
fn report_overflow_obligation<T>(
|
||||
&self,
|
||||
obligation: &Obligation<'tcx, T>,
|
||||
suggest_increasing_limit: bool,
|
||||
) -> !
|
||||
where
|
||||
T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>> + Clone,
|
||||
{
|
||||
let predicate = obligation.predicate.clone().upcast(self.tcx);
|
||||
let predicate = self.resolve_vars_if_possible(predicate);
|
||||
self.report_overflow_error(
|
||||
OverflowCause::TraitSolver(predicate),
|
||||
obligation.cause.span,
|
||||
suggest_increasing_limit,
|
||||
|err| {
|
||||
self.note_obligation_cause_code(
|
||||
obligation.cause.body_id,
|
||||
err,
|
||||
predicate,
|
||||
obligation.param_env,
|
||||
obligation.cause.code(),
|
||||
&mut vec![],
|
||||
&mut Default::default(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Reports that a cycle was detected which led to overflow and halts
|
||||
/// compilation. This is equivalent to `report_overflow_obligation` except
|
||||
/// that we can give a more helpful error message (and, in particular,
|
||||
/// we do not suggest increasing the overflow limit, which is not
|
||||
/// going to help).
|
||||
fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
|
||||
let cycle = self.resolve_vars_if_possible(cycle.to_owned());
|
||||
assert!(!cycle.is_empty());
|
||||
|
||||
debug!(?cycle, "report_overflow_error_cycle");
|
||||
|
||||
// The 'deepest' obligation is most likely to have a useful
|
||||
// cause 'backtrace'
|
||||
self.report_overflow_obligation(
|
||||
cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn report_overflow_no_abort(
|
||||
&self,
|
||||
obligation: PredicateObligation<'tcx>,
|
||||
suggest_increasing_limit: bool,
|
||||
) -> ErrorGuaranteed {
|
||||
let obligation = self.resolve_vars_if_possible(obligation);
|
||||
let mut err = self.build_overflow_error(
|
||||
OverflowCause::TraitSolver(obligation.predicate),
|
||||
obligation.cause.span,
|
||||
suggest_increasing_limit,
|
||||
);
|
||||
self.note_obligation_cause(&mut err, &obligation);
|
||||
self.point_at_returns_when_relevant(&mut err, &obligation);
|
||||
err.emit()
|
||||
}
|
||||
}
|
@ -44,7 +44,7 @@ use std::assert_matches::debug_assert_matches;
|
||||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
|
||||
use crate::error_reporting::traits::type_err_ctxt_ext::InferCtxtPrivExt;
|
||||
use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
|
||||
use crate::infer::InferCtxtExt as _;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use rustc_middle::ty::print::{
|
||||
@ -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,7 +1,7 @@
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::error_reporting::traits::{OverflowCause, TypeErrCtxtExt};
|
||||
use crate::error_reporting::traits::{OverflowCause, TypeErrCtxtOverflowExt};
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::error_reporting::traits::TypeErrCtxtExt;
|
||||
use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use crate::infer::{InferCtxt, TyOrConstInferVar};
|
||||
use crate::traits::normalize::normalize_with_depth_to;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
|
@ -3,7 +3,7 @@
|
||||
use super::SelectionContext;
|
||||
use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
|
||||
use crate::error_reporting::traits::OverflowCause;
|
||||
use crate::error_reporting::traits::TypeErrCtxtExt;
|
||||
use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use crate::solve::NextSolverError;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_infer::infer::at::At;
|
||||
|
@ -3,7 +3,7 @@
|
||||
//! `normalize_canonicalized_projection_ty` query when it encounters projections.
|
||||
|
||||
use crate::error_reporting::traits::OverflowCause;
|
||||
use crate::error_reporting::traits::TypeErrCtxtExt;
|
||||
use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use crate::infer::at::At;
|
||||
use crate::infer::canonical::OriginalQueryValues;
|
||||
use crate::infer::{InferCtxt, InferOk};
|
||||
|
@ -18,7 +18,7 @@ use super::{
|
||||
TraitQueryMode,
|
||||
};
|
||||
|
||||
use crate::error_reporting::traits::TypeErrCtxtExt;
|
||||
use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener};
|
||||
use crate::solve::InferCtxtSelectExt as _;
|
||||
use crate::traits::normalize::normalize_with_depth;
|
||||
|
@ -7,7 +7,7 @@ use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::traits::CodegenObligationError;
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
|
||||
use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
|
||||
use rustc_trait_selection::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use rustc_trait_selection::traits::{
|
||||
ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext,
|
||||
Unimplemented,
|
||||
|
@ -2,7 +2,7 @@ use rustc_infer::infer::canonical::{Canonical, QueryResponse};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
|
||||
use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
|
||||
use rustc_trait_selection::error_reporting::traits::TypeErrCtxtOverflowExt;
|
||||
use rustc_trait_selection::infer::InferCtxtBuilderExt;
|
||||
use rustc_trait_selection::traits::query::{
|
||||
normalize::NormalizationResult, CanonicalAliasGoal, NoSolution,
|
||||
|
Loading…
Reference in New Issue
Block a user