Skip to content

Commit 7af825f

Browse files
Split out overflow handling into its own module
1 parent a2d5819 commit 7af825f

File tree

10 files changed

+208
-187
lines changed

10 files changed

+208
-187
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pub mod ambiguity;
44
mod infer_ctxt_ext;
55
pub mod on_unimplemented;
6+
mod overflow;
67
pub mod suggestions;
78
mod type_err_ctxt_ext;
89

@@ -17,6 +18,7 @@ use rustc_span::Span;
1718
use std::ops::ControlFlow;
1819

1920
pub use self::infer_ctxt_ext::*;
21+
pub use self::overflow::*;
2022
pub use self::type_err_ctxt_ext::*;
2123

2224
// When outputting impl candidates, prefer showing those that are more similar.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
use std::fmt;
2+
3+
use rustc_errors::{
4+
struct_span_code_err, Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, E0275,
5+
};
6+
use rustc_hir::def::Namespace;
7+
use rustc_hir::def_id::LOCAL_CRATE;
8+
use rustc_infer::infer::error_reporting::TypeErrCtxt;
9+
use rustc_infer::traits::{Obligation, PredicateObligation};
10+
use rustc_macros::extension;
11+
use rustc_middle::ty::print::{FmtPrinter, Print};
12+
use rustc_middle::ty::{self, TyCtxt};
13+
use rustc_session::Limit;
14+
use rustc_span::Span;
15+
use rustc_type_ir::Upcast;
16+
17+
use super::InferCtxtPrivExt;
18+
use crate::error_reporting::traits::suggestions::TypeErrCtxtExt;
19+
20+
pub enum OverflowCause<'tcx> {
21+
DeeplyNormalize(ty::AliasTerm<'tcx>),
22+
TraitSolver(ty::Predicate<'tcx>),
23+
}
24+
25+
pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
26+
tcx: TyCtxt<'tcx>,
27+
err: &mut Diag<'_, G>,
28+
) {
29+
let suggested_limit = match tcx.recursion_limit() {
30+
Limit(0) => Limit(2),
31+
limit => limit * 2,
32+
};
33+
err.help(format!(
34+
"consider increasing the recursion limit by adding a \
35+
`#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
36+
suggested_limit,
37+
tcx.crate_name(LOCAL_CRATE),
38+
));
39+
}
40+
41+
#[extension(pub trait TypeErrCtxtOverflowExt<'a, 'tcx>)]
42+
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
43+
/// Reports that an overflow has occurred and halts compilation. We
44+
/// halt compilation unconditionally because it is important that
45+
/// overflows never be masked -- they basically represent computations
46+
/// whose result could not be truly determined and thus we can't say
47+
/// if the program type checks or not -- and they are unusual
48+
/// occurrences in any case.
49+
fn report_overflow_error(
50+
&self,
51+
cause: OverflowCause<'tcx>,
52+
span: Span,
53+
suggest_increasing_limit: bool,
54+
mutate: impl FnOnce(&mut Diag<'_>),
55+
) -> ! {
56+
let mut err = self.build_overflow_error(cause, span, suggest_increasing_limit);
57+
mutate(&mut err);
58+
err.emit();
59+
FatalError.raise();
60+
}
61+
62+
fn build_overflow_error(
63+
&self,
64+
cause: OverflowCause<'tcx>,
65+
span: Span,
66+
suggest_increasing_limit: bool,
67+
) -> Diag<'a> {
68+
fn with_short_path<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> String
69+
where
70+
T: fmt::Display + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
71+
{
72+
let s = value.to_string();
73+
if s.len() > 50 {
74+
// We don't need to save the type to a file, we will be talking about this type already
75+
// in a separate note when we explain the obligation, so it will be available that way.
76+
let mut cx: FmtPrinter<'_, '_> =
77+
FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, rustc_session::Limit(6));
78+
value.print(&mut cx).unwrap();
79+
cx.into_buffer()
80+
} else {
81+
s
82+
}
83+
}
84+
85+
let mut err = match cause {
86+
OverflowCause::DeeplyNormalize(alias_term) => {
87+
let alias_term = self.resolve_vars_if_possible(alias_term);
88+
let kind = alias_term.kind(self.tcx).descr();
89+
let alias_str = with_short_path(self.tcx, alias_term);
90+
struct_span_code_err!(
91+
self.dcx(),
92+
span,
93+
E0275,
94+
"overflow normalizing the {kind} `{alias_str}`",
95+
)
96+
}
97+
OverflowCause::TraitSolver(predicate) => {
98+
let predicate = self.resolve_vars_if_possible(predicate);
99+
match predicate.kind().skip_binder() {
100+
ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ })
101+
| ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
102+
struct_span_code_err!(
103+
self.dcx(),
104+
span,
105+
E0275,
106+
"overflow assigning `{a}` to `{b}`",
107+
)
108+
}
109+
_ => {
110+
let pred_str = with_short_path(self.tcx, predicate);
111+
struct_span_code_err!(
112+
self.dcx(),
113+
span,
114+
E0275,
115+
"overflow evaluating the requirement `{pred_str}`",
116+
)
117+
}
118+
}
119+
}
120+
};
121+
122+
if suggest_increasing_limit {
123+
suggest_new_overflow_limit(self.tcx, &mut err);
124+
}
125+
126+
err
127+
}
128+
129+
/// Reports that an overflow has occurred and halts compilation. We
130+
/// halt compilation unconditionally because it is important that
131+
/// overflows never be masked -- they basically represent computations
132+
/// whose result could not be truly determined and thus we can't say
133+
/// if the program type checks or not -- and they are unusual
134+
/// occurrences in any case.
135+
fn report_overflow_obligation<T>(
136+
&self,
137+
obligation: &Obligation<'tcx, T>,
138+
suggest_increasing_limit: bool,
139+
) -> !
140+
where
141+
T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>> + Clone,
142+
{
143+
let predicate = obligation.predicate.clone().upcast(self.tcx);
144+
let predicate = self.resolve_vars_if_possible(predicate);
145+
self.report_overflow_error(
146+
OverflowCause::TraitSolver(predicate),
147+
obligation.cause.span,
148+
suggest_increasing_limit,
149+
|err| {
150+
self.note_obligation_cause_code(
151+
obligation.cause.body_id,
152+
err,
153+
predicate,
154+
obligation.param_env,
155+
obligation.cause.code(),
156+
&mut vec![],
157+
&mut Default::default(),
158+
);
159+
},
160+
);
161+
}
162+
163+
/// Reports that a cycle was detected which led to overflow and halts
164+
/// compilation. This is equivalent to `report_overflow_obligation` except
165+
/// that we can give a more helpful error message (and, in particular,
166+
/// we do not suggest increasing the overflow limit, which is not
167+
/// going to help).
168+
fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
169+
let cycle = self.resolve_vars_if_possible(cycle.to_owned());
170+
assert!(!cycle.is_empty());
171+
172+
debug!(?cycle, "report_overflow_error_cycle");
173+
174+
// The 'deepest' obligation is most likely to have a useful
175+
// cause 'backtrace'
176+
self.report_overflow_obligation(
177+
cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(),
178+
false,
179+
);
180+
}
181+
182+
fn report_overflow_no_abort(
183+
&self,
184+
obligation: PredicateObligation<'tcx>,
185+
suggest_increasing_limit: bool,
186+
) -> ErrorGuaranteed {
187+
let obligation = self.resolve_vars_if_possible(obligation);
188+
let mut err = self.build_overflow_error(
189+
OverflowCause::TraitSolver(obligation.predicate),
190+
obligation.cause.span,
191+
suggest_increasing_limit,
192+
);
193+
self.note_obligation_cause(&mut err, &obligation);
194+
self.point_at_returns_when_relevant(&mut err, &obligation);
195+
err.emit()
196+
}
197+
}

0 commit comments

Comments
 (0)