diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index d1b99aae25b1d..5e2a68e1f02db 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -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::{ diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs similarity index 93% rename from compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs rename to compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index dec3b26420c24..f7ec5f1ff325f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1,8 +1,6 @@ -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::errors::{ AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, }; @@ -12,12 +10,12 @@ use crate::infer::{self, InferCtxt}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::NormalizeExt; use crate::traits::{ - elaborate, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, - ObligationCause, ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation, - SelectionError, SignatureMismatch, TraitNotObjectSafe, + elaborate, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, + ObligationCtxt, Overflow, PredicateObligation, SelectionError, SignatureMismatch, + TraitNotObjectSafe, }; use core::ops::ControlFlow; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{pluralize, struct_span_code_err, Applicability, StringPart}; @@ -44,9 +42,8 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; -use rustc_span::{BytePos, ExpnKind, Span, Symbol, DUMMY_SP}; +use rustc_span::{BytePos, Span, Symbol, DUMMY_SP}; use std::borrow::Cow; -use std::iter; use super::{ ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst, @@ -54,128 +51,8 @@ use super::{ pub use rustc_infer::traits::error_reporting::*; -#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)] +#[extension(pub trait TypeErrCtxtSelectionErrExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - fn report_fulfillment_errors( - &self, - mut errors: Vec>, - ) -> 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, // 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")) - } - /// The `root_obligation` parameter should be the `root_obligation` field /// from a `FulfillmentError`. If no `FulfillmentError` is available, /// then it should be the same as `obligation`. @@ -803,7 +680,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.point_at_returns_when_relevant(&mut err, &obligation); err.emit() } +} +#[extension(pub(super) trait TypeErrCtxtExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn apply_do_not_recommend(&self, obligation: &mut PredicateObligation<'tcx>) -> bool { let mut base_cause = obligation.cause.code().clone(); let mut applied_do_not_recommend = false; @@ -1324,72 +1204,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - #[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), - } - } - #[instrument(level = "debug", skip_all)] fn report_projection_error( &self, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs index 34da8e576ce86..e8d7e80ac562c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs @@ -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, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index e776248e68476..2131e236401ef 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -1,24 +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 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::overflow::*; -pub use self::type_err_ctxt_ext::*; // When outputting impl candidates, prefer showing those that are more similar. // @@ -127,6 +137,195 @@ pub enum DefIdOrName { Name(&'static str), } +#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + fn report_fulfillment_errors( + &self, + mut errors: Vec>, + ) -> 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, // 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 { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 83c6798ba2ed1..e90fe8fb94dd2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -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, }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 11a2b1d323379..2cf808f962f08 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -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::{