From 345241536fbd2302c032851c0422f18ed847a0df Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 Oct 2025 11:15:31 +0200 Subject: [PATCH] repr_transparent_external_private_fields: normalize types during traversal --- .../rustc_hir_analysis/src/check/check.rs | 15 ++++--- .../repr/repr-transparent-non-exhaustive.rs | 12 +++++ .../repr-transparent-non-exhaustive.stderr | 44 ++++++++++++------- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 9c2d52c3e8030..4c910d25c30a8 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1510,11 +1510,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) return; } + let typing_env = ty::TypingEnv::non_body_analysis(tcx, adt.did()); // For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with // "known" respecting #[non_exhaustive] attributes. let field_infos = adt.all_fields().map(|field| { let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did)); - let typing_env = ty::TypingEnv::non_body_analysis(tcx, field.did); let layout = tcx.layout_of(typing_env.as_query_input(ty)); // We are currently checking the type this field came from, so it must be local let span = tcx.hir_span_if_local(field.did).unwrap(); @@ -1526,11 +1526,16 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) fn check_non_exhaustive<'tcx>( tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, t: Ty<'tcx>, ) -> ControlFlow<(&'static str, DefId, GenericArgsRef<'tcx>, bool)> { + // We can encounter projections during traversal, so ensure the type is normalized. + let t = tcx.try_normalize_erasing_regions(typing_env, t).unwrap_or(t); match t.kind() { - ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)), - ty::Array(ty, _) => check_non_exhaustive(tcx, *ty), + ty::Tuple(list) => { + list.iter().try_for_each(|t| check_non_exhaustive(tcx, typing_env, t)) + } + ty::Array(ty, _) => check_non_exhaustive(tcx, typing_env, *ty), ty::Adt(def, args) => { if !def.did().is_local() && !find_attr!( @@ -1555,13 +1560,13 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } def.all_fields() .map(|field| field.ty(tcx, args)) - .try_for_each(|t| check_non_exhaustive(tcx, t)) + .try_for_each(|t| check_non_exhaustive(tcx, typing_env, t)) } _ => ControlFlow::Continue(()), } } - (span, trivial, check_non_exhaustive(tcx, ty).break_value()) + (span, trivial, check_non_exhaustive(tcx, typing_env, ty).break_value()) }); let non_trivial_fields = field_infos diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.rs b/tests/ui/repr/repr-transparent-non-exhaustive.rs index 9894b89e8e418..38bab0163b4c5 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.rs +++ b/tests/ui/repr/repr-transparent-non-exhaustive.rs @@ -24,6 +24,13 @@ pub struct InternalIndirection { pub type Sized = i32; +pub trait Trait { + type Assoc; +} +impl Trait for i32 { + type Assoc = NonExhaustive; +} + #[repr(transparent)] pub struct T1(Sized, InternalPrivate); #[repr(transparent)] @@ -43,6 +50,11 @@ pub struct T6(Sized, 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 T6a(Sized, ::Assoc); // normalizes to `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 T7(Sized, NonExhaustiveEnum); //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.stderr b/tests/ui/repr/repr-transparent-non-exhaustive.stderr index 808b9bc986d91..27ba6e82a539a 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.stderr +++ b/tests/ui/repr/repr-transparent-non-exhaustive.stderr @@ -1,5 +1,5 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:37:22 + --> $DIR/repr-transparent-non-exhaustive.rs:44:22 | LL | pub struct T5(Sized, Private); | ^^^^^^^ @@ -14,7 +14,7 @@ LL | #![deny(repr_transparent_external_private_fields)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:42:22 + --> $DIR/repr-transparent-non-exhaustive.rs:49:22 | LL | pub struct T6(Sized, NonExhaustive); | ^^^^^^^^^^^^^ @@ -24,7 +24,17 @@ LL | pub struct T6(Sized, NonExhaustive); = 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:47:22 + --> $DIR/repr-transparent-non-exhaustive.rs:54:23 + | +LL | pub struct T6a(Sized, ::Assoc); // normalizes to `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 + = 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:59:22 | LL | pub struct T7(Sized, NonExhaustiveEnum); | ^^^^^^^^^^^^^^^^^ @@ -34,7 +44,7 @@ LL | pub struct T7(Sized, NonExhaustiveEnum); = note: this enum contains `NonExhaustiveEnum`, 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:52:22 + --> $DIR/repr-transparent-non-exhaustive.rs:64:22 | LL | pub struct T8(Sized, NonExhaustiveVariant); | ^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +54,7 @@ LL | pub struct T8(Sized, NonExhaustiveVariant); = 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. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:57:22 + --> $DIR/repr-transparent-non-exhaustive.rs:69:22 | LL | pub struct T9(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,7 +64,7 @@ LL | pub struct T9(Sized, InternalIndirection); = 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:62:23 + --> $DIR/repr-transparent-non-exhaustive.rs:74:23 | LL | pub struct T10(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +74,7 @@ LL | pub struct T10(Sized, InternalIndirection); = 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:67:23 + --> $DIR/repr-transparent-non-exhaustive.rs:79:23 | LL | pub struct T11(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,7 +84,7 @@ LL | pub struct T11(Sized, InternalIndirection); = note: this enum contains `NonExhaustiveEnum`, 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:72:23 + --> $DIR/repr-transparent-non-exhaustive.rs:84:23 | LL | pub struct T12(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +94,7 @@ LL | pub struct T12(Sized, InternalIndirection); = 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. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:77:23 + --> $DIR/repr-transparent-non-exhaustive.rs:89:23 | LL | pub struct T13(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +104,7 @@ LL | pub struct T13(Sized, ExternalIndirection); = 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:82:23 + --> $DIR/repr-transparent-non-exhaustive.rs:94:23 | LL | pub struct T14(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +114,7 @@ LL | pub struct T14(Sized, ExternalIndirection); = 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:87:23 + --> $DIR/repr-transparent-non-exhaustive.rs:99:23 | LL | pub struct T15(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +124,7 @@ LL | pub struct T15(Sized, ExternalIndirection); = note: this enum contains `NonExhaustiveEnum`, 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:92:23 + --> $DIR/repr-transparent-non-exhaustive.rs:104:23 | LL | pub struct T16(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,7 +134,7 @@ LL | pub struct T16(Sized, ExternalIndirection); = 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. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-non-exhaustive.rs:97:16 + --> $DIR/repr-transparent-non-exhaustive.rs:109:16 | LL | pub struct T17(NonExhaustive, Sized); | ^^^^^^^^^^^^^ @@ -134,7 +144,7 @@ LL | pub struct T17(NonExhaustive, Sized); = 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 + --> $DIR/repr-transparent-non-exhaustive.rs:114:31 | LL | pub struct T18(NonExhaustive, NonExhaustive); | ^^^^^^^^^^^^^ @@ -144,7 +154,7 @@ LL | pub struct T18(NonExhaustive, NonExhaustive); = 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 + --> $DIR/repr-transparent-non-exhaustive.rs:119:31 | LL | pub struct T19(NonExhaustive, Private); | ^^^^^^^ @@ -154,7 +164,7 @@ LL | pub struct T19(NonExhaustive, Private); = 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 + --> $DIR/repr-transparent-non-exhaustive.rs:124:32 | LL | pub struct T19Flipped(Private, NonExhaustive); | ^^^^^^^^^^^^^ @@ -163,5 +173,5 @@ LL | pub struct T19Flipped(Private, NonExhaustive); = note: for more information, see issue #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 +error: aborting due to 17 previous errors