Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite representability #100720

Merged
merged 1 commit into from
Oct 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3552,7 +3552,6 @@ dependencies = [
"rustc_span",
"rustc_target",
"rustc_trait_selection",
"rustc_ty_utils",
"rustc_type_ir",
"smallvec",
"tracing",
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_hir_analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ rustc_span = { path = "../rustc_span" }
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_ty_utils = { path = "../rustc_ty_utils" }
rustc_lint = { path = "../rustc_lint" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_type_ir = { path = "../rustc_type_ir" }
Expand Down
28 changes: 3 additions & 25 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ use rustc_span::{self, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCtxt};
use rustc_ty_utils::representability::{self, Representability};

use std::ops::ControlFlow;

Expand Down Expand Up @@ -381,7 +380,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let def = tcx.adt_def(def_id);
let span = tcx.def_span(def_id);
def.destructor(tcx); // force the destructor to be evaluated
check_representable(tcx, span, def_id);
let _ = tcx.representability(def_id);

if def.repr().simd() {
check_simd(tcx, span, def_id);
Expand All @@ -395,7 +394,7 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let def = tcx.adt_def(def_id);
let span = tcx.def_span(def_id);
def.destructor(tcx); // force the destructor to be evaluated
check_representable(tcx, span, def_id);
let _ = tcx.representability(def_id);
check_transparent(tcx, span, def);
check_union_fields(tcx, span, def_id);
check_packed(tcx, span, def);
Expand Down Expand Up @@ -1151,27 +1150,6 @@ fn check_impl_items_against_trait<'tcx>(
}
}

/// Checks whether a type can be represented in memory. In particular, it
/// identifies types that contain themselves without indirection through a
/// pointer, which would mean their size is unbounded.
pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool {
let rty = tcx.type_of(item_def_id);

// Check that it is possible to represent this type. This call identifies
// (1) types that contain themselves and (2) types that contain a different
// recursive type. It is only necessary to throw an error on those that
// contain themselves. For case 2, there must be an inner type that will be
// caught by case 1.
match representability::ty_is_representable(tcx, rty, sp, None) {
Representability::SelfRecursive(spans) => {
recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans);
return false;
}
Representability::Representable | Representability::ContainsRecursive => (),
}
true
}

pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
let t = tcx.type_of(def_id);
if let ty::Adt(def, substs) = t.kind()
Expand Down Expand Up @@ -1509,7 +1487,7 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L

detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp);

check_representable(tcx, sp, def_id);
let _ = tcx.representability(def_id);
check_transparent(tcx, sp, def);
}

Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ use rustc_span::{self, BytePos, Span, Symbol};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error;
use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
use std::cell::RefCell;
use std::num::NonZeroU32;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ macro_rules! pluralize {
($x:expr) => {
if $x != 1 { "s" } else { "" }
};
("has", $x:expr) => {
if $x == 1 { "has" } else { "have" }
};
("is", $x:expr) => {
if $x == 1 { "is" } else { "are" }
};
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ provide! { tcx, def_id, other, cdata,
lookup_const_stability => { table }
lookup_default_body_stability => { table }
lookup_deprecation_entry => { table }
params_in_repr => { table }
unused_generic_params => { table }
opt_def_kind => { table_direct }
impl_parent => { table }
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
if let DefKind::Trait | DefKind::TraitAlias = def_kind {
record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id));
}
if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind {
let params_in_repr = self.tcx.params_in_repr(def_id);
record!(self.tables.params_in_repr[def_id] <- params_in_repr);
}
if should_encode_trait_impl_trait_tys(tcx, def_id)
&& let Ok(table) = self.tcx.collect_trait_impl_trait_tys(def_id)
{
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, DefPathHash, StableCrateId};
use rustc_hir::definitions::DefKey;
use rustc_hir::lang_items;
use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
use rustc_index::bit_set::{BitSet, FiniteBitSet};
use rustc_index::vec::IndexVec;
use rustc_middle::metadata::ModChild;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
Expand Down Expand Up @@ -383,6 +384,7 @@ define_tables! {
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
unused_generic_params: Table<DefIndex, LazyValue<FiniteBitSet<u32>>>,
params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>,
repr_options: Table<DefIndex, LazyValue<ReprOptions>>,
// `def_keys` and `def_path_hashes` represent a lazy version of a
// `DefPathTable`. This allows us to avoid deserializing an entire
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
]);
)
}
Expand Down
26 changes: 26 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/parameterized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
170 changes: 165 additions & 5 deletions compiler/rustc_middle/src/values.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
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()) }
}
}

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 {
Expand All @@ -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 {
Expand All @@ -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
Expand All @@ -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))
}
_ => {}
}
}
Loading