Skip to content

Commit 956deab

Browse files
committed
Auto merge of rust-lang#127495 - compiler-errors:more-trait-error-reworking, r=lcnr
More trait error reworking More work on rust-lang#127492, specifically those sub-bullets under "Move trait error reporting to `error_reporting::traits`". Stacked on top of rust-lang#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
2 parents 7d640b6 + bbbff80 commit 956deab

File tree

15 files changed

+1168
-1126
lines changed

15 files changed

+1168
-1126
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ use rustc_span::symbol::sym;
5959
use rustc_span::{BytePos, DesugaringKind, Span, DUMMY_SP};
6060
use rustc_target::spec::abi::Abi;
6161
use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt;
62-
use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
62+
use rustc_trait_selection::error_reporting::traits::TypeErrCtxtSelectionErrExt as _;
6363
use rustc_trait_selection::infer::InferCtxtExt as _;
6464
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
6565
use rustc_trait_selection::traits::{

compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs

+565-3
Large diffs are not rendered by default.

compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

+17-1,046
Large diffs are not rendered by default.

compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// FIXME(error_reporting): This should be made into private methods on `TypeErrCtxt`.
2+
13
use crate::infer::InferCtxt;
24
use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
35
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag};
@@ -9,8 +11,6 @@ use rustc_span::{Span, DUMMY_SP};
911

1012
use super::ArgKind;
1113

12-
pub use rustc_infer::traits::error_reporting::*;
13-
1414
#[extension(pub trait InferCtxtExt<'tcx>)]
1515
impl<'tcx> InferCtxt<'tcx> {
1616
/// Given some node representing a fn-like thing in the HIR map,

compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs

+208-65
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
1-
// ignore-tidy-filelength :(
2-
31
pub mod ambiguity;
2+
mod fulfillment_errors;
43
mod infer_ctxt_ext;
54
pub mod on_unimplemented;
5+
mod overflow;
66
pub mod suggestions;
7-
mod type_err_ctxt_ext;
87

9-
use rustc_data_structures::fx::FxIndexSet;
10-
use rustc_hir as hir;
8+
use std::iter;
9+
10+
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
1111
use rustc_hir::def_id::DefId;
1212
use rustc_hir::intravisit::Visitor;
13-
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
13+
use rustc_hir::{self as hir, LangItem};
14+
use rustc_infer::infer::error_reporting::TypeErrCtxt;
15+
use rustc_infer::traits::{
16+
Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError,
17+
};
18+
use rustc_macros::extension;
1419
use rustc_middle::ty::print::PrintTraitRefExt as _;
1520
use rustc_middle::ty::{self, Ty, TyCtxt};
16-
use rustc_span::Span;
17-
use std::ops::ControlFlow;
21+
use rustc_span::{ErrorGuaranteed, ExpnKind, Span};
22+
23+
use ambiguity::TypeErrCtxtAmbiguityExt as _;
24+
use fulfillment_errors::TypeErrCtxtExt as _;
25+
use suggestions::TypeErrCtxtExt as _;
26+
27+
use crate::traits::{FulfillmentError, FulfillmentErrorCode};
1828

29+
pub use self::fulfillment_errors::*;
1930
pub use self::infer_ctxt_ext::*;
20-
pub use self::type_err_ctxt_ext::*;
31+
pub use self::overflow::*;
2132

2233
// When outputting impl candidates, prefer showing those that are more similar.
2334
//
@@ -89,49 +100,6 @@ impl<'v> Visitor<'v> for FindExprBySpan<'v> {
89100
}
90101
}
91102

