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