rustc_trait_selection/error_reporting/traits/
overflow.rs

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