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