From 7af825fea4fea17d0b58fcd8c1f2b6fa11638cfb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 8 Jul 2024 16:23:01 -0400 Subject: [PATCH] Split out overflow handling into its own module --- .../src/error_reporting/traits/mod.rs | 2 + .../src/error_reporting/traits/overflow.rs | 197 ++++++++++++++++++ .../traits/type_err_ctxt_ext.rs | 182 +--------------- .../src/solve/normalize.rs | 2 +- .../src/traits/fulfill.rs | 2 +- .../src/traits/normalize.rs | 2 +- .../src/traits/query/normalize.rs | 2 +- .../src/traits/select/mod.rs | 2 +- compiler/rustc_traits/src/codegen.rs | 2 +- .../src/normalize_projection_ty.rs | 2 +- 10 files changed, 208 insertions(+), 187 deletions(-) create mode 100644 compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs 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 438b25056c3..7d334d623cc 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -3,6 +3,7 @@ pub mod ambiguity; mod infer_ctxt_ext; pub mod on_unimplemented; +mod overflow; pub mod suggestions; mod type_err_ctxt_ext; @@ -17,6 +18,7 @@ use rustc_span::Span; use std::ops::ControlFlow; 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. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs new file mode 100644 index 00000000000..061a5a4be20 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -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( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: Upcast, 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() + } +} 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/type_err_ctxt_ext.rs index 7843a95d966..ee5f4f44251 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs @@ -3,6 +3,7 @@ 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::{ @@ -23,7 +24,7 @@ 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::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey}; +use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; @@ -46,12 +47,9 @@ use rustc_middle::ty::{ TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; -use rustc_session::Limit; -use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; use rustc_span::{BytePos, ExpnKind, Span, Symbol, DUMMY_SP}; use std::borrow::Cow; -use std::fmt; use std::iter; use super::{ @@ -61,27 +59,6 @@ use super::{ pub use rustc_infer::traits::error_reporting::*; -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 TypeErrCtxtExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn report_fulfillment_errors( @@ -204,161 +181,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors")) } - /// 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( - &self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool, - ) -> ! - where - T: Upcast, 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() - } - /// 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`. diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index bda2137fa73..ca313590265 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -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; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 6f7e69798ee..5597c8be592 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -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; diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index a34fd804467..dda3aaaf71e 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -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; diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index da4e4fef3cf..c11e86abef8 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -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}; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1e55b2d0150..2c4ffdabf14 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -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; diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 51ac7b2c7ac..3ee5fd876ff 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -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, diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 85d84983d28..2d70fdc3935 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -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,