]>
Commit | Line | Data |
---|---|---|
9c376795 | 1 | use crate::dep_graph::DepKind; |
2b03887a FG |
2 | use rustc_data_structures::fx::FxHashSet; |
3 | use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; | |
4 | use rustc_hir as hir; | |
9ffffee4 | 5 | use rustc_hir::def::{DefKind, Res}; |
2b03887a | 6 | use rustc_middle::ty::Representability; |
353b0b11 | 7 | use rustc_middle::ty::{self, Ty, TyCtxt}; |
2b03887a | 8 | use rustc_query_system::query::QueryInfo; |
f2b60f7d | 9 | use rustc_query_system::Value; |
2b03887a FG |
10 | use rustc_span::def_id::LocalDefId; |
11 | use rustc_span::Span; | |
12 | ||
13 | use std::fmt::Write; | |
f2b60f7d | 14 | |
9c376795 FG |
15 | impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Ty<'_> { |
16 | fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self { | |
f2b60f7d FG |
17 | // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. |
18 | // FIXME: Represent the above fact in the trait system somehow. | |
fe692bf9 | 19 | unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_misc_error(tcx)) } |
f2b60f7d FG |
20 | } |
21 | } | |
22 | ||
9c376795 FG |
23 | impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::SymbolName<'_> { |
24 | fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self { | |
f2b60f7d FG |
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 | ||
9c376795 FG |
35 | impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::Binder<'_, ty::FnSig<'_>> { |
36 | fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo<DepKind>]) -> Self { | |
fe692bf9 | 37 | let err = Ty::new_misc_error(tcx); |
487cf647 FG |
38 | |
39 | let arity = if let Some(frame) = stack.get(0) | |
9c376795 | 40 | && frame.query.dep_kind == DepKind::fn_sig |
487cf647 FG |
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 | ||
f2b60f7d | 51 | let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig( |
487cf647 | 52 | std::iter::repeat(err).take(arity), |
f2b60f7d FG |
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 | } | |
2b03887a | 64 | |
9c376795 FG |
65 | impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Representability { |
66 | fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self { | |
2b03887a FG |
67 | let mut item_and_field_ids = Vec::new(); |
68 | let mut representable_ids = FxHashSet::default(); | |
69 | for info in cycle { | |
9c376795 | 70 | if info.query.dep_kind == DepKind::representability |
2b03887a FG |
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 { | |
9c376795 | 84 | if info.query.dep_kind == DepKind::representability_adt_ty |
2b03887a FG |
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 | ||
9c376795 FG |
97 | impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<Ty<'_>> { |
98 | fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self { | |
fe692bf9 | 99 | ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle)) |
9c376795 FG |
100 | } |
101 | } | |
102 | ||
103 | impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>> { | |
104 | fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self { | |
fe692bf9 | 105 | ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle)) |
9c376795 FG |
106 | } |
107 | } | |
108 | ||
fe692bf9 | 109 | impl<'tcx, T> Value<TyCtxt<'tcx>, DepKind> for Result<T, &'_ ty::layout::LayoutError<'_>> { |
49aad941 | 110 | fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo<DepKind>]) -> Self { |
fe692bf9 FG |
111 | // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under |
112 | // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really, | |
113 | // tcx.arena.alloc is pretty much equal to leaking). | |
114 | Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle))) | |
49aad941 FG |
115 | } |
116 | } | |
117 | ||
2b03887a FG |
118 | // item_and_field_ids should form a cycle where each field contains the |
119 | // type in the next element in the list | |
120 | pub fn recursive_type_error( | |
121 | tcx: TyCtxt<'_>, | |
122 | mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>, | |
123 | representable_ids: &FxHashSet<LocalDefId>, | |
124 | ) { | |
125 | const ITEM_LIMIT: usize = 5; | |
126 | ||
127 | // Rotate the cycle so that the item with the lowest span is first | |
128 | let start_index = item_and_field_ids | |
129 | .iter() | |
130 | .enumerate() | |
131 | .min_by_key(|&(_, &(id, _))| tcx.def_span(id)) | |
132 | .unwrap() | |
133 | .0; | |
134 | item_and_field_ids.rotate_left(start_index); | |
135 | ||
136 | let cycle_len = item_and_field_ids.len(); | |
137 | let show_cycle_len = cycle_len.min(ITEM_LIMIT); | |
138 | ||
139 | let mut err_span = MultiSpan::from_spans( | |
140 | item_and_field_ids[..show_cycle_len] | |
141 | .iter() | |
142 | .map(|(id, _)| tcx.def_span(id.to_def_id())) | |
143 | .collect(), | |
144 | ); | |
145 | let mut suggestion = Vec::with_capacity(show_cycle_len * 2); | |
146 | for i in 0..show_cycle_len { | |
147 | let (_, field_id) = item_and_field_ids[i]; | |
148 | let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len]; | |
149 | // Find the span(s) that contain the next item in the cycle | |
150 | let hir_id = tcx.hir().local_def_id_to_hir_id(field_id); | |
151 | let hir::Node::Field(field) = tcx.hir().get(hir_id) else { bug!("expected field") }; | |
152 | let mut found = Vec::new(); | |
153 | find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids); | |
154 | ||
155 | // Couldn't find the type. Maybe it's behind a type alias? | |
156 | // In any case, we'll just suggest boxing the whole field. | |
157 | if found.is_empty() { | |
158 | found.push(field.ty.span); | |
159 | } | |
160 | ||
161 | for span in found { | |
162 | err_span.push_span_label(span, "recursive without indirection"); | |
163 | // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed | |
164 | suggestion.push((span.shrink_to_lo(), "Box<".to_string())); | |
165 | suggestion.push((span.shrink_to_hi(), ">".to_string())); | |
166 | } | |
167 | } | |
168 | let items_list = { | |
169 | let mut s = String::new(); | |
49aad941 FG |
170 | for (i, &(item_id, _)) in item_and_field_ids.iter().enumerate() { |
171 | let path = tcx.def_path_str(item_id); | |
2b03887a FG |
172 | write!(&mut s, "`{path}`").unwrap(); |
173 | if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT { | |
174 | write!(&mut s, " and {} more", cycle_len - 5).unwrap(); | |
175 | break; | |
176 | } | |
177 | if cycle_len > 1 && i < cycle_len - 2 { | |
178 | s.push_str(", "); | |
179 | } else if cycle_len > 1 && i == cycle_len - 2 { | |
180 | s.push_str(" and ") | |
181 | } | |
182 | } | |
183 | s | |
184 | }; | |
185 | let mut err = struct_span_err!( | |
186 | tcx.sess, | |
187 | err_span, | |
188 | E0072, | |
189 | "recursive type{} {} {} infinite size", | |
190 | pluralize!(cycle_len), | |
191 | items_list, | |
192 | pluralize!("has", cycle_len), | |
193 | ); | |
194 | err.multipart_suggestion( | |
195 | "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle", | |
196 | suggestion, | |
197 | Applicability::HasPlaceholders, | |
198 | ); | |
199 | err.emit(); | |
200 | } | |
201 | ||
202 | fn find_item_ty_spans( | |
203 | tcx: TyCtxt<'_>, | |
204 | ty: &hir::Ty<'_>, | |
205 | needle: LocalDefId, | |
206 | spans: &mut Vec<Span>, | |
207 | seen_representable: &FxHashSet<LocalDefId>, | |
208 | ) { | |
209 | match ty.kind { | |
210 | hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { | |
9ffffee4 | 211 | if let Res::Def(kind, def_id) = path.res |
add651ee | 212 | && !matches!(kind, DefKind::TyAlias { .. }) { |
2b03887a FG |
213 | let check_params = def_id.as_local().map_or(true, |def_id| { |
214 | if def_id == needle { | |
215 | spans.push(ty.span); | |
216 | } | |
217 | seen_representable.contains(&def_id) | |
218 | }); | |
219 | if check_params && let Some(args) = path.segments.last().unwrap().args { | |
220 | let params_in_repr = tcx.params_in_repr(def_id); | |
487cf647 FG |
221 | // the domain size check is needed because the HIR may not be well-formed at this point |
222 | for (i, arg) in args.args.iter().enumerate().take(params_in_repr.domain_size()) { | |
2b03887a FG |
223 | if let hir::GenericArg::Type(ty) = arg && params_in_repr.contains(i as u32) { |
224 | find_item_ty_spans(tcx, ty, needle, spans, seen_representable); | |
225 | } | |
226 | } | |
227 | } | |
228 | } | |
229 | } | |
230 | hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable), | |
231 | hir::TyKind::Tup(tys) => { | |
232 | tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable)) | |
233 | } | |
234 | _ => {} | |
235 | } | |
236 | } |