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