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