Skip to content

Commit febe0bc

Browse files
Rollup merge of #153317 - nnethercote:abort-after-infinite-errors, r=oli-obk
Abort after `representability` errors Doing so results in better error messages and makes the code a bit simpler. Details in individual commits. r? @oli-obk
2 parents ed9d772 + ff3d308 commit febe0bc

File tree

21 files changed

+84
-184
lines changed

21 files changed

+84
-184
lines changed

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ fn check_type_defn<'tcx>(
997997
item: &hir::Item<'tcx>,
998998
all_sized: bool,
999999
) -> Result<(), ErrorGuaranteed> {
1000-
let _ = tcx.representability(item.owner_id.def_id);
1000+
let _ = tcx.check_representability(item.owner_id.def_id);
10011001
let adt_def = tcx.adt_def(item.owner_id);
10021002

10031003
enter_wf_checking_ctxt(tcx, item.owner_id.def_id, |wfcx| {

compiler/rustc_middle/src/queries.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -593,18 +593,23 @@ rustc_queries! {
593593
}
594594

595595
/// Checks whether a type is representable or infinitely sized
596-
query representability(key: LocalDefId) -> rustc_middle::ty::Representability {
596+
query check_representability(key: LocalDefId) -> rustc_middle::ty::Representability {
597597
desc { "checking if `{}` is representable", tcx.def_path_str(key) }
598-
// infinitely sized types will cause a cycle
598+
// Infinitely sized types will cause a cycle. The custom `FromCycleError` impl for
599+
// `Representability` will print a custom error about the infinite size and then abort
600+
// compilation. (In the past we recovered and continued, but in practice that leads to
601+
// confusing subsequent error messages about cycles that then abort.)
599602
cycle_delay_bug
600-
// we don't want recursive representability calls to be forced with
603+
// We don't want recursive representability calls to be forced with
601604
// incremental compilation because, if a cycle occurs, we need the
602-
// entire cycle to be in memory for diagnostics
605+
// entire cycle to be in memory for diagnostics. This means we can't
606+
// use `ensure_ok()` with this query.
603607
anon
604608
}
605609

606-
/// An implementation detail for the `representability` query
607-
query representability_adt_ty(key: Ty<'tcx>) -> rustc_middle::ty::Representability {
610+
/// An implementation detail for the `check_representability` query. See that query for more
611+
/// details, particularly on the modifiers.
612+
query check_representability_adt_ty(key: Ty<'tcx>) -> rustc_middle::ty::Representability {
608613
desc { "checking if `{}` is representable", key }
609614
cycle_delay_bug
610615
anon

compiler/rustc_middle/src/ty/adt.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -741,8 +741,7 @@ impl<'tcx> AdtDef<'tcx> {
741741
}
742742
}
743743

744+
/// This type exists just so a `FromCycleError` impl can be made for the `check_representability`
745+
/// query.
744746
#[derive(Clone, Copy, Debug, HashStable)]
745-
pub enum Representability {
746-
Representable,
747-
Infinite(ErrorGuaranteed),
748-
}
747+
pub struct Representability;

compiler/rustc_middle/src/ty/inhabitedness/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,9 @@ pub(crate) fn provide(providers: &mut Providers) {
6161
/// requires calling [`InhabitedPredicate::instantiate`]
6262
fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> {
6363
if let Some(def_id) = def_id.as_local() {
64-
if matches!(tcx.representability(def_id), ty::Representability::Infinite(_)) {
65-
return InhabitedPredicate::True;
66-
}
64+
let _ = tcx.check_representability(def_id);
6765
}
66+
6867
let adt = tcx.adt_def(def_id);
6968
InhabitedPredicate::any(
7069
tcx,

compiler/rustc_query_impl/src/from_cycle_error.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ impl<'tcx> FromCycleError<'tcx> for Representability {
9595
let mut item_and_field_ids = Vec::new();
9696
let mut representable_ids = FxHashSet::default();
9797
for info in &cycle_error.cycle {
98-
if info.frame.dep_kind == DepKind::representability
98+
if info.frame.dep_kind == DepKind::check_representability
9999
&& let Some(field_id) = info.frame.def_id
100100
&& let Some(field_id) = field_id.as_local()
101101
&& let Some(DefKind::Field) = info.frame.info.def_kind
@@ -109,16 +109,18 @@ impl<'tcx> FromCycleError<'tcx> for Representability {
109109
}
110110
}
111111
for info in &cycle_error.cycle {
112-
if info.frame.dep_kind == DepKind::representability_adt_ty
112+
if info.frame.dep_kind == DepKind::check_representability_adt_ty
113113
&& let Some(def_id) = info.frame.def_id_for_ty_in_cycle
114114
&& let Some(def_id) = def_id.as_local()
115115
&& !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
116116
{
117117
representable_ids.insert(def_id);
118118
}
119119
}
120+
// We used to continue here, but the cycle error printed next is actually less useful than
121+
// the error produced by `recursive_type_error`.
120122
let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids);
121-
Representability::Infinite(guar)
123+
guar.raise_fatal();
122124
}
123125
}
124126

compiler/rustc_ty_utils/src/representability.rs

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,81 +6,83 @@ use rustc_middle::ty::{self, Representability, Ty, TyCtxt};
66
use rustc_span::def_id::LocalDefId;
77

88
pub(crate) fn provide(providers: &mut Providers) {
9-
*providers =
10-
Providers { representability, representability_adt_ty, params_in_repr, ..*providers };
11-
}
12-
13-
macro_rules! rtry {
14-
($e:expr) => {
15-
match $e {
16-
e @ Representability::Infinite(_) => return e,
17-
Representability::Representable => {}
18-
}
9+
*providers = Providers {
10+
check_representability,
11+
check_representability_adt_ty,
12+
params_in_repr,
13+
..*providers
1914
};
2015
}
2116

22-
fn representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability {
17+
fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability {
2318
match tcx.def_kind(def_id) {
2419
DefKind::Struct | DefKind::Union | DefKind::Enum => {
2520
for variant in tcx.adt_def(def_id).variants() {
2621
for field in variant.fields.iter() {
27-
rtry!(tcx.representability(field.did.expect_local()));
22+
let _ = tcx.check_representability(field.did.expect_local());
2823
}
2924
}
30-
Representability::Representable
3125
}
32-
DefKind::Field => representability_ty(tcx, tcx.type_of(def_id).instantiate_identity()),
26+
DefKind::Field => {
27+
check_representability_ty(tcx, tcx.type_of(def_id).instantiate_identity());
28+
}
3329
def_kind => bug!("unexpected {def_kind:?}"),
3430
}
31+
Representability
3532
}
3633

37-
fn representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability {
34+
fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) {
3835
match *ty.kind() {
39-
ty::Adt(..) => tcx.representability_adt_ty(ty),
36+
// This one must be a query rather than a vanilla `check_representability_adt_ty` call. See
37+
// the comment on `check_representability_adt_ty` below for why.
38+
ty::Adt(..) => {
39+
let _ = tcx.check_representability_adt_ty(ty);
40+
}
4041
// FIXME(#11924) allow zero-length arrays?
41-
ty::Array(ty, _) => representability_ty(tcx, ty),
42+
ty::Array(ty, _) => {
43+
check_representability_ty(tcx, ty);
44+
}
4245
ty::Tuple(tys) => {
4346
for ty in tys {
44-
rtry!(representability_ty(tcx, ty));
47+
check_representability_ty(tcx, ty);
4548
}
46-
Representability::Representable
4749
}
48-
_ => Representability::Representable,
50+
_ => {}
4951
}
5052
}
5153

52-
/*
53-
The reason for this being a separate query is very subtle:
54-
Consider this infinitely sized struct: `struct Foo(Box<Foo>, Bar<Foo>)`:
55-
When calling representability(Foo), a query cycle will occur:
56-
representability(Foo)
57-
-> representability_adt_ty(Bar<Foo>)
58-
-> representability(Foo)
59-
For the diagnostic output (in `Value::from_cycle_error`), we want to detect that
60-
the `Foo` in the *second* field of the struct is culpable. This requires
61-
traversing the HIR of the struct and calling `params_in_repr(Bar)`. But we can't
62-
call params_in_repr for a given type unless it is known to be representable.
63-
params_in_repr will cycle/panic on infinitely sized types. Looking at the query
64-
cycle above, we know that `Bar` is representable because
65-
representability_adt_ty(Bar<..>) is in the cycle and representability(Bar) is
66-
*not* in the cycle.
67-
*/
68-
fn representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability {
54+
// The reason for this being a separate query is very subtle. Consider this
55+
// infinitely sized struct: `struct Foo(Box<Foo>, Bar<Foo>)`. When calling
56+
// check_representability(Foo), a query cycle will occur:
57+
//
58+
// check_representability(Foo)
59+
// -> check_representability_adt_ty(Bar<Foo>)
60+
// -> check_representability(Foo)
61+
//
62+
// For the diagnostic output (in `Value::from_cycle_error`), we want to detect
63+
// that the `Foo` in the *second* field of the struct is culpable. This
64+
// requires traversing the HIR of the struct and calling `params_in_repr(Bar)`.
65+
// But we can't call params_in_repr for a given type unless it is known to be
66+
// representable. params_in_repr will cycle/panic on infinitely sized types.
67+
// Looking at the query cycle above, we know that `Bar` is representable
68+
// because `check_representability_adt_ty(Bar<..>)` is in the cycle and
69+
// `check_representability(Bar)` is *not* in the cycle.
70+
fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability {
6971
let ty::Adt(adt, args) = ty.kind() else { bug!("expected adt") };
7072
if let Some(def_id) = adt.did().as_local() {
71-
rtry!(tcx.representability(def_id));
73+
let _ = tcx.check_representability(def_id);
7274
}
7375
// At this point, we know that the item of the ADT type is representable;
7476
// but the type parameters may cause a cycle with an upstream type
7577
let params_in_repr = tcx.params_in_repr(adt.did());
7678
for (i, arg) in args.iter().enumerate() {
7779
if let ty::GenericArgKind::Type(ty) = arg.kind() {
7880
if params_in_repr.contains(i as u32) {
79-
rtry!(representability_ty(tcx, ty));
81+
check_representability_ty(tcx, ty);
8082
}
8183
}
8284
}
83-
Representability::Representable
85+
Representability
8486
}
8587

8688
fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DenseBitSet<u32> {

compiler/rustc_ty_utils/src/ty.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,10 @@ fn adt_sizedness_constraint<'tcx>(
116116
tcx: TyCtxt<'tcx>,
117117
(def_id, sizedness): (DefId, SizedTraitKind),
118118
) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
119-
if let Some(def_id) = def_id.as_local()
120-
&& let ty::Representability::Infinite(_) = tcx.representability(def_id)
121-
{
122-
return None;
119+
if let Some(def_id) = def_id.as_local() {
120+
let _ = tcx.check_representability(def_id);
123121
}
122+
124123
let def = tcx.adt_def(def_id);
125124

126125
if !def.is_struct() {

tests/ui/enum-discriminant/issue-72554.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::collections::BTreeSet;
33
#[derive(Hash)]
44
pub enum ElemDerived {
55
//~^ ERROR recursive type `ElemDerived` has infinite size
6-
//~| ERROR cycle detected
76
A(ElemDerived)
87
}
98

tests/ui/enum-discriminant/issue-72554.stderr

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ error[E0072]: recursive type `ElemDerived` has infinite size
33
|
44
LL | pub enum ElemDerived {
55
| ^^^^^^^^^^^^^^^^^^^^
6-
...
6+
LL |
77
LL | A(ElemDerived)
88
| ----------- recursive without indirection
99
|
@@ -12,21 +12,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
1212
LL | A(Box<ElemDerived>)
1313
| ++++ +
1414

15-
error[E0391]: cycle detected when computing drop-check constraints for `ElemDerived`
16-
--> $DIR/issue-72554.rs:4:1
17-
|
18-
LL | pub enum ElemDerived {
19-
| ^^^^^^^^^^^^^^^^^^^^
20-
|
21-
= note: ...which immediately requires computing drop-check constraints for `ElemDerived` again
22-
note: cycle used when computing drop-check constraints for `Elem`
23-
--> $DIR/issue-72554.rs:11:1
24-
|
25-
LL | pub enum Elem {
26-
| ^^^^^^^^^^^^^
27-
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
28-
29-
error: aborting due to 2 previous errors
15+
error: aborting due to 1 previous error
3016

31-
Some errors have detailed explanations: E0072, E0391.
32-
For more information about an error, try `rustc --explain E0072`.
17+
For more information about this error, try `rustc --explain E0072`.

tests/ui/infinite/infinite-struct.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
struct Take(Take);
22
//~^ ERROR has infinite size
3-
//~| ERROR cycle
4-
//~| ERROR reached the recursion limit finding the struct tail for `Take`
53

64
// check that we don't hang trying to find the tail of a recursive struct (#79437)
75
fn foo() -> Take {

0 commit comments

Comments
 (0)