Skip to main content

rustc_query_impl/
values.rs

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