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 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 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 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
164fn 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 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 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 Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle(guar))))
271 }
272}
273
274pub 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 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 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 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 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 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}