1 use rustc_data_structures::fx::FxHashSet;
2 use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
4 use rustc_hir::def::DefKind;
5 use rustc_middle::ty::Representability;
6 use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt};
7 use rustc_query_system::query::QueryInfo;
8 use rustc_query_system::Value;
9 use rustc_span::def_id::LocalDefId;
14 impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
15 fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
16 // SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
17 // FIXME: Represent the above fact in the trait system somehow.
18 unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(tcx.ty_error()) }
22 impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
23 fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
24 // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
25 // FIXME: Represent the above fact in the trait system somehow.
27 std::mem::transmute::<ty::SymbolName<'tcx>, ty::SymbolName<'_>>(ty::SymbolName::new(
34 impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
35 fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo]) -> Self {
36 let err = tcx.ty_error();
38 let arity = if let Some(frame) = stack.get(0)
39 && frame.query.name == "fn_sig"
40 && let Some(def_id) = frame.query.def_id
41 && let Some(node) = tcx.hir().get_if_local(def_id)
42 && let Some(sig) = node.fn_sig()
44 sig.decl.inputs.len() + sig.decl.implicit_self.has_implicit_self() as usize
46 tcx.sess.abort_if_errors();
50 let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig(
51 std::iter::repeat(err).take(arity),
54 rustc_hir::Unsafety::Normal,
55 rustc_target::spec::abi::Abi::Rust,
58 // SAFETY: This is never called when `Self` is not `ty::Binder<'tcx, ty::FnSig<'tcx>>`.
59 // FIXME: Represent the above fact in the trait system somehow.
60 unsafe { std::mem::transmute::<ty::PolyFnSig<'tcx>, ty::Binder<'_, ty::FnSig<'_>>>(fn_sig) }
64 impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
65 fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo]) -> Self {
66 let mut item_and_field_ids = Vec::new();
67 let mut representable_ids = FxHashSet::default();
69 if info.query.name == "representability"
70 && let Some(field_id) = info.query.def_id
71 && let Some(field_id) = field_id.as_local()
72 && let Some(DefKind::Field) = info.query.def_kind
74 let parent_id = tcx.parent(field_id.to_def_id());
75 let item_id = match tcx.def_kind(parent_id) {
76 DefKind::Variant => tcx.parent(parent_id),
79 item_and_field_ids.push((item_id.expect_local(), field_id));
83 if info.query.name == "representability_adt_ty"
84 && let Some(def_id) = info.query.ty_adt_id
85 && let Some(def_id) = def_id.as_local()
86 && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
88 representable_ids.insert(def_id);
91 recursive_type_error(tcx, item_and_field_ids, &representable_ids);
92 Representability::Infinite
96 // item_and_field_ids should form a cycle where each field contains the
97 // type in the next element in the list
98 pub fn recursive_type_error(
100 mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
101 representable_ids: &FxHashSet<LocalDefId>,
103 const ITEM_LIMIT: usize = 5;
105 // Rotate the cycle so that the item with the lowest span is first
106 let start_index = item_and_field_ids
109 .min_by_key(|&(_, &(id, _))| tcx.def_span(id))
112 item_and_field_ids.rotate_left(start_index);
114 let cycle_len = item_and_field_ids.len();
115 let show_cycle_len = cycle_len.min(ITEM_LIMIT);
117 let mut err_span = MultiSpan::from_spans(
118 item_and_field_ids[..show_cycle_len]
120 .map(|(id, _)| tcx.def_span(id.to_def_id()))
123 let mut suggestion = Vec::with_capacity(show_cycle_len * 2);
124 for i in 0..show_cycle_len {
125 let (_, field_id) = item_and_field_ids[i];
126 let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len];
127 // Find the span(s) that contain the next item in the cycle
128 let hir_id = tcx.hir().local_def_id_to_hir_id(field_id);
129 let hir::Node::Field(field) = tcx.hir().get(hir_id) else { bug!("expected field") };
130 let mut found = Vec::new();
131 find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids);
133 // Couldn't find the type. Maybe it's behind a type alias?
134 // In any case, we'll just suggest boxing the whole field.
135 if found.is_empty() {
136 found.push(field.ty.span);
140 err_span.push_span_label(span, "recursive without indirection");
141 // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
142 suggestion.push((span.shrink_to_lo(), "Box<".to_string()));
143 suggestion.push((span.shrink_to_hi(), ">".to_string()));
147 let mut s = String::new();
148 for (i, (item_id, _)) in item_and_field_ids.iter().enumerate() {
149 let path = tcx.def_path_str(item_id.to_def_id());
150 write!(&mut s, "`{path}`").unwrap();
151 if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT {
152 write!(&mut s, " and {} more", cycle_len - 5).unwrap();
155 if cycle_len > 1 && i < cycle_len - 2 {
157 } else if cycle_len > 1 && i == cycle_len - 2 {
163 let mut err = struct_span_err!(
167 "recursive type{} {} {} infinite size",
168 pluralize!(cycle_len),
170 pluralize!("has", cycle_len),
172 err.multipart_suggestion(
173 "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle",
175 Applicability::HasPlaceholders,
180 fn find_item_ty_spans(
184 spans: &mut Vec<Span>,
185 seen_representable: &FxHashSet<LocalDefId>,
188 hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
189 if let Some(def_id) = path.res.opt_def_id() {
190 let check_params = def_id.as_local().map_or(true, |def_id| {
191 if def_id == needle {
194 seen_representable.contains(&def_id)
196 if check_params && let Some(args) = path.segments.last().unwrap().args {
197 let params_in_repr = tcx.params_in_repr(def_id);
198 // the domain size check is needed because the HIR may not be well-formed at this point
199 for (i, arg) in args.args.iter().enumerate().take(params_in_repr.domain_size()) {
200 if let hir::GenericArg::Type(ty) = arg && params_in_repr.contains(i as u32) {
201 find_item_ty_spans(tcx, ty, needle, spans, seen_representable);
207 hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable),
208 hir::TyKind::Tup(tys) => {
209 tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable))