Change:
But The reference says The transparent representation can only be used on a struct or an enum with a single variant that has: a single field with non-zero size, and any number of fields with size 0 and alignment 1 (e.g. PhantomData<T). I do not see a reason for this restriction. Maybe simply replace "a single" by "at most one" and remove the check.
Marked as bug because people might assume values have size 0 in unsafe code. Unlikely to be the case in real code.
Agreed. Cc
Discussed in the meeting. We agree with the sense of this but would like a specific semantics -- i.e., if all fields are ZSTs, then we need to define what the ABI behavior of the type is (if there is a single non-ZST field , then it behaves like , the "real value" -- but what does it act like if there are no real values?). Perhaps the answer is just "whatever happens if you try to pass ". If someone can supply a concrete rule that makes sense, we would like to approve it =)
To clarify: This removes a restriction, but it now allows a new possibility that didn't exist before, so what are the rules meant to be for that case? (Well, it did exist because of generics, but presumably the rules didn't expect it to exist.)
A possible rule is to use the ABI of the first 1-ZST Field if there are no non 1-ZST fields. One question I have, what about types with size 0 and alignment 1 (0 sized arrays like [u16;0] are examples). Under this rule, these types would still seem to be excluded. That could probably be to the first part at the same time as it's lifted to allow zero of such types. If a ZST that has an alignment 1 is in a repr(transparent) struct, the ABI follows that ZST specifically.
AFAIK "zero-sized-field" for means 1-ZST. Everything else would be buggy I think... So a ZST with alignment greater 1 behaves like a non-ZST. I don't think that should change.
If it's already permitted, that's brillaint. In which case my point can be reduced to a point of clarification, it would be good if the documentation noted that ("At most one non-zero-sized field or zero-sized field with an alignment greater than 1", wording could probably be changed, I'm much better writing normative text than text for users). If its not already permitted, adding it seems to be an extension of what is being discussed here.
It's not a permission though, it's a restriction? For example, the following type is rejected, as it should:
On the other hand, this is also rejected: Even though it has exactly one non-1-ZST field. So looks like the actual check is more something like All fields except for 1 must be zero-sized And additionally all zero-sized fields must have alignment 1
This is what my point is. If we permit transparent types with only ZST's, it seems like it would be equally inconsistent if we don't allow a transparent type with exactly one ZST that isn't a 1-ZST (and any number of 1-ZSTs). The consistency issue raised above would apply here, if it's possible to have a generic transparent type which is monomorphized with , but its not permitted to include directly, even if all other fields are 1-ZSTs.
Fully agreed.
Point of clarification: What is a 1-ZST? A ZST with alignment 1?
Yes. (This is terminology the as it was very helpful for layout discussions.)
OK, so, to restate the above point: We should check whether you can use generics to "sneak" in a ZST with alignment other than 1 (e.g., ). However, I don't think that you can. Based on some quick play experiments I believe we treat generic types "as if" they were a non-zero-sized field. Ah, ok, I think I understand now. The point is that we enforce (currently) a rule that says "any ZST must have alignment 1 in a repr(transparent) type" but that rule can be circumvented via generics. So, the proposed rule is something like. A type T must meet the following rules: It may have any number of 1-ZST fields In addition, it may have at most one other field of type U If that other field is present, then T behaves like type U for the purposes of ABIs. Otherwise it behaves like...? Is there a reason we can't just say ?
As I mentioned, it may be reasonable to say the first member if all members are 1-ZSTs. I can't see a reason that it wouldn't work just saying , though.
Ah, sorry, I missed that comment. I agree that "first member" is an option, though I have a mild preference for avoiding a reliance on the ordering that things were written. That said, I also have a mild preference for avoiding introducing an arbitrary type like . I doubt it matters much in practice since I imagine that all 1-ZSTs basically behave the same with respect to the ABI, though.
I wouldn't call "arbitrary". It is basically the canonical "informationless type" in Rust, or "final element" in category theory speak -- similar to how is the canonical "uninhabited type" or "initial element". So, specifying as a canonical representative of 1-ZST makes a lot of sense IMO.
That's a good point. So here is a proposed set of rules: A type must meet the following rules: It may have any number of 1-ZST fields In addition, it may have at most one other field of type U If that other field is present, then T behaves like type U for ABI purposes. Otherwise, T consists only of 1-ZST types, and it behaves like , which is the "canonical" 1-ZST. I'm going to go ahead and kick off an FCP request... fcp merge
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [ ] [x] [x] [x] [x] [x] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
+1 to not having anything order-dependent in a braced-struct. Hmm, the lint considers an . But then an empty struct is UB(!?) in C, so I guess there isn't a canonical ABI for this. reviewed
:bell: This is now entering its final comment period, as per the . :bell:
GCC-compatible compilers do , treating as zero-sized types. As a result, ABIs often do document the behavior when C empty structs are passed or returned by value. Unfortunately, C++ has an entirely different interpretation of empty structs, where they get a padding byte to make them not zero-sized. This definitely changes their representation in memory, and sometimes also changes their representation when passing or returning them by value… depending on the ABI. As such, empty structs are best avoided entirely.
Indeed, ZSTs exist though only in "non-standardized" extensions. The real question here is whether there exists some "special" 1-ZST that would be treated differently by the ABI than any other 1-ZST. I do not believe that is the case, which is why is kind of "as good as any other". (There are some C structs, as I understand it, that are treated somewhat specially by ABIs, but very few -- I think maybe having to do with complex types? Can't remember.)
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. The RFC will be merged soon.
are you interested in preparing a fix for this?
Removing nomination as we have resolved this fr the lang-team's perspective, though we still need to do implementation work here.

Discussion:
<del> A struct with the representation hint `repr(transparent)` had zero or more than one fields that were not guaranteed to be zero-sized. </del> <ins> A struct with the representation hint `repr(transparent)` had two or more fields that were not guaranteed to be zero-sized. </ins> Erroneous code example: ```compile_fail,E0690 #[repr(transparent)] <del> struct LengthWithUnit<U> { // error: transparent struct needs exactly one </del> <ins> struct LengthWithUnit<U> { // error: transparent struct needs at most one </ins> value: f32,            //        non-zero-sized field, but has 2 unit: U, } ``` Because transparent structs are represented exactly like one of their fields at <del> run time, said field must be uniquely determined. If there is no field, or if there are multiple fields, it is not clear how the struct should be represented. </del> <ins> run time, said field must be uniquely determined. If there are multiple fields, it is not clear how the struct should be represented. </ins> Note that fields of zero-sized types (e.g., `PhantomData`) can also exist alongside the field that contains the actual data, they do not count for this error. When generic types are involved (as in the above example), an error is

let non_zst_fields = field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None }); let non_zst_count = non_zst_fields.clone().count(); <del> if non_zst_count != 1 { </del> <ins> if non_zst_count >= 2 { </ins> bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp); } for (span, zst, align1) in field_infos {

} } <del> /// Emit an error when encountering more or less than one variant in a transparent enum. </del> <ins> /// Emit an error when encountering two or more variants in a transparent enum. </ins> fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: &'tcx ty::AdtDef, sp: Span, did: DefId) { let variant_spans: Vec<_> = adt .variants

