Skip to main content

rustc_query_impl/
handle_cycle_error.rs

1use std::collections::VecDeque;
2use std::fmt::Write;
3use std::iter;
4use std::ops::ControlFlow;
5
6use rustc_data_structures::fx::FxHashSet;
7use rustc_errors::codes::*;
8use rustc_errors::{Applicability, Diag, MultiSpan, pluralize, struct_span_code_err};
9use rustc_hir as hir;
10use rustc_hir::def::{DefKind, Res};
11use rustc_middle::bug;
12use rustc_middle::queries::TaggedQueryKey;
13use rustc_middle::query::Cycle;
14use rustc_middle::ty::{self, Ty, TyCtxt};
15use rustc_span::def_id::{DefId, LocalDefId};
16use rustc_span::{ErrorGuaranteed, Span};
17
18use crate::job::create_cycle_error;
19
20// Default cycle handler used for all queries that don't use the `handle_cycle_error` query
21// modifier.
22pub(crate) fn default(err: Diag<'_>) -> ! {
23    let guar = err.emit();
24    guar.raise_fatal()
25}
26
27pub(crate) fn fn_sig<'tcx>(
28    tcx: TyCtxt<'tcx>,
29    def_id: DefId,
30    _: Cycle<'tcx>,
31    err: Diag<'_>,
32) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> {
33    let guar = err.delay_as_bug();
34
35    let err = Ty::new_error(tcx, guar);
36
37    let arity = if let Some(node) = tcx.hir_get_if_local(def_id)
38        && let Some(sig) = node.fn_sig()
39    {
40        sig.decl.inputs.len()
41    } else {
42        tcx.dcx().abort_if_errors();
43        ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
44    };
45
46    ty::EarlyBinder::bind(ty::Binder::dummy(tcx.mk_fn_sig(
47        std::iter::repeat_n(err, arity),
48        err,
49        false,
50        rustc_hir::Safety::Safe,
51        rustc_abi::ExternAbi::Rust,
52    )))
53}
54
55pub(crate) fn check_representability<'tcx>(
56    tcx: TyCtxt<'tcx>,
57    _key: LocalDefId,
58    cycle: Cycle<'tcx>,
59    _err: Diag<'_>,
60) {
61    check_representability_inner(tcx, cycle);
62}
63
64pub(crate) fn check_representability_adt_ty<'tcx>(
65    tcx: TyCtxt<'tcx>,
66    _key: Ty<'tcx>,
67    cycle: Cycle<'tcx>,
68    _err: Diag<'_>,
69) {
70    check_representability_inner(tcx, cycle);
71}
72
73fn check_representability_inner<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> ! {
74    let mut item_and_field_ids = Vec::new();
75    let mut representable_ids = FxHashSet::default();
76    for frame in &cycle.frames {
77        if let TaggedQueryKey::check_representability(def_id) = frame.tagged_key
78            && tcx.def_kind(def_id) == DefKind::Field
79        {
80            let field_id: LocalDefId = def_id;
81            let parent_id = tcx.parent(field_id.to_def_id());
82            let item_id = match tcx.def_kind(parent_id) {
83                DefKind::Variant => tcx.parent(parent_id),
84                _ => parent_id,
85            };
86            item_and_field_ids.push((item_id.expect_local(), field_id));
87        }
88    }
89    for frame in &cycle.frames {
90        if let TaggedQueryKey::check_representability_adt_ty(key) = frame.tagged_key
91            && let Some(adt) = key.ty_adt_def()
92            && let Some(def_id) = adt.did().as_local()
93            && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
94        {
95            representable_ids.insert(def_id);
96        }
97    }
98    // We used to continue here, but the cycle error printed next is actually less useful than
99    // the error produced by `recursive_type_error`.
100    let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids);
101    guar.raise_fatal()
102}
103
104pub(crate) fn variances_of<'tcx>(
105    tcx: TyCtxt<'tcx>,
106    def_id: DefId,
107    _cycle: Cycle<'tcx>,
108    err: Diag<'_>,
109) -> &'tcx [ty::Variance] {
110    let _guar = err.delay_as_bug();
111    let n = tcx.generics_of(def_id).own_params.len();
112    tcx.arena.alloc_from_iter(iter::repeat_n(ty::Bivariant, n))
113}
114
115// Take a cycle of `Q` and try `try_cycle` on every permutation, falling back to `otherwise`.
116fn search_for_cycle_permutation<Q, T>(
117    cycle: &[Q],
118    try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow<T, ()>,
119    otherwise: impl FnOnce() -> T,
120) -> T {
121    let mut cycle: VecDeque<_> = cycle.iter().collect();
122    for _ in 0..cycle.len() {
123        match try_cycle(&mut cycle) {
124            ControlFlow::Continue(_) => {
125                cycle.rotate_left(1);
126            }
127            ControlFlow::Break(t) => return t,
128        }
129    }
130
131    otherwise()
132}
133
134pub(crate) fn layout_of<'tcx>(
135    tcx: TyCtxt<'tcx>,
136    _key: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
137    cycle: Cycle<'tcx>,
138    err: Diag<'_>,
139) -> Result<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
140    let _guar = err.delay_as_bug();
141    let diag = search_for_cycle_permutation(
142        &cycle.frames,
143        |frames| {
144            if let TaggedQueryKey::layout_of(key) = frames[0].tagged_key
145                && let ty::Coroutine(def_id, _) = key.value.kind()
146                && let Some(def_id) = def_id.as_local()
147                && let def_kind = tcx.def_kind(def_id)
148                && #[allow(non_exhaustive_omitted_patterns)] match def_kind {
    DefKind::Closure => true,
    _ => false,
}matches!(def_kind, DefKind::Closure)
149                && let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
150            {
151                // FIXME: `def_span` for an fn-like coroutine will point to the fn's body
152                // due to interactions between the desugaring into a closure expr and the
153                // def_span code. I'm not motivated to fix it, because I tried and it was
154                // not working, so just hack around it by grabbing the parent fn's span.
155                let span = if coroutine_kind.is_fn_like() {
156                    tcx.def_span(tcx.local_parent(def_id))
157                } else {
158                    tcx.def_span(def_id)
159                };
160                let mut diag = {
    tcx.sess.dcx().struct_span_err(span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("recursion in {0} {1} requires boxing",
                            tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
                            tcx.def_kind_descr(def_kind, def_id.to_def_id())))
                })).with_code(E0733)
}struct_span_code_err!(
161                    tcx.sess.dcx(),
162                    span,
163                    E0733,
164                    "recursion in {} {} requires boxing",
165                    tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
166                    tcx.def_kind_descr(def_kind, def_id.to_def_id()),
167                );
168                for (i, frame) in frames.iter().enumerate() {
169                    let TaggedQueryKey::layout_of(frame_key) = frame.tagged_key else {
170                        continue;
171                    };
172                    let &ty::Coroutine(frame_def_id, _) = frame_key.value.kind() else {
173                        continue;
174                    };
175                    let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
176                        continue;
177                    };
178                    let frame_span =
179                        frame.tagged_key.default_span(tcx, frames[(i + 1) % frames.len()].span);
180                    if frame_span.is_dummy() {
181                        continue;
182                    }
183                    if i == 0 {
184                        diag.span_label(frame_span, "recursive call here");
185                    } else {
186                        let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() {
187                            tcx.def_span(tcx.parent(frame_def_id))
188                        } else {
189                            tcx.def_span(frame_def_id)
190                        };
191                        let mut multispan = MultiSpan::from_span(coroutine_span);
192                        multispan.push_span_label(frame_span, "...leading to this recursive call");
193                        diag.span_note(
194                            multispan,
195                            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("which leads to this {0}",
                tcx.def_descr(frame_def_id)))
    })format!("which leads to this {}", tcx.def_descr(frame_def_id)),