92-
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
93-
/// `param: ?Sized` would be a valid constraint.
94-
struct FindTypeParam {
95-
param: rustc_span::Symbol,
96-
invalid_spans: Vec<Span>,
97-
nested: bool,
98-
}
99-
100-
impl<'v> Visitor<'v> for FindTypeParam {
101-
fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) {
102-
// Skip where-clauses, to avoid suggesting indirection for type parameters found there.
103-
}
104-
105-
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
106-
// We collect the spans of all uses of the "bare" type param, like in `field: T` or
107-
// `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
108-
// valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
109-
// obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
110-
// and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
111-
// in that case should make what happened clear enough.
112-
match ty.kind {
113-
hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
114-
hir::TyKind::Path(hir::QPath::Resolved(None, path))
115-
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
116-
{
117-
if !self.nested {
118-
debug!(?ty, "FindTypeParam::visit_ty");
119-
self.invalid_spans.push(ty.span);
120-
}
121-
}
122-
hir::TyKind::Path(_) => {
123-
let prev = self.nested;
124-
self.nested = true;
125-
hir::intravisit::walk_ty(self, ty);
126-
self.nested = prev;
127-
}
128-
_ => {
129-
hir::intravisit::walk_ty(self, ty);
130-
}
131-
}
132-
}
133-
}
134-
135103
/// Summarizes information
136104
#[derive(Clone)]
137105
pub enum ArgKind {
@@ -163,24 +131,199 @@ impl ArgKind {
163131
}
164132
}
165133

166-
struct HasNumericInferVisitor;
134+
#[derive(Copy, Clone)]
135+
pub enum DefIdOrName {
136+
DefId(DefId),
137+
Name(&'static str),
138+
}
167139

168-
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
169-
type Result = ControlFlow<()>;
140+
#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
141+
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
142+
fn report_fulfillment_errors(
143+
&self,
144+
mut errors: Vec<FulfillmentError<'tcx>>,
145+
) -> ErrorGuaranteed {
146+
self.sub_relations
147+
.borrow_mut()
148+
.add_constraints(self, errors.iter().map(|e| e.obligation.predicate));
149+
150+
#[derive(Debug)]
151+
struct ErrorDescriptor<'tcx> {
152+
predicate: ty::Predicate<'tcx>,
153+
index: Option<usize>, // None if this is an old error
154+
}
170155

171-
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
172-
if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
173-
ControlFlow::Break(())
174-
} else {
175-
ControlFlow::Continue(())
156+
let mut error_map: FxIndexMap<_, Vec<_>> = self
157+
.reported_trait_errors
158+
.borrow()
159+
.iter()
160+
.map(|(&span, predicates)| {
161+
(
162+
span,
163+
predicates
164+
.0
165+
.iter()
166+
.map(|&predicate| ErrorDescriptor { predicate, index: None })
167+
.collect(),
168+
)
169+
})
170+
.collect();
171+
172+
// Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics
173+
// with more relevant type information and hide redundant E0282 errors.
174+
errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
175+
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
176+
if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) =>
177+
{
178+
1
179+
}
180+
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
181+
ty::PredicateKind::Coerce(_) => 2,
182+
_ => 0,
183+
});
184+
185+
for (index, error) in errors.iter().enumerate() {
186+
// We want to ignore desugarings here: spans are equivalent even
187+
// if one is the result of a desugaring and the other is not.
188+
let mut span = error.obligation.cause.span;
189+
let expn_data = span.ctxt().outer_expn_data();
190+
if let ExpnKind::Desugaring(_) = expn_data.kind {
191+
span = expn_data.call_site;
192+
}
193+
194+
error_map.entry(span).or_default().push(ErrorDescriptor {
195+
predicate: error.obligation.predicate,
196+
index: Some(index),
197+
});
176198
}
199+
200+
// We do this in 2 passes because we want to display errors in order, though
201+
// maybe it *is* better to sort errors by span or something.
202+
let mut is_suppressed = vec![false; errors.len()];
203+
for (_, error_set) in error_map.iter() {
204+
// We want to suppress "duplicate" errors with the same span.
205+
for error in error_set {
206+
if let Some(index) = error.index {
207+
// Suppress errors that are either:
208+
// 1) strictly implied by another error.
209+
// 2) implied by an error with a smaller index.
210+
for error2 in error_set {
211+
if error2.index.is_some_and(|index2| is_suppressed[index2]) {
212+
// Avoid errors being suppressed by already-suppressed
213+
// errors, to prevent all errors from being suppressed
214+
// at once.
215+
continue;
216+
}
217+
218+
if self.error_implies(error2.predicate, error.predicate)
219+
&& !(error2.index >= error.index
220+
&& self.error_implies(error.predicate, error2.predicate))
221+
{
222+
info!("skipping {:?} (implied by {:?})", error, error2);
223+
is_suppressed[index] = true;
224+
break;
225+
}
226+
}
227+
}
228+
}
229+
}
230+
231+
let mut reported = None;
232+
233+
for from_expansion in [false, true] {
234+
for (error, suppressed) in iter::zip(&errors, &is_suppressed) {
235+
if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
236+
let guar = self.report_fulfillment_error(error);
237+
self.infcx.set_tainted_by_errors(guar);
238+
reported = Some(guar);
239+
// We want to ignore desugarings here: spans are equivalent even
240+
// if one is the result of a desugaring and the other is not.
241+
let mut span = error.obligation.cause.span;
242+
let expn_data = span.ctxt().outer_expn_data();
243+
if let ExpnKind::Desugaring(_) = expn_data.kind {
244+
span = expn_data.call_site;
245+
}
246+
self.reported_trait_errors
247+
.borrow_mut()
248+
.entry(span)
249+
.or_insert_with(|| (vec![], guar))
250+
.0
251+
.push(error.obligation.predicate);
252+
}
253+
}
254+
}
255+
256+
// It could be that we don't report an error because we have seen an `ErrorReported` from
257+
// another source. We should probably be able to fix most of these, but some are delayed
258+
// bugs that get a proper error after this function.
259+
reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors"))
177260
}
178-
}
179261

