diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index db98d6cf5ae5b..4395978eb899d 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -590,14 +590,15 @@ rustc_queries! { separate_provide_extern } - /// Checks whether a type is representable or infinitely sized - query check_representability(key: LocalDefId) -> rustc_middle::ty::Representability { + /// Checks whether a type is representable or infinitely sized, + /// causes a cycle error if it's infinitely sized. + query check_representability(key: LocalDefId) { desc { "checking if `{}` is representable", tcx.def_path_str(key) } - // Infinitely sized types will cause a cycle. The custom `FromCycleError` impl for - // `Representability` will print a custom error about the infinite size and then abort - // compilation. (In the past we recovered and continued, but in practice that leads to + // For infinitely sized types a cycle handler will print + // a custom error about the infinite size and then abort compilation. + // (In the past we recovered and continued, but in practice that leads to // confusing subsequent error messages about cycles that then abort.) - 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. This means we can't @@ -607,15 +608,16 @@ rustc_queries! { /// An implementation detail for the `check_representability` query. See that query for more /// details, particularly on the modifiers. - query check_representability_adt_ty(key: Ty<'tcx>) -> rustc_middle::ty::Representability { + query check_representability_adt_ty(key: Ty<'tcx>) { desc { "checking if `{}` is representable", key } - cycle_delay_bug anon } /// Set of param indexes for type params that are in the type's representation + /// + /// An implementation detail for the `representability` query query params_in_repr(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet { - desc { "finding type parameters in the representation" } + desc { "finding type parameters in the representation of `{}`", tcx.def_path_str(key) } arena_cache no_hash separate_provide_extern diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 4d4833b4943e4..c3db895f58782 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -396,7 +396,6 @@ impl_erasable_for_simple_types! { rustc_middle::ty::Destructor, rustc_middle::ty::fast_reject::SimplifiedType, rustc_middle::ty::ImplPolarity, - rustc_middle::ty::Representability, rustc_middle::ty::UnusedGenericParams, rustc_middle::ty::util::AlwaysRequiresDrop, rustc_middle::ty::Visibility, diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 6a51ea4deffee..70cebd3998e54 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -740,8 +740,3 @@ impl<'tcx> AdtDef<'tcx> { if self.is_struct() { tcx.adt_sizedness_constraint((self.did(), sizedness)) } else { None } } } - -/// This type exists just so a `FromCycleError` impl can be made for the `check_representability` -/// query. -#[derive(Clone, Copy, Debug, HashStable)] -pub struct Representability; diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 34be511ad3121..10826e7bb06c6 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -18,6 +18,7 @@ use rustc_span::{DUMMY_SP, Span}; use crate::collect_active_jobs_from_all_queries; use crate::dep_graph::{DepNode, DepNodeIndex}; +use crate::from_cycle_error::representability_cycle_handler; use crate::job::{QueryJobInfo, QueryJobMap, find_cycle_in_stack, report_cycle}; use crate::plumbing::{current_query_job, next_job_id, start_query}; @@ -125,6 +126,11 @@ fn mk_cycle<'tcx, C: QueryCache>( tcx: TyCtxt<'tcx>, cycle_error: CycleError, ) -> C::Value { + // Try to handle the cycle error with our custom handlers. + // They will unwind if they handle the cycle. + representability_cycle_handler(tcx, &cycle_error); + + // Create the default cycle error let error = report_cycle(tcx.sess, &cycle_error); match query.cycle_error_handling { CycleErrorHandling::Error => { diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index eb6942ba491ff..db578b68c3c50 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -10,7 +10,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_middle::dep_graph::DepKind; use rustc_middle::query::CycleError; use rustc_middle::query::plumbing::CyclePlaceholder; -use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span}; @@ -86,42 +86,51 @@ impl<'tcx> FromCycleError<'tcx> for ty::Binder<'_, ty::FnSig<'_>> { } } -impl<'tcx> FromCycleError<'tcx> for Representability { - fn from_cycle_error( - tcx: TyCtxt<'tcx>, - cycle_error: CycleError, - _guar: ErrorGuaranteed, - ) -> Self { - let mut item_and_field_ids = Vec::new(); - let mut representable_ids = FxHashSet::default(); - for info in &cycle_error.cycle { - if info.frame.dep_kind == DepKind::check_representability - && let Some(field_id) = info.frame.def_id - && let Some(field_id) = field_id.as_local() - && let Some(DefKind::Field) = info.frame.info.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)); - } +pub(super) fn representability_cycle_handler(tcx: TyCtxt<'_>, cycle_error: &CycleError) { + // We only handle cycle errors where `check_representability` is present and + // `check_representability_adt_ty` is the only potential additional query + let applies = cycle_error.cycle.iter().all(|query| { + matches!( + query.frame.dep_kind, + DepKind::check_representability | DepKind::check_representability_adt_ty + ) + }) && cycle_error + .cycle + .iter() + .find(|query| query.frame.dep_kind == DepKind::check_representability) + .is_some(); + if !applies { + return; + } + + let mut item_and_field_ids = Vec::new(); + let mut representable_ids = FxHashSet::default(); + for info in &cycle_error.cycle { + if info.frame.dep_kind == DepKind::check_representability + && let Some(field_id) = info.frame.def_id + && let Some(field_id) = field_id.as_local() + && let Some(DefKind::Field) = info.frame.info.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_error.cycle { - if info.frame.dep_kind == DepKind::check_representability_adt_ty - && let Some(def_id) = info.frame.def_id_for_ty_in_cycle - && let Some(def_id) = def_id.as_local() - && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) - { - representable_ids.insert(def_id); - } + } + for info in &cycle_error.cycle { + if info.frame.dep_kind == DepKind::check_representability_adt_ty + && let Some(def_id) = info.frame.def_id_for_ty_in_cycle + && let Some(def_id) = def_id.as_local() + && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) + { + representable_ids.insert(def_id); } - // We used to continue here, but the cycle error printed next is actually less useful than - // the error produced by `recursive_type_error`. - let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids); - guar.raise_fatal(); } + // We used to continue here, but the cycle error printed next is actually less useful than + // the error produced by `recursive_type_error`. + recursive_type_error(tcx, item_and_field_ids, &representable_ids).raise_fatal() } impl<'tcx> FromCycleError<'tcx> for ty::EarlyBinder<'_, Ty<'_>> { diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index 1814e7604a2d1..cef964ab139ae 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -2,7 +2,7 @@ use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; pub(crate) fn provide(providers: &mut Providers) { @@ -14,7 +14,7 @@ pub(crate) fn provide(providers: &mut Providers) { }; } -fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability { +fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) { match tcx.def_kind(def_id) { DefKind::Struct | DefKind::Union | DefKind::Enum => { for variant in tcx.adt_def(def_id).variants() { @@ -28,7 +28,6 @@ fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representabili } def_kind => bug!("unexpected {def_kind:?}"), } - Representability } fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { @@ -67,7 +66,7 @@ fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { // Looking at the query cycle above, we know that `Bar` is representable // because `check_representability_adt_ty(Bar<..>)` is in the cycle and // `check_representability(Bar)` is *not* in the cycle. -fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability { +fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { let ty::Adt(adt, args) = ty.kind() else { bug!("expected adt") }; if let Some(def_id) = adt.did().as_local() { let _ = tcx.check_representability(def_id); @@ -82,7 +81,6 @@ fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Repre } } } - Representability } fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DenseBitSet {