]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/values.rs
Rollup merge of #105655 - RedDocMD:bug-105645, r=oli-obk
[rust.git] / compiler / rustc_middle / src / values.rs
1 use crate::dep_graph::DepKind;
2 use rustc_data_structures::fx::FxHashSet;
3 use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
4 use rustc_hir as hir;
5 use rustc_hir::def::DefKind;
6 use rustc_middle::ty::Representability;
7 use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt};
8 use rustc_query_system::query::QueryInfo;
9 use rustc_query_system::Value;
10 use rustc_span::def_id::LocalDefId;
11 use rustc_span::Span;
12
13 use std::fmt::Write;
14
15 impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Ty<'_> {
16     fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self {
17         // SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
18         // FIXME: Represent the above fact in the trait system somehow.
19         unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(tcx.ty_error()) }
20     }
21 }
22
23 impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::SymbolName<'_> {
24     fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self {
25         // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
26         // FIXME: Represent the above fact in the trait system somehow.
27         unsafe {
28             std::mem::transmute::<ty::SymbolName<'tcx>, ty::SymbolName<'_>>(ty::SymbolName::new(
29                 tcx, "<error>",
30             ))
31         }
32     }
33 }
34
35 impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::Binder<'_, ty::FnSig<'_>> {
36     fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo<DepKind>]) -> Self {
37         let err = tcx.ty_error();
38
39         let arity = if let Some(frame) = stack.get(0)
40             && frame.query.dep_kind == DepKind::fn_sig
41             && let Some(def_id) = frame.query.def_id
42             && let Some(node) = tcx.hir().get_if_local(def_id)
43             && let Some(sig) = node.fn_sig()
44         {
45             sig.decl.inputs.len() + sig.decl.implicit_self.has_implicit_self() as usize
46         } else {
47             tcx.sess.abort_if_errors();
48             unreachable!()
49         };
50
51         let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig(
52             std::iter::repeat(err).take(arity),
53             err,
54             false,
55             rustc_hir::Unsafety::Normal,
56             rustc_target::spec::abi::Abi::Rust,
57         ));
58
59         // SAFETY: This is never called when `Self` is not `ty::Binder<'tcx, ty::FnSig<'tcx>>`.
60         // FIXME: Represent the above fact in the trait system somehow.
61         unsafe { std::mem::transmute::<ty::PolyFnSig<'tcx>, ty::Binder<'_, ty::FnSig<'_>>>(fn_sig) }
62     }
63 }
64
65 impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Representability {
66     fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
67         let mut item_and_field_ids = Vec::new();
68         let mut representable_ids = FxHashSet::default();
69         for info in cycle {
70             if info.query.dep_kind == DepKind::representability
71                 && let Some(field_id) = info.query.def_id
72                 && let Some(field_id) = field_id.as_local()
73                 && let Some(DefKind::Field) = info.query.def_kind
74             {
75                 let parent_id = tcx.parent(field_id.to_def_id());
76                 let item_id = match tcx.def_kind(parent_id) {
77                     DefKind::Variant => tcx.parent(parent_id),
78                     _ => parent_id,
79                 };
80                 item_and_field_ids.push((item_id.expect_local(), field_id));
81             }
82         }
83         for info in cycle {
84             if info.query.dep_kind == DepKind::representability_adt_ty
85                 && let Some(def_id) = info.query.ty_adt_id
86                 && let Some(def_id) = def_id.as_local()
87                 && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
88             {
89                 representable_ids.insert(def_id);
90             }
91         }
92         recursive_type_error(tcx, item_and_field_ids, &representable_ids);
93         Representability::Infinite
94     }
95 }
96
97 // item_and_field_ids should form a cycle where each field contains the
98 // type in the next element in the list
99 pub fn recursive_type_error(
100     tcx: TyCtxt<'_>,
101     mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
102     representable_ids: &FxHashSet<LocalDefId>,
103 ) {
104     const ITEM_LIMIT: usize = 5;
105
106     // Rotate the cycle so that the item with the lowest span is first
107     let start_index = item_and_field_ids
108         .iter()
109         .enumerate()
110         .min_by_key(|&(_, &(id, _))| tcx.def_span(id))
111         .unwrap()
112         .0;
113     item_and_field_ids.rotate_left(start_index);
114
115     let cycle_len = item_and_field_ids.len();
116     let show_cycle_len = cycle_len.min(ITEM_LIMIT);
117
118     let mut err_span = MultiSpan::from_spans(
119         item_and_field_ids[..show_cycle_len]
120             .iter()
121             .map(|(id, _)| tcx.def_span(id.to_def_id()))
122             .collect(),
123     );
124     let mut suggestion = Vec::with_capacity(show_cycle_len * 2);
125     for i in 0..show_cycle_len {
126         let (_, field_id) = item_and_field_ids[i];
127         let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len];
128         // Find the span(s) that contain the next item in the cycle
129         let hir_id = tcx.hir().local_def_id_to_hir_id(field_id);
130         let hir::Node::Field(field) = tcx.hir().get(hir_id) else { bug!("expected field") };
131         let mut found = Vec::new();
132         find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids);
133
134         // Couldn't find the type. Maybe it's behind a type alias?
135         // In any case, we'll just suggest boxing the whole field.
136         if found.is_empty() {
137             found.push(field.ty.span);
138         }
139
140         for span in found {
141             err_span.push_span_label(span, "recursive without indirection");
142             // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
143             suggestion.push((span.shrink_to_lo(), "Box<".to_string()));
144             suggestion.push((span.shrink_to_hi(), ">".to_string()));
145         }
146     }
147     let items_list = {
148         let mut s = String::new();
149         for (i, (item_id, _)) in item_and_field_ids.iter().enumerate() {
150             let path = tcx.def_path_str(item_id.to_def_id());
151             write!(&mut s, "`{path}`").unwrap();
152             if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT {
153                 write!(&mut s, " and {} more", cycle_len - 5).unwrap();
154                 break;
155             }
156             if cycle_len > 1 && i < cycle_len - 2 {
157                 s.push_str(", ");
158             } else if cycle_len > 1 && i == cycle_len - 2 {
159                 s.push_str(" and ")
160             }
161         }
162         s
163     };
164     let mut err = struct_span_err!(
165         tcx.sess,
166         err_span,
167         E0072,
168         "recursive type{} {} {} infinite size",
169         pluralize!(cycle_len),
170         items_list,
171         pluralize!("has", cycle_len),
172     );
173     err.multipart_suggestion(
174         "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle",
175         suggestion,
176         Applicability::HasPlaceholders,
177     );
178     err.emit();
179 }
180
181 fn find_item_ty_spans(
182     tcx: TyCtxt<'_>,
183     ty: &hir::Ty<'_>,
184     needle: LocalDefId,
185     spans: &mut Vec<Span>,
186     seen_representable: &FxHashSet<LocalDefId>,
187 ) {
188     match ty.kind {
189         hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
190             if let Some(def_id) = path.res.opt_def_id() {
191                 let check_params = def_id.as_local().map_or(true, |def_id| {
192                     if def_id == needle {
193                         spans.push(ty.span);
194                     }
195                     seen_representable.contains(&def_id)
196                 });
197                 if check_params && let Some(args) = path.segments.last().unwrap().args {
198                     let params_in_repr = tcx.params_in_repr(def_id);
199                     // the domain size check is needed because the HIR may not be well-formed at this point
200                     for (i, arg) in args.args.iter().enumerate().take(params_in_repr.domain_size()) {
201                         if let hir::GenericArg::Type(ty) = arg && params_in_repr.contains(i as u32) {
202                             find_item_ty_spans(tcx, ty, needle, spans, seen_representable);
203                         }
204                     }
205                 }
206             }
207         }
208         hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable),
209         hir::TyKind::Tup(tys) => {
210             tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable))
211         }
212         _ => {}
213     }
214 }