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