-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
Code
use core::cell::UnsafeCell;
use core::mem::ManuallyDrop;
union U {
foo: u32,
bar: ManuallyDrop<UnsafeCell<U>>,
}
Current output
error[E0072]: recursive type `U` has infinite size
--> src/lib.rs:4:1
|
4 | union U {
| ^^^^^^^
5 | foo: u32,
6 | bar: ManuallyDrop<UnsafeCell<U>>,
| - recursive without indirection
|
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
|
6 | bar: ManuallyDrop<UnsafeCell<Box<U>>>,
| ++++ +
For more information about this error, try `rustc --explain E0072`.
error: could not compile `playground` (lib) due to 1 previous error
Desired output
error[E0072]: type `U` contains another value of the same type without indirection
--> src/lib.rs:4:1
|
4 | union U {
| ^^^^^^^
5 | foo: u32,
6 | bar: ManuallyDrop<UnsafeCell<U>>,
| - recursive without indirection
|
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
|
6 | bar: ManuallyDrop<UnsafeCell<Box<U>>>,
| ++++ +
For more information about this error, try `rustc --explain E0072`.
error: could not compile `playground` (lib) due to 1 previous error
Rationale and extra context
Currently, error E0072 assumes that the reason why a recursive type is rejected is that it would be infinitely large (i.e. it contains a copy of itself plus additional data, meaning that it is larger than itself).
However, there are cases in which the error triggers simply due to the type being recursive without indirection, even though it could be implemented with finite size. In the example above, I used a union
, which is capable of containing a copy of itself because it is the same size as itself. (UnsafeCell<T>
can be larger than T
in some cases, but this is not one of them.)
Other examples include a struct struct T(T)
, and the second example in #68065.
#56674 is highly related, but a bit different (the code in #56674 could reasonably be accepted rather than rejected). #11924 is also related – again, it shows code that could reasonably be accepted rather than rejected, but the error message with which it is currently rejected is wrong.
Unlike the code in those two bugs, I think the example in this report (a union
or otherwise empty struct
that contains itself) should probably be rejected rather than accepted: I can't think of any valid use for it and it would be very difficult to implement correctly (both in terms of trying to prove that the indirection through UnsafeCell
doesn't make it larger, and because it makes it possible to have infinitely many different provenances to the same address – the "inside" and "outside" of an UnsafeCell
are treated differently and this would create an infinitely nested set of UnsafeCell
s if it were allowed). The code is rejected at present, and I think it should continue to be rejected – but the message is misleading because the type does not in fact have an infinite size.
The current message is likely fine in cases where the size of the type actually is infinite (like struct T(T, u32)
), even though it fails to describe the actual rule that causes the program to be rejected ("the type would have infinite size" is the primary reason behind the rule, but the rule itself is "a type cannot contain itself without indirection" and this rejects some types with finite size too).
Other cases
Rust Version
Tested on the Rust playground, which reports its version as `1.90.0-nightly (2025-07-27 f8e355c230c6eb7b78ff)`, because my local rustc is too old.
Anything else?
No response