196                        );
197                    }
198                }
199                // FIXME: We could report a structured suggestion if we had
200                // enough info here... Maybe we can use a hacky HIR walker.
201                if #[allow(non_exhaustive_omitted_patterns)] match coroutine_kind {
    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => true,
    _ => false,
}matches!(
202                    coroutine_kind,
203                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
204                ) {
205                    diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future");
206                }
207
208                ControlFlow::Break(diag)
209            } else {
210                ControlFlow::Continue(())
211            }
212        },
213        || create_cycle_error(tcx, &cycle),
214    );
215
216    diag.emit().raise_fatal()
217}
218
219// item_and_field_ids should form a cycle where each field contains the
220// type in the next element in the list
221fn recursive_type_error(
222    tcx: TyCtxt<'_>,
223    mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
224    representable_ids: &FxHashSet<LocalDefId>,
225) -> ErrorGuaranteed {
226    const ITEM_LIMIT: usize = 5;
227
228    // Rotate the cycle so that the item with the lowest span is first
229    let start_index = item_and_field_ids
230        .iter()
231        .enumerate()
232        .min_by_key(|&(_, &(id, _))| tcx.def_span(id))
233        .unwrap()
234        .0;
235    item_and_field_ids.rotate_left(start_index);
236
237    let cycle_len = item_and_field_ids.len();
238    let show_cycle_len = cycle_len.min(ITEM_LIMIT);
239
240    let mut err_span = MultiSpan::from_spans(
241        item_and_field_ids[..show_cycle_len]
242            .iter()
243            .map(|(id, _)| tcx.def_span(id.to_def_id()))
244            .collect(),
245    );
246    let mut suggestion = Vec::with_capacity(show_cycle_len * 2);
247    for i in 0..show_cycle_len {
248        let (_, field_id) = item_and_field_ids[i];
249        let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len];
250        // Find the span(s) that contain the next item in the cycle
251        let hir::Node::Field(field) = tcx.hir_node_by_def_id(field_id) else {
252            ::rustc_middle::util::bug::bug_fmt(format_args!("expected field"))bug!("expected field")
253        };
254        let mut found = Vec::new();
255        find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids);
256
257        // Couldn't find the type. Maybe it's behind a type alias?
258        // In any case, we'll just suggest boxing the whole field.
259        if found.is_empty() {
260            found.push(field.ty.span);
261        }
262
263        for span in found {
264            err_span.push_span_label(span, "recursive without indirection");
265            // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
266            suggestion.push((span.shrink_to_lo(), "Box<".to_string()));
267            suggestion.push((span.shrink_to_hi(), ">".to_string()));
268        }
269    }
270    let items_list = {
271        let mut s = String::new();
272        for (i, &(item_id, _)) in item_and_field_ids.iter().enumerate() {
273            let path = tcx.def_path_str(item_id);
274            (&mut s).write_fmt(format_args!("`{0}`", path))write!(&mut s, "`{path}`").unwrap();
275            if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT {
276                (&mut s).write_fmt(format_args!(" and {0} more", cycle_len - 5))write!(&mut s, " and {} more", cycle_len - 5).unwrap();
277                break;
278            }
279            if cycle_len > 1 && i < cycle_len - 2 {
280                s.push_str(", ");
281            } else if cycle_len > 1 && i == cycle_len - 2 {
282                s.push_str(" and ")
283            }
284        }
285        s
286    };
287    {
    tcx.dcx().struct_span_err(err_span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("recursive type{0} {1} {2} infinite size",
                            if cycle_len == 1 { "" } else { "s" }, items_list,
                            if cycle_len == 1 { "has" } else { "have" }))
                })).with_code(E0072)
}struct_span_code_err!(
288        tcx.dcx(),
289        err_span,
290        E0072,
291        "recursive type{} {} {} infinite size",
292        pluralize!(cycle_len),
293        items_list,
294        pluralize!("has", cycle_len),
295    )
296    .with_multipart_suggestion(
297        "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle",
298        suggestion,
299        Applicability::HasPlaceholders,
300    )
301    .emit()
302}
303
304fn find_item_ty_spans(
305    tcx: TyCtxt<'_>,
306    ty: &hir::Ty<'_>,
307    needle: LocalDefId,
308    spans: &mut Vec<Span>,
309    seen_representable: &FxHashSet<LocalDefId>,
310) {
311    match ty.kind {
312        hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
313            if let Res::Def(kind, def_id) = path.res
314                && #[allow(non_exhaustive_omitted_patterns)] match kind {
    DefKind::Enum | DefKind::Struct | DefKind::Union => true,
    _ => false,
}matches!(kind, DefKind::Enum | DefKind::Struct | DefKind::Union)
315            {
316                let check_params = def_id.as_local().is_none_or(|def_id| {
317                    if def_id == needle {
318                        spans.push(ty.span);
319                    }
320                    seen_representable.contains(&def_id)
321                });
322                if check_params && let Some(args) = path.segments.last().unwrap().args {
323                    let params_in_repr = tcx.params_in_repr(def_id);
324                    // the domain size check is needed because the HIR may not be well-formed at this point
325                    for (i, arg) in args.args.iter().enumerate().take(params_in_repr.domain_size())
326                    {
327                        if let hir::GenericArg::Type(ty) = arg
328                            && params_in_repr.contains(i as u32)
329                        {
330                            find_item_ty_spans(
331                                tcx,
332                                ty.as_unambig_ty(),
333                                needle,
334                                spans,
335                                seen_representable,
336                            );
337                        }
338                    }
339                }
340            }
341        }
342        hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable),
343        hir::TyKind::Tup(tys) => {
344            tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable))
345        }
346        _ => {}
347    }
348}