1use std::collections::VecDeque;
2use std::fmt::Write;
3use std::iter;
4use std::ops::ControlFlow;
5
6use rustc_data_structures::fx::FxHashSet;
7use rustc_errors::codes::*;
8use rustc_errors::{Applicability, Diag, MultiSpan, pluralize, struct_span_code_err};
9use rustc_hir as hir;
10use rustc_hir::def::{DefKind, Res};
11use rustc_middle::bug;
12use rustc_middle::queries::{QueryVTables, TaggedQueryKey};
13use rustc_middle::query::Cycle;
14use rustc_middle::query::erase::erase_val;
15use rustc_middle::ty::layout::LayoutError;
16use rustc_middle::ty::{self, Ty, TyCtxt};
17use rustc_span::def_id::{DefId, LocalDefId};
18use rustc_span::{ErrorGuaranteed, Span};
19
20use crate::job::create_cycle_error;
21
22pub(crate) fn specialize_query_vtables<'tcx>(vtables: &mut QueryVTables<'tcx>) {
23 vtables.fn_sig.handle_cycle_error_fn = |tcx, key, _, err| {
24 let guar = err.delay_as_bug();
25 erase_val(fn_sig(tcx, key, guar))
26 };
27
28 vtables.check_representability.handle_cycle_error_fn =
29 |tcx, _, cycle, _err| check_representability(tcx, cycle);
30
31 vtables.check_representability_adt_ty.handle_cycle_error_fn =
32 |tcx, _, cycle, _err| check_representability(tcx, cycle);
33
34 vtables.variances_of.handle_cycle_error_fn = |tcx, key, _, err| {
35 let _guar = err.delay_as_bug();
36 erase_val(variances_of(tcx, key))
37 };
38
39 vtables.layout_of.handle_cycle_error_fn = |tcx, _, cycle, err| {
40 let _guar = err.delay_as_bug();
41 erase_val(Err(layout_of(tcx, cycle)))
42 }
43}
44
45pub(crate) fn default(err: Diag<'_>) -> ! {
46 let guar = err.emit();
47 guar.raise_fatal()
48}
49
50fn fn_sig<'tcx>(
51 tcx: TyCtxt<'tcx>,
52 def_id: DefId,
53 guar: ErrorGuaranteed,
54) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> {
55 let err = Ty::new_error(tcx, guar);
56
57 let arity = if let Some(node) = tcx.hir_get_if_local(def_id)
58 && let Some(sig) = node.fn_sig()
59 {
60 sig.decl.inputs.len()
61 } else {
62 tcx.dcx().abort_if_errors();
63 ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
64 };
65
66 ty::EarlyBinder::bind(ty::Binder::dummy(tcx.mk_fn_sig(
67 std::iter::repeat_n(err, arity),
68 err,
69 false,
70 rustc_hir::Safety::Safe,
71 rustc_abi::ExternAbi::Rust,
72 )))
73}
74
75fn check_representability<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> ! {
76 let mut item_and_field_ids = Vec::new();
77 let mut representable_ids = FxHashSet::default();
78 for frame in &cycle.frames {
79 if let TaggedQueryKey::check_representability(def_id) = frame.tagged_key
80 && tcx.def_kind(def_id) == DefKind::Field
81 {
82 let field_id: LocalDefId = def_id;
83 let parent_id = tcx.parent(field_id.to_def_id());
84 let item_id = match tcx.def_kind(parent_id) {
85 DefKind::Variant => tcx.parent(parent_id),
86 _ => parent_id,
87 };
88 item_and_field_ids.push((item_id.expect_local(), field_id));
89 }
90 }
91 for frame in &cycle.frames {
92 if let TaggedQueryKey::check_representability_adt_ty(key) = frame.tagged_key
93 && let Some(adt) = key.ty_adt_def()
94 && let Some(def_id) = adt.did().as_local()
95 && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
96 {
97 representable_ids.insert(def_id);
98 }
99 }
100 let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids);
103 guar.raise_fatal()
104}
105
106fn variances_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx [ty::Variance] {
107 let n = tcx.generics_of(def_id).own_params.len();
108 tcx.arena.alloc_from_iter(iter::repeat_n(ty::Bivariant, n))
109}
110
111fn search_for_cycle_permutation<Q, T>(
113 cycle: &[Q],
114 try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow<T, ()>,
115 otherwise: impl FnOnce() -> T,
116) -> T {
117 let mut cycle: VecDeque<_> = cycle.iter().collect();
118 for _ in 0..cycle.len() {
119 match try_cycle(&mut cycle) {
120 ControlFlow::Continue(_) => {
121 cycle.rotate_left(1);
122 }
123 ControlFlow::Break(t) => return t,
124 }
125 }
126
127 otherwise()
128}
129
130fn layout_of<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> &'tcx ty::layout::LayoutError<'tcx> {
131 let diag = search_for_cycle_permutation(
132 &cycle.frames,
133 |frames| {
134 if let TaggedQueryKey::layout_of(key) = frames[0].tagged_key
135 && let ty::Coroutine(def_id, _) = key.value.kind()
136 && let Some(def_id) = def_id.as_local()
137 && let def_kind = tcx.def_kind(def_id)
138 && #[allow(non_exhaustive_omitted_patterns)] match def_kind {
DefKind::Closure => true,
_ => false,
}matches!(def_kind, DefKind::Closure)
139 && let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
140 {
141 let span = if coroutine_kind.is_fn_like() {
146 tcx.def_span(tcx.local_parent(def_id))
147 } else {
148 tcx.def_span(def_id)
149 };
150 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!(
151 tcx.sess.dcx(),
152 span,
153 E0733,
154 "recursion in {} {} requires boxing",
155 tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
156 tcx.def_kind_descr(def_kind, def_id.to_def_id()),
157 );
158 for (i, frame) in frames.iter().enumerate() {
159 let TaggedQueryKey::layout_of(frame_key) = frame.tagged_key else {
160 continue;
161 };
162 let &ty::Coroutine(frame_def_id, _) = frame_key.value.kind() else {
163 continue;
164 };
165 let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
166 continue;
167 };
168 let frame_span =
169 frame.tagged_key.default_span(tcx, frames[(i + 1) % frames.len()].span);
170 if frame_span.is_dummy() {
171 continue;
172 }
173 if i == 0 {
174 diag.span_label(frame_span, "recursive call here");
175 } else {
176 let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() {
177 tcx.def_span(tcx.parent(frame_def_id))
178 } else {
179 tcx.def_span(frame_def_id)
180 };
181 let mut multispan = MultiSpan::from_span(coroutine_span);
182 multispan.push_span_label(frame_span, "...leading to this recursive call");
183 diag.span_note(
184 multispan,
185 ::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)),
186 );
187 }
188 }
189 if #[allow(non_exhaustive_omitted_patterns)] match coroutine_kind {
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => true,
_ => false,
}matches!(
192 coroutine_kind,
193 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
194 ) {
195 diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future");
196 }
197
198 ControlFlow::Break(diag)
199 } else {
200 ControlFlow::Continue(())
201 }
202 },
203 || create_cycle_error(tcx, &cycle),
204 );
205
206 let guar = diag.emit();
207 tcx.arena.alloc(LayoutError::Cycle(guar))
208}
209
210fn recursive_type_error(
213 tcx: TyCtxt<'_>,
214 mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
215 representable_ids: &FxHashSet<LocalDefId>,
216) -> ErrorGuaranteed {
217 const ITEM_LIMIT: usize = 5;
218
219 let start_index = item_and_field_ids
221 .iter()
222 .enumerate()
223 .min_by_key(|&(_, &(id, _))| tcx.def_span(id))
224 .unwrap()
225 .0;
226 item_and_field_ids.rotate_left(start_index);
227
228 let cycle_len = item_and_field_ids.len();
229 let show_cycle_len = cycle_len.min(ITEM_LIMIT);
230
231 let mut err_span = MultiSpan::from_spans(
232 item_and_field_ids[..show_cycle_len]
233 .iter()
234 .map(|(id, _)| tcx.def_span(id.to_def_id()))
235 .collect(),
236 );
237 let mut suggestion = Vec::with_capacity(show_cycle_len * 2);
238 for i in 0..show_cycle_len {
239 let (_, field_id) = item_and_field_ids[i];
240 let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len];
241 let hir::Node::Field(field) = tcx.hir_node_by_def_id(field_id) else {
243 ::rustc_middle::util::bug::bug_fmt(format_args!("expected field"))bug!("expected field")
244 };
245 let mut found = Vec::new();
246 find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids);
247
248 if found.is_empty() {
251 found.push(field.ty.span);
252 }
253
254 for span in found {
255 err_span.push_span_label(span, "recursive without indirection");
256 suggestion.push((span.shrink_to_lo(), "Box<".to_string()));
258 suggestion.push((span.shrink_to_hi(), ">".to_string()));
259 }
260 }
261 let items_list = {
262 let mut s = String::new();
263 for (i, &(item_id, _)) in item_and_field_ids.iter().enumerate() {
264 let path = tcx.def_path_str(item_id);
265 (&mut s).write_fmt(format_args!("`{0}`", path))write!(&mut s, "`{path}`").unwrap();
266 if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT {
267 (&mut s).write_fmt(format_args!(" and {0} more", cycle_len - 5))write!(&mut s, " and {} more", cycle_len - 5).unwrap();
268 break;
269 }
270 if cycle_len > 1 && i < cycle_len - 2 {
271 s.push_str(", ");
272 } else if cycle_len > 1 && i == cycle_len - 2 {
273 s.push_str(" and ")
274 }
275 }
276 s
277 };
278 {
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!(
279 tcx.dcx(),
280 err_span,
281 E0072,
282 "recursive type{} {} {} infinite size",
283 pluralize!(cycle_len),
284 items_list,
285 pluralize!("has", cycle_len),
286 )
287 .with_multipart_suggestion(
288 "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle",
289 suggestion,
290 Applicability::HasPlaceholders,
291 )
292 .emit()
293}
294
295fn find_item_ty_spans(
296 tcx: TyCtxt<'_>,
297 ty: &hir::Ty<'_>,
298 needle: LocalDefId,
299 spans: &mut Vec<Span>,
300 seen_representable: &FxHashSet<LocalDefId>,
301) {
302 match ty.kind {
303 hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
304 if let Res::Def(kind, def_id) = path.res
305 && #[allow(non_exhaustive_omitted_patterns)] match kind {
DefKind::Enum | DefKind::Struct | DefKind::Union => true,
_ => false,
}matches!(kind, DefKind::Enum | DefKind::Struct | DefKind::Union)
306 {
307 let check_params = def_id.as_local().is_none_or(|def_id| {
308 if def_id == needle {
309 spans.push(ty.span);
310 }
311 seen_representable.contains(&def_id)
312 });
313 if check_params && let Some(args) = path.segments.last().unwrap().args {
314 let params_in_repr = tcx.params_in_repr(def_id);
315 for (i, arg) in args.args.iter().enumerate().take(params_in_repr.domain_size())
317 {
318 if let hir::GenericArg::Type(ty) = arg
319 && params_in_repr.contains(i as u32)
320 {
321 find_item_ty_spans(
322 tcx,
323 ty.as_unambig_ty(),
324 needle,
325 spans,
326 seen_representable,
327 );
328 }
329 }
330 }
331 }
332 }
333 hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable),
334 hir::TyKind::Tup(tys) => {
335 tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable))
336 }
337 _ => {}
338 }
339}