180-
#[derive(Copy, Clone)]
181-
pub enum DefIdOrName {
182-
DefId(DefId),
183-
Name(&'static str),
262+
#[instrument(skip(self), level = "debug")]
263+
fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed {
264+
let mut error = FulfillmentError {
265+
obligation: error.obligation.clone(),
266+
code: error.code.clone(),
267+
root_obligation: error.root_obligation.clone(),
268+
};
269+
if matches!(
270+
error.code,
271+
FulfillmentErrorCode::Select(crate::traits::SelectionError::Unimplemented)
272+
| FulfillmentErrorCode::Project(_)
273+
) && self.apply_do_not_recommend(&mut error.obligation)
274+
{
275+
error.code = FulfillmentErrorCode::Select(SelectionError::Unimplemented);
276+
}
277+
278+
match error.code {
279+
FulfillmentErrorCode::Select(ref selection_error) => self.report_selection_error(
280+
error.obligation.clone(),
281+
&error.root_obligation,
282+
selection_error,
283+
),
284+
FulfillmentErrorCode::Project(ref e) => {
285+
self.report_projection_error(&error.obligation, e)
286+
}
287+
FulfillmentErrorCode::Ambiguity { overflow: None } => {
288+
self.maybe_report_ambiguity(&error.obligation)
289+
}
290+
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
291+
self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit)
292+
}
293+
FulfillmentErrorCode::Subtype(ref expected_found, ref err) => self
294+
.report_mismatched_types(
295+
&error.obligation.cause,
296+
expected_found.expected,
297+
expected_found.found,
298+
*err,
299+
)
300+
.emit(),
301+
FulfillmentErrorCode::ConstEquate(ref expected_found, ref err) => {
302+
let mut diag = self.report_mismatched_consts(
303+
&error.obligation.cause,
304+
expected_found.expected,
305+
expected_found.found,
306+
*err,
307+
);
308+
let code = error.obligation.cause.code().peel_derives().peel_match_impls();
309+
if let ObligationCauseCode::WhereClause(..)
310+
| ObligationCauseCode::WhereClauseInExpr(..) = code
311+
{
312+
self.note_obligation_cause_code(
313+
error.obligation.cause.body_id,
314+
&mut diag,
315+
error.obligation.predicate,
316+
error.obligation.param_env,
317+
code,
318+
&mut vec![],
319+
&mut Default::default(),
320+
);
321+
}
322+
diag.emit()
323+
}
324+
FulfillmentErrorCode::Cycle(ref cycle) => self.report_overflow_obligation_cycle(cycle),
325+
}
326+
}
184327
}
185328

186329
/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a

compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{ObligationCauseCode, PredicateObligation};
2-
use crate::error_reporting::traits::type_err_ctxt_ext::InferCtxtPrivExt;
2+
use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
33
use crate::errors::{
44
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
55
};

0 commit comments

Comments
 (0)