Change:
Lifted from I believe this future compatibility warning was badly affected by . Minimal repro: As far as I understand what this issue is about, this code should be 100% fine. This issue is about preventing a previously-accepted struct from ending up with contents which a transparent struct is not allowed to have, after a permissible size increase of a non-exhaustive formerly-ZST from a different crate. That's not possible in the code above. continues to be a valid struct if increases in size. Regression in nightly-2023-09-18: Originally posted by in
Ah... good catch. When a type is not "stably 1-ZST" we should just consider it the one non-1-ZST field. Though given that this is a lint right now the logic is a bit trickier... we need to remember if there was a non-1-ZST field, and then if there was, all 1-ZST fields need to be stably 1-ZST if there wasn't, all but one 1-ZST field need to be stably 1-ZST. Having a nice diagnostic for this could be tricky because ideally we'd point it at two 1-ZST fields that are not stable, and explain "both of these might become non-1-ZST in the future and then this wouldn't build any more". Sadly I won't have the time to work on fixing this quickly. Let me know if you want me to revert the PR.
WG-prioritization assigning priority (). label -I-prioritize +P-high

Discussion:
); return; } <del> for (span, _trivial, non_exhaustive) in field_infos { if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive { tcx.struct_span_lint_hir( REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()), span, "zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types", |lint| { let note = if non_exhaustive { "is marked with `#[non_exhaustive]`" } else { "contains private fields" }; let field_ty = tcx.def_path_str_with_args(def_id, args); lint .note(format!("this {descr} contains `{field_ty}`, which {note},  and makes it not a breaking change to become non-zero-sized in the future.")) }, ) </del> <ins> let mut prev_non_exhaustive_1zst = false; for (span, _trivial, non_exhaustive_1zst) in field_infos { if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive_1zst { // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts. // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst. if non_trivial_count > 0 || prev_non_exhaustive_1zst { tcx.struct_span_lint_hir( REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()), span, "zero-sized fields in `repr(transparent)` cannot  contain external non-exhaustive types", |lint| { let note = if non_exhaustive { "is marked with `#[non_exhaustive]`" } else { "contains private fields" }; let field_ty = tcx.def_path_str_with_args(def_id, args); lint.note(format!( "this {descr} contains `{field_ty}`, which {note},  and makes it not a breaking change to become  non-zero-sized in the future." )) }, ) } else { prev_non_exhaustive_1zst = true; } </ins> } } }

//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types //~| WARN this was previously accepted by the compiler <ins> #[repr(transparent)] pub struct T17(NonExhaustive, Sized); //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types //~| WARN this was previously accepted by the compiler #[repr(transparent)] pub struct T18(NonExhaustive, NonExhaustive); //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types //~| WARN this was previously accepted by the compiler #[repr(transparent)] pub struct T19(NonExhaustive, Private); //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types //~| WARN this was previously accepted by the compiler #[repr(transparent)] pub struct T19Flipped(Private, NonExhaustive); //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types //~| WARN this was previously accepted by the compiler #[repr(transparent)] pub struct T20(NonExhaustive); // Okay, since it's the only field. #[repr(transparent)] pub struct T21(NonExhaustive, InternalNonExhaustive); // Okay, since there's only 1 foreign non-exhaustive type. #[repr(transparent)] pub struct T21Flipped(InternalNonExhaustive, NonExhaustive); // Okay, since there's only 1 foreign non-exhaustive type. #[repr(transparent)] pub struct T22(NonExhaustive, InternalPrivate); // Okay, since there's only 1 foreign non-exhaustive type. #[repr(transparent)] pub struct T22Flipped(InternalPrivate, NonExhaustive); // Okay, since there's only 1 foreign non-exhaustive type. </ins> fn main() {}

= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586> = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. <del> error: aborting due to 12 previous errors </del> <ins> error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:97:16 | LL | pub struct T17(NonExhaustive, Sized); |                ^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586> = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:102:31 | LL | pub struct T18(NonExhaustive, NonExhaustive); |                               ^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586> = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:107:31 | LL | pub struct T19(NonExhaustive, Private); |                               ^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586> = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:112:32 | LL | pub struct T19Flipped(Private, NonExhaustive); |                                ^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586> = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. error: aborting due to 16 previous errors </ins>