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::DepKind;
11use rustc_middle::query::CycleError;
12use rustc_middle::query::plumbing::CyclePlaceholder;
13use rustc_middle::ty::{self, Representability, Ty, TyCtxt};
14use rustc_middle::{bug, span_bug};
15use rustc_span::def_id::LocalDefId;
16use rustc_span::{ErrorGuaranteed, Span};
17
18use crate::job::report_cycle;
19
20pub(crate) trait FromCycleError<'tcx>: Sized {
21 fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self;
26}
27
28impl<'tcx, T> FromCycleError<'tcx> for T {
29 default fn from_cycle_error(
30 tcx: TyCtxt<'tcx>,
31 cycle_error: CycleError,
32 _guar: ErrorGuaranteed,
33 ) -> T {
34 let Some(guar) = tcx.sess.dcx().has_errors() else {
35 ::rustc_middle::util::bug::bug_fmt(format_args!("<{0} as FromCycleError>::from_cycle_error called without errors: {1:#?}",
std::any::type_name::<T>(), cycle_error.cycle));bug!(
36 "<{} as FromCycleError>::from_cycle_error called without errors: {:#?}",
37 std::any::type_name::<T>(),
38 cycle_error.cycle,
39 );
40 };
41 guar.raise_fatal();
42 }
43}
44
45impl<'tcx> FromCycleError<'tcx> for Ty<'_> {
46 fn from_cycle_error(tcx: TyCtxt<'tcx>, _: CycleError, guar: ErrorGuaranteed) -> Self {
47 unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_error(tcx, guar)) }
50 }
51}
52
53impl<'tcx> FromCycleError<'tcx> for Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
54 fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: CycleError, guar: ErrorGuaranteed) -> Self {
55 Err(CyclePlaceholder(guar))
56 }
57}
58
59impl<'tcx> FromCycleError<'tcx> for ty::Binder<'_, ty::FnSig<'_>> {
60 fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self {
61 let err = Ty::new_error(tcx, guar);
62
63 let arity = if let Some(info) = cycle_error.cycle.get(0)
64 && info.frame.dep_kind == DepKind::fn_sig
65 && let Some(def_id) = info.frame.def_id
66 && let Some(node) = tcx.hir_get_if_local(def_id)
67 && let Some(sig) = node.fn_sig()
68 {
69 sig.decl.inputs.len()
70 } else {
71 tcx.dcx().abort_if_errors();
72 ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
73 };
74
75 let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig(
76 std::iter::repeat_n(err, arity),
77 err,
78 false,
79 rustc_hir::Safety::Safe,
80 rustc_abi::ExternAbi::Rust,
81 ));
82
83 unsafe { std::mem::transmute::<ty::PolyFnSig<'tcx>, ty::Binder<'_, ty::FnSig<'_>>>(fn_sig) }
86 }
87}
88
89impl<'tcx> FromCycleError<'tcx> for Representability {
90 fn from_cycle_error(
91 tcx: TyCtxt<'tcx>,
92 cycle_error: CycleError,
93 _guar: ErrorGuaranteed,
94 ) -> Self {
95 let mut item_and_field_ids = Vec::new();
96 let mut representable_ids = FxHashSet::default();
97 for info in &cycle_error.cycle {
98 if info.frame.dep_kind == DepKind::check_representability
99 && let Some(field_id) = info.frame.def_id
100 && let Some(field_id) = field_id.as_local()
101 && let Some(DefKind::Field) = info.frame.info.def_kind
102 {
103 let parent_id = tcx.parent(field_id.to_def_id());
104 let item_id = match tcx.def_kind(parent_id) {
105 DefKind::Variant => tcx.parent(parent_id),
106 _ => parent_id,
107 };
108 item_and_field_ids.push((item_id.expect_local(), field_id));
109 }
110 }
111 for info in &cycle_error.cycle {
112 if info.frame.dep_kind == DepKind::check_representability_adt_ty
113 && let Some(def_id) = info.frame.def_id_for_ty_in_cycle
114 && let Some(def_id) = def_id.as_local()
115 && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
116 {
117 representable_ids.insert(def_id);
118 }
119 }
120 let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids);
123 guar.raise_fatal();
124 }
125}
126
127impl<'tcx> FromCycleError<'tcx> for ty::EarlyBinder<'_, Ty<'_>> {
128 fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self {
129 ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle_error, guar))
130 }
131}
132
133impl<'tcx> FromCycleError<'tcx> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> {
134 fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self {
135 ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle_error, guar))
136 }
137}
138
139impl<'tcx> FromCycleError<'tcx> for &[ty::Variance] {
140 fn from_cycle_error(
141 tcx: TyCtxt<'tcx>,
142 cycle_error: CycleError,
143 _guar: ErrorGuaranteed,
144 ) -> Self {
145 search_for_cycle_permutation(
146 &cycle_error.cycle,
147 |cycle| {
148 if let Some(info) = cycle.get(0)
149 && info.frame.dep_kind == DepKind::variances_of
150 && let Some(def_id) = info.frame.def_id
151 {
152 let n = tcx.generics_of(def_id).own_params.len();
153 ControlFlow::Break(::alloc::vec::from_elem(ty::Bivariant, n)vec![ty::Bivariant; n].leak())
154 } else {
155 ControlFlow::Continue(())
156 }
157 },
158 || {
159 ::rustc_middle::util::bug::span_bug_fmt(cycle_error.usage.as_ref().unwrap().0,
format_args!("only `variances_of` returns `&[ty::Variance]`"))span_bug!(
160 cycle_error.usage.as_ref().unwrap().0,
161 "only `variances_of` returns `&[ty::Variance]`"
162 )
163 },
164 )
165 }
166}
167
168fn search_for_cycle_permutation<Q, T>(
170 cycle: &[Q],
171 try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow<T, ()>,
172 otherwise: impl FnOnce() -> T,
173) -> T {
174 let mut cycle: VecDeque<_> = cycle.iter().collect();
175 for _ in 0..cycle.len() {
176 match try_cycle(&mut cycle) {
177 ControlFlow::Continue(_) => {
178 cycle.rotate_left(1);
179 }
180 ControlFlow::Break(t) => return t,
181 }
182 }
183
184 otherwise()
185}
186
187impl<'tcx, T> FromCycleError<'tcx> for Result<T, &'_ ty::layout::LayoutError<'_>> {
188 fn from_cycle_error(
189 tcx: TyCtxt<'tcx>,
190 cycle_error: CycleError,
191 _guar: ErrorGuaranteed,
192 ) -> Self {
193 let diag = search_for_cycle_permutation(
194 &cycle_error.cycle,
195 |cycle| {
196 if cycle[0].frame.dep_kind == DepKind::layout_of
197 && let Some(def_id) = cycle[0].frame.def_id_for_ty_in_cycle
198 && let Some(def_id) = def_id.as_local()
199 && let def_kind = tcx.def_kind(def_id)
200 && #[allow(non_exhaustive_omitted_patterns)] match def_kind {
DefKind::Closure => true,
_ => false,
}matches!(def_kind, DefKind::Closure)
201 && let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
202 {
203 let span = if coroutine_kind.is_fn_like() {
208 tcx.def_span(tcx.local_parent(def_id))
209 } else {
210 tcx.def_span(def_id)
211 };
212 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!(
213 tcx.sess.dcx(),
214 span,
215 E0733,
216 "recursion in {} {} requires boxing",
217 tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
218 tcx.def_kind_descr(def_kind, def_id.to_def_id()),
219 );
220 for (i, info) in cycle.iter().enumerate() {
221 if info.frame.dep_kind != DepKind::layout_of {
222 continue;
223 }
224 let Some(frame_def_id) = info.frame.def_id_for_ty_in_cycle else {
225 continue;
226 };
227 let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
228 continue;
229 };
230 let frame_span =
231 info.frame.info.default_span(cycle[(i + 1) % cycle.len()].span);
232 if frame_span.is_dummy() {
233 continue;
234 }
235 if i == 0 {
236 diag.span_label(frame_span, "recursive call here");
237 } else {
238 let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() {
239 tcx.def_span(tcx.parent(frame_def_id))
240 } else {
241 tcx.def_span(frame_def_id)
242 };
243 let mut multispan = MultiSpan::from_span(coroutine_span);
244 multispan
245 .push_span_label(frame_span, "...leading to this recursive call");
246 diag.span_note(
247 multispan,
248 ::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)),
249 );
250 }
251 }
252 if #[allow(non_exhaustive_omitted_patterns)] match coroutine_kind {
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => true,
_ => false,
}matches!(
255 coroutine_kind,
256 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
257 ) {
258 diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future");
259 }
260
261 ControlFlow::Break(diag)
262 } else {
263 ControlFlow::Continue(())
264 }
265 },
266 || report_cycle(tcx.sess, &cycle_error),
267 );
268
269 let guar = diag.emit();
270
271 Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle(guar))))
275 }
276}
277
278fn recursive_type_error(
281 tcx: TyCtxt<'_>,
282 mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
283 representable_ids: &FxHashSet<LocalDefId>,
284) -> ErrorGuaranteed {
285 const ITEM_LIMIT: usize = 5;
286
287 let start_index = item_and_field_ids
289 .iter()
290 .enumerate()
291 .min_by_key(|&(_, &(id, _))| tcx.def_span(id))
292 .unwrap()
293 .0;
294 item_and_field_ids.rotate_left(start_index);
295
296 let cycle_len = item_and_field_ids.len();
297 let show_cycle_len = cycle_len.min(ITEM_LIMIT);
298
299 let mut err_span = MultiSpan::from_spans(
300 item_and_field_ids[..show_cycle_len]
301 .iter()
302 .map(|(id, _)| tcx.def_span(id.to_def_id()))
303 .collect(),
304 );
305 let mut suggestion = Vec::with_capacity(show_cycle_len * 2);
306 for i in 0..show_cycle_len {
307 let (_, field_id) = item_and_field_ids[i];
308 let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len];
309 let hir::Node::Field(field) = tcx.hir_node_by_def_id(field_id) else {
311 ::rustc_middle::util::bug::bug_fmt(format_args!("expected field"))bug!("expected field")
312 };
313 let mut found = Vec::new();
314 find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids);
315
316 if found.is_empty() {
319 found.push(field.ty.span);
320 }
321
322 for span in found {
323 err_span.push_span_label(span, "recursive without indirection");
324 suggestion.push((span.shrink_to_lo(), "Box<".to_string()));
326 suggestion.push((span.shrink_to_hi(), ">".to_string()));
327 }
328 }
329 let items_list = {
330 let mut s = String::new();
331 for (i, &(item_id, _)) in item_and_field_ids.iter().enumerate() {
332 let path = tcx.def_path_str(item_id);
333 (&mut s).write_fmt(format_args!("`{0}`", path))write!(&mut s, "`{path}`").unwrap();
334 if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT {
335 (&mut s).write_fmt(format_args!(" and {0} more", cycle_len - 5))write!(&mut s, " and {} more", cycle_len - 5).unwrap();
336 break;
337 }
338 if cycle_len > 1 && i < cycle_len - 2 {
339 s.push_str(", ");
340 } else if cycle_len > 1 && i == cycle_len - 2 {
341 s.push_str(" and ")
342 }
343 }
344 s
345 };
346 {
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!(
347 tcx.dcx(),
348 err_span,
349 E0072,
350 "recursive type{} {} {} infinite size",
351 pluralize!(cycle_len),
352 items_list,
353 pluralize!("has", cycle_len),
354 )
355 .with_multipart_suggestion(
356 "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle",
357 suggestion,
358 Applicability::HasPlaceholders,
359 )
360 .emit()
361}
362
363fn find_item_ty_spans(
364 tcx: TyCtxt<'_>,
365 ty: &hir::Ty<'_>,
366 needle: LocalDefId,
367 spans: &mut Vec<Span>,
368 seen_representable: &FxHashSet<LocalDefId>,
369) {
370 match ty.kind {
371 hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
372 if let Res::Def(kind, def_id) = path.res
373 && #[allow(non_exhaustive_omitted_patterns)] match kind {
DefKind::Enum | DefKind::Struct | DefKind::Union => true,
_ => false,
}matches!(kind, DefKind::Enum | DefKind::Struct | DefKind::Union)
374 {
375 let check_params = def_id.as_local().is_none_or(|def_id| {
376 if def_id == needle {
377 spans.push(ty.span);
378 }
379 seen_representable.contains(&def_id)
380 });
381 if check_params && let Some(args) = path.segments.last().unwrap().args {
382 let params_in_repr = tcx.params_in_repr(def_id);
383 for (i, arg) in args.args.iter().enumerate().take(params_in_repr.domain_size())
385 {
386 if let hir::GenericArg::Type(ty) = arg
387 && params_in_repr.contains(i as u32)
388 {
389 find_item_ty_spans(
390 tcx,
391 ty.as_unambig_ty(),
392 needle,
393 spans,
394 seen_representable,
395 );
396 }
397 }
398 }
399 }
400 }
401 hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable),
402 hir::TyKind::Tup(tys) => {
403 tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable))
404 }
405 _ => {}
406 }
407}