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 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 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 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
185fn 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 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 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 Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle(guar))))
292 }
293}
294
295fn 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 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 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 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 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 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}