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