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