err.emit(); } <del> /// Emit an error when encountering more or less than one non-zero-sized field in a transparent </del> <ins> /// Emit an error when encountering two or more non-zero-sized fields in a transparent </ins> /// enum. fn bad_non_zero_sized_fields<'tcx>( tcx: TyCtxt<'tcx>,

field_spans: impl Iterator<Item = Span>, sp: Span, ) { <del> let msg = format!("needs exactly one non-zero-sized field, but has {}", field_count); </del> <ins> let msg = format!("needs at most one non-zero-sized field, but has {}", field_count); </ins> let mut err = struct_span_err!( tcx.sess, sp,

use std::marker::PhantomData; #[repr(transparent)] <del> struct NoFields; //~ ERROR needs exactly one non-zero-sized field </del> <ins> struct NoFields; </ins> #[repr(transparent)] <del> struct ContainsOnlyZst(()); //~ ERROR needs exactly one non-zero-sized field </del> <ins> struct ContainsOnlyZst(()); </ins> #[repr(transparent)] <del> struct ContainsOnlyZstArray([bool; 0]); //~ ERROR needs exactly one non-zero-sized field </del> <ins> struct ContainsOnlyZstArray([bool; 0]); </ins> #[repr(transparent)] struct ContainsMultipleZst(PhantomData<*const i32>, NoFields); <del> //~^ ERROR needs exactly one non-zero-sized field </del> #[repr(transparent)] <del> struct MultipleNonZst(u8, u8); //~ ERROR needs exactly one non-zero-sized field </del> <ins> struct ContainsZstAndNonZst((), [i32; 2]); #[repr(transparent)] struct MultipleNonZst(u8, u8); //~ ERROR needs at most one non-zero-sized field </ins> trait Mirror { type It: ?Sized; } impl<T: ?Sized> Mirror for T { type It = Self; } #[repr(transparent)] pub struct StructWithProjection(f32, <f32 as Mirror>::It); <del> //~^ ERROR needs exactly one non-zero-sized field </del> <ins> //~^ ERROR needs at most one non-zero-sized field </ins> #[repr(transparent)] struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR alignment larger than 1

struct GenericAlign<T>(ZstAlign32<T>, u32); //~ ERROR alignment larger than 1 #[repr(transparent)] //~ ERROR unsupported representation for zero-variant enum <del> enum Void {} //~^ ERROR transparent enum needs exactly one variant, but has 0 </del> <ins> enum Void {} //~ ERROR transparent enum needs exactly one variant, but has 0 </ins> #[repr(transparent)] <del> enum FieldlessEnum { //~ ERROR transparent enum needs exactly one non-zero-sized field, but has 0 </del> <ins> enum FieldlessEnum { </ins> Foo, } #[repr(transparent)] <ins> enum UnitFieldEnum { Foo(()), } #[repr(transparent)] </ins> enum TooManyFieldsEnum { Foo(u32, String), } <del> //~^^^ ERROR transparent enum needs exactly one non-zero-sized field, but has 2 </del> <ins> //~^^^ ERROR transparent enum needs at most one non-zero-sized field, but has 2 </ins> #[repr(transparent)] <del> enum TooManyVariants { //~ ERROR transparent enum needs exactly one variant, but has 2 </del> <ins> enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, but has 2 </ins> Foo(String), Bar, }

} #[repr(transparent)] <del> union UnitUnion { //~ ERROR transparent union needs exactly one non-zero-sized field, but has 0 </del> <ins> union UnitUnion { </ins> u: (), } #[repr(transparent)] <del> union TooManyFields { //~ ERROR transparent union needs exactly one non-zero-sized field, but has 2 </del> <ins> union TooManyFields { //~ ERROR transparent union needs at most one non-zero-sized field, but has 2 </ins> u: u32, s: i32 }

<del> error[E0690]: transparent struct needs exactly one non-zero-sized field, but has 0 --> $DIR/repr-transparent.rs:11:1 | LL | struct NoFields; | ^^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 0 error[E0690]: transparent struct needs exactly one non-zero-sized field, but has 0 --> $DIR/repr-transparent.rs:14:1 | LL | struct ContainsOnlyZst(()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 0 error[E0690]: transparent struct needs exactly one non-zero-sized field, but has 0 --> $DIR/repr-transparent.rs:17:1 | LL | struct ContainsOnlyZstArray([bool; 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 0 error[E0690]: transparent struct needs exactly one non-zero-sized field, but has 0 --> $DIR/repr-transparent.rs:20:1 | LL | struct ContainsMultipleZst(PhantomData<*const i32>, NoFields); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 0 error[E0690]: transparent struct needs exactly one non-zero-sized field, but has 2 --> $DIR/repr-transparent.rs:24:1 </del> <ins> error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2 --> $DIR/repr-transparent.rs:26:1 </ins> | LL | struct MultipleNonZst(u8, u8); | ^^^^^^^^^^^^^^^^^^^^^^--^^--^^ | |                     |   | | |                     |   this field is non-zero-sized | |                     this field is non-zero-sized <del> | needs exactly one non-zero-sized field, but has 2 </del> <ins> | needs at most one non-zero-sized field, but has 2 </ins> <del> error[E0690]: transparent struct needs exactly one non-zero-sized field, but has 2 --> $DIR/repr-transparent.rs:30:1 </del> <ins> error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2 --> $DIR/repr-transparent.rs:32:1 </ins> | LL | pub struct StructWithProjection(f32, <f32 as Mirror>::It); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^-------------------^^ | |                               |    | | |                               |    this field is non-zero-sized | |                               this field is non-zero-sized <del> | needs exactly one non-zero-sized field, but has 2 </del> <ins> | needs at most one non-zero-sized field, but has 2 </ins> error[E0691]: zero-sized field in transparent struct has alignment larger than 1 <del> --> $DIR/repr-transparent.rs:34:32 </del> <ins> --> $DIR/repr-transparent.rs:36:32 </ins> | LL | struct NontrivialAlignZst(u32, [u16; 0]); |                                ^^^^^^^^ has alignment larger than 1 error[E0691]: zero-sized field in transparent struct has alignment larger than 1 <del> --> $DIR/repr-transparent.rs:40:24 </del> <ins> --> $DIR/repr-transparent.rs:42:24 </ins> | LL | struct GenericAlign<T>(ZstAlign32<T>, u32); |                        ^^^^^^^^^^^^^ has alignment larger than 1 error[E0084]: unsupported representation for zero-variant enum <del> --> $DIR/repr-transparent.rs:42:1 </del> <ins> --> $DIR/repr-transparent.rs:44:1 </ins> | LL | #[repr(transparent)] | ^^^^^^^^^^^^^^^^^^^^

| ------------ zero-variant enum error[E0731]: transparent enum needs exactly one variant, but has 0 <del> --> $DIR/repr-transparent.rs:43:1 </del> <ins> --> $DIR/repr-transparent.rs:45:1 </ins> | LL | enum Void {} | ^^^^^^^^^ needs exactly one variant, but has 0 <del> error[E0690]: the variant of a transparent enum needs exactly one non-zero-sized field, but has 0 --> $DIR/repr-transparent.rs:47:1 | LL | enum FieldlessEnum { | ^^^^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 0 error[E0690]: the variant of a transparent enum needs exactly one non-zero-sized field, but has 2 --> $DIR/repr-transparent.rs:52:1 </del> <ins> error[E0690]: the variant of a transparent enum needs at most one non-zero-sized field, but has 2 --> $DIR/repr-transparent.rs:58:1 </ins> | LL | enum TooManyFieldsEnum { <del> | ^^^^^^^^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 2 </del> <ins> | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2 </ins> LL |     Foo(u32, String), |         ---  ------ this field is non-zero-sized |         | |         this field is non-zero-sized error[E0731]: transparent enum needs exactly one variant, but has 2 <del> --> $DIR/repr-transparent.rs:58:1 </del> <ins> --> $DIR/repr-transparent.rs:64:1 </ins> | <del> LL | enum TooManyVariants { | ^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2 </del> <ins> LL | enum MultipleVariants { | ^^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2 </ins> LL |     Foo(String), |     ----------- LL |     Bar, <del> |     --- too many variants in `TooManyVariants` </del> <ins> |     --- too many variants in `MultipleVariants` </ins> error[E0691]: zero-sized field in transparent enum has alignment larger than 1 <del> --> $DIR/repr-transparent.rs:65:14 </del> <ins> --> $DIR/repr-transparent.rs:71:14 </ins> | LL |     Foo(u32, [u16; 0]), |              ^^^^^^^^ has alignment larger than 1 error[E0691]: zero-sized field in transparent enum has alignment larger than 1 <del> --> $DIR/repr-transparent.rs:70:11 </del> <ins> --> $DIR/repr-transparent.rs:76:11 </ins> | LL |     Foo { bar: ZstAlign32<T>, baz: u32 } |           ^^^^^^^^^^^^^^^^^^ has alignment larger than 1 <del> error[E0690]: transparent union needs exactly one non-zero-sized field, but has 0 --> $DIR/repr-transparent.rs:74:1 | LL | union UnitUnion { | ^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 0 error[E0690]: transparent union needs exactly one non-zero-sized field, but has 2 --> $DIR/repr-transparent.rs:79:1 </del> <ins> error[E0690]: transparent union needs at most one non-zero-sized field, but has 2 --> $DIR/repr-transparent.rs:85:1 </ins> | LL | union TooManyFields { <del> | ^^^^^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 2 </del> <ins> | ^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2 </ins> LL |     u: u32, |     ------ this field is non-zero-sized LL |     s: i32 |     ------ this field is non-zero-sized <del> error: aborting due to 17 previous errors </del> <ins> error: aborting due to 11 previous errors </ins> Some errors have detailed explanations: E0084, E0690, E0691, E0731. For more information about an error, try `rustc --explain E0084`.