diff options
| author | bors <bors@rust-lang.org> | 2022-10-08 11:53:25 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-10-08 11:53:25 +0000 |
| commit | bba9785dd73f61aacd301a2cb379e1e85a129047 (patch) | |
| tree | 1325e86a86ce101c60353107590ac9f990a3e41c /compiler/rustc_middle | |
| parent | a688a0305fad9219505a8f2576446510601bafe8 (diff) | |
| parent | ff940db666daeae83c6c71685901c6c14df17018 (diff) | |
| download | rust-bba9785dd73f61aacd301a2cb379e1e85a129047.tar.gz rust-bba9785dd73f61aacd301a2cb379e1e85a129047.zip | |
Auto merge of #100720 - camsteffen:representable, r=cjgillot
Rewrite representability * Improve placement of `Box` in the suggestion * Multiple items in a cycle emit 1 error instead of an error for each item in the cycle * Introduce `representability` query to avoid traversing an item every time it is used. * Also introduce `params_in_repr` query to avoid traversing generic items every time it is used.
Diffstat (limited to 'compiler/rustc_middle')
| -rw-r--r-- | compiler/rustc_middle/src/arena.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/query/mod.rs | 26 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/adt.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/parameterized.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/values.rs | 170 |
5 files changed, 200 insertions, 5 deletions
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 6bdf5a023b6..d2847e4bc12 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -103,6 +103,7 @@ macro_rules! arena_types { [] dep_kind: rustc_middle::dep_graph::DepKindStruct<'tcx>, [decode] trait_impl_trait_tys: rustc_data_structures::fx::FxHashMap<rustc_hir::def_id::DefId, rustc_middle::ty::Ty<'tcx>>, + [] bit_set_u32: rustc_index::bit_set::BitSet<u32>, ]); ) } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 334eb953cbc..a175b1eb467 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -301,6 +301,32 @@ rustc_queries! { separate_provide_extern } + /// Checks whether a type is representable or infinitely sized + query representability(_: LocalDefId) -> rustc_middle::ty::Representability { + desc { "checking if {:?} is representable", tcx.def_path_str(key.to_def_id()) } + // infinitely sized types will cause a cycle + cycle_delay_bug + // we don't want recursive representability calls to be forced with + // incremental compilation because, if a cycle occurs, we need the + // entire cycle to be in memory for diagnostics + anon + } + + /// An implementation detail for the `representability` query + query representability_adt_ty(_: Ty<'tcx>) -> rustc_middle::ty::Representability { + desc { "checking if {:?} is representable", key } + cycle_delay_bug + anon + } + + /// Set of param indexes for type params that are in the type's representation + query params_in_repr(key: DefId) -> rustc_index::bit_set::BitSet<u32> { + desc { "finding type parameters in the representation" } + arena_cache + no_hash + separate_provide_extern + } + /// Fetch the THIR for a given body. If typeck for that body failed, returns an empty `Thir`. query thir_body(key: ty::WithOptConstParam<LocalDefId>) -> Result<(&'tcx Steal<thir::Thir<'tcx>>, thir::ExprId), ErrorGuaranteed> diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 3c485e26409..80bbc8e630e 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -566,3 +566,10 @@ impl<'tcx> AdtDef<'tcx> { ty::EarlyBinder(tcx.adt_sized_constraint(self.did()).0) } } + +#[derive(Clone, Copy, Debug)] +#[derive(HashStable)] +pub enum Representability { + Representable, + Infinite, +} diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 0257ca7b29c..f289f2265a2 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -82,6 +82,7 @@ trivially_parameterized_over_tcx! { rustc_hir::def::DefKind, rustc_hir::def_id::DefIndex, rustc_hir::definitions::DefKey, + rustc_index::bit_set::BitSet<u32>, rustc_index::bit_set::FiniteBitSet<u32>, rustc_session::cstore::ForeignModule, rustc_session::cstore::LinkagePreference, diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 7fbe9ae2a84..bb89959b29d 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -1,8 +1,18 @@ -use rustc_middle::ty::{self, AdtSizedConstraint, Ty, TyCtxt}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_middle::ty::Representability; +use rustc_middle::ty::{self, AdtSizedConstraint, DefIdTree, Ty, TyCtxt}; +use rustc_query_system::query::QueryInfo; use rustc_query_system::Value; +use rustc_span::def_id::LocalDefId; +use rustc_span::Span; + +use std::fmt::Write; impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self { // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(tcx.ty_error()) } @@ -10,7 +20,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> { } impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self { // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. unsafe { @@ -22,7 +32,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> { } impl<'tcx> Value<TyCtxt<'tcx>> for AdtSizedConstraint<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self { // SAFETY: This is never called when `Self` is not `AdtSizedConstraint<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. unsafe { @@ -34,7 +44,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for AdtSizedConstraint<'_> { } impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> { - fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self { let err = tcx.ty_error(); // FIXME(compiler-errors): It would be nice if we could get the // query key, so we could at least generate a fn signature that @@ -52,3 +62,153 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> { unsafe { std::mem::transmute::<ty::PolyFnSig<'tcx>, ty::Binder<'_, ty::FnSig<'_>>>(fn_sig) } } } + +impl<'tcx> Value<TyCtxt<'tcx>> for Representability { + fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo]) -> Self { + let mut item_and_field_ids = Vec::new(); + let mut representable_ids = FxHashSet::default(); + for info in cycle { + if info.query.name == "representability" + && let Some(field_id) = info.query.def_id + && let Some(field_id) = field_id.as_local() + && let Some(DefKind::Field) = info.query.def_kind + { + let parent_id = tcx.parent(field_id.to_def_id()); + let item_id = match tcx.def_kind(parent_id) { + DefKind::Variant => tcx.parent(parent_id), + _ => parent_id, + }; + item_and_field_ids.push((item_id.expect_local(), field_id)); + } + } + for info in cycle { + if info.query.name == "representability_adt_ty" + && let Some(def_id) = info.query.ty_adt_id + && let Some(def_id) = def_id.as_local() + && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) + { + representable_ids.insert(def_id); + } + } + recursive_type_error(tcx, item_and_field_ids, &representable_ids); + Representability::Infinite + } +} + +// item_and_field_ids should form a cycle where each field contains the +// type in the next element in the list +pub fn recursive_type_error( + tcx: TyCtxt<'_>, + mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>, + representable_ids: &FxHashSet<LocalDefId>, +) { + const ITEM_LIMIT: usize = 5; + + // Rotate the cycle so that the item with the lowest span is first + let start_index = item_and_field_ids + .iter() + .enumerate() + .min_by_key(|&(_, &(id, _))| tcx.def_span(id)) + .unwrap() + .0; + item_and_field_ids.rotate_left(start_index); + + let cycle_len = item_and_field_ids.len(); + let show_cycle_len = cycle_len.min(ITEM_LIMIT); + + let mut err_span = MultiSpan::from_spans( + item_and_field_ids[..show_cycle_len] + .iter() + .map(|(id, _)| tcx.def_span(id.to_def_id())) + .collect(), + ); + let mut suggestion = Vec::with_capacity(show_cycle_len * 2); + for i in 0..show_cycle_len { + let (_, field_id) = item_and_field_ids[i]; + let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len]; + // Find the span(s) that contain the next item in the cycle + let hir_id = tcx.hir().local_def_id_to_hir_id(field_id); + let hir::Node::Field(field) = tcx.hir().get(hir_id) else { bug!("expected field") }; + let mut found = Vec::new(); + find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids); + + // Couldn't find the type. Maybe it's behind a type alias? + // In any case, we'll just suggest boxing the whole field. + if found.is_empty() { + found.push(field.ty.span); + } + + for span in found { + err_span.push_span_label(span, "recursive without indirection"); + // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed + suggestion.push((span.shrink_to_lo(), "Box<".to_string())); + suggestion.push((span.shrink_to_hi(), ">".to_string())); + } + } + let items_list = { + let mut s = String::new(); + for (i, (item_id, _)) in item_and_field_ids.iter().enumerate() { + let path = tcx.def_path_str(item_id.to_def_id()); + write!(&mut s, "`{path}`").unwrap(); + if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT { + write!(&mut s, " and {} more", cycle_len - 5).unwrap(); + break; + } + if cycle_len > 1 && i < cycle_len - 2 { + s.push_str(", "); + } else if cycle_len > 1 && i == cycle_len - 2 { + s.push_str(" and ") + } + } + s + }; + let mut err = struct_span_err!( + tcx.sess, + err_span, + E0072, + "recursive type{} {} {} infinite size", + pluralize!(cycle_len), + items_list, + pluralize!("has", cycle_len), + ); + err.multipart_suggestion( + "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle", + suggestion, + Applicability::HasPlaceholders, + ); + err.emit(); +} + +fn find_item_ty_spans( + tcx: TyCtxt<'_>, + ty: &hir::Ty<'_>, + needle: LocalDefId, + spans: &mut Vec<Span>, + seen_representable: &FxHashSet<LocalDefId>, +) { + match ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { + if let Some(def_id) = path.res.opt_def_id() { + let check_params = def_id.as_local().map_or(true, |def_id| { + if def_id == needle { + spans.push(ty.span); + } + seen_representable.contains(&def_id) + }); + if check_params && let Some(args) = path.segments.last().unwrap().args { + let params_in_repr = tcx.params_in_repr(def_id); + for (i, arg) in args.args.iter().enumerate() { + if let hir::GenericArg::Type(ty) = arg && params_in_repr.contains(i as u32) { + find_item_ty_spans(tcx, ty, needle, spans, seen_representable); + } + } + } + } + } + hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable), + hir::TyKind::Tup(tys) => { + tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable)) + } + _ => {} + } +} |
