From 5bd164bb5ed3a8d425001509c702c6c4d9698845 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 Sep 2025 12:38:27 +0200 Subject: [PATCH 1/7] repr(transparent): do not consider repr(C) types to be 1-ZST --- .../rustc_hir_analysis/src/check/check.rs | 132 ++++++++++-------- compiler/rustc_lint_defs/src/lib.rs | 2 +- tests/ui/repr/repr-transparent-repr-c.rs | 32 +++++ tests/ui/repr/repr-transparent-repr-c.stderr | 37 +++++ 4 files changed, 146 insertions(+), 57 deletions(-) create mode 100644 tests/ui/repr/repr-transparent-repr-c.rs create mode 100644 tests/ui/repr/repr-transparent-repr-c.stderr diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 886ebddc75c97..cce1744dfc7b0 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1510,8 +1510,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) return; } - // 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. + // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST). 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); @@ -1519,49 +1518,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // 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(); let trivial = layout.is_ok_and(|layout| layout.is_1zst()); - if !trivial { - return (span, trivial, None); - } - // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`. - - fn check_non_exhaustive<'tcx>( - tcx: TyCtxt<'tcx>, - t: Ty<'tcx>, - ) -> ControlFlow<(&'static str, DefId, GenericArgsRef<'tcx>, bool)> { - 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::Adt(def, args) => { - if !def.did().is_local() - && !find_attr!( - tcx.get_all_attrs(def.did()), - AttributeKind::PubTransparent(_) - ) - { - let non_exhaustive = def.is_variant_list_non_exhaustive() - || def - .variants() - .iter() - .any(ty::VariantDef::is_field_list_non_exhaustive); - let has_priv = def.all_fields().any(|f| !f.vis.is_public()); - if non_exhaustive || has_priv { - return ControlFlow::Break(( - def.descr(), - def.did(), - args, - non_exhaustive, - )); - } - } - def.all_fields() - .map(|field| field.ty(tcx, args)) - .try_for_each(|t| check_non_exhaustive(tcx, t)) - } - _ => ControlFlow::Continue(()), - } - } - - (span, trivial, check_non_exhaustive(tcx, ty).break_value()) + (span, trivial, ty) }); let non_trivial_fields = field_infos @@ -1578,12 +1535,74 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) ); return; } - 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 { + + // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private + // fields or `repr(C)`. We call those fields "unsuited". Search for unsuited fields and + // error if the repr(transparent) condition relies on them. + enum UnsuitedReason { + NonExhaustive, + PrivateField, + ReprC, + } + struct UnsuitedInfo<'tcx> { + descr: &'static str, + def_id: DefId, + args: GenericArgsRef<'tcx>, + reason: UnsuitedReason, + } + + fn check_unsuited_1zst<'tcx>( + tcx: TyCtxt<'tcx>, + t: Ty<'tcx>, + ) -> ControlFlow> { + match t.kind() { + ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited_1zst(tcx, t)), + ty::Array(ty, _) => check_unsuited_1zst(tcx, *ty), + ty::Adt(def, args) => { + if !def.did().is_local() + && !find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::PubTransparent(_)) + { + let non_exhaustive = def.is_variant_list_non_exhaustive() + || def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive); + let has_priv = def.all_fields().any(|f| !f.vis.is_public()); + if non_exhaustive || has_priv { + return ControlFlow::Break(UnsuitedInfo { + descr: def.descr(), + def_id: def.did(), + args, + reason: if non_exhaustive { + UnsuitedReason::NonExhaustive + } else { + UnsuitedReason::PrivateField + }, + }); + } + } + if def.repr().c() { + return ControlFlow::Break(UnsuitedInfo { + descr: def.descr(), + def_id: def.did(), + args, + reason: UnsuitedReason::ReprC, + }); + } + def.all_fields() + .map(|field| field.ty(tcx, args)) + .try_for_each(|t| check_unsuited_1zst(tcx, t)) + } + _ => ControlFlow::Continue(()), + } + } + + let mut prev_unsuited_1zst = false; + for (span, trivial, ty) in field_infos { + if !trivial { + continue; + } + if let Some(unsuited) = check_unsuited_1zst(tcx, ty).break_value() { // 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 { + if non_trivial_count > 0 || prev_unsuited_1zst { tcx.node_span_lint( REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, tcx.local_def_id_to_hir_id(adt.did().expect_local()), @@ -1593,21 +1612,22 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) "zero-sized fields in `repr(transparent)` cannot \ contain external non-exhaustive types", ); - let note = if non_exhaustive { - "is marked with `#[non_exhaustive]`" - } else { - "contains private fields" + let note = match unsuited.reason { + UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`", + UnsuitedReason::PrivateField => "contains private fields", + UnsuitedReason::ReprC => "is marked with `#[repr(C)]`", }; - let field_ty = tcx.def_path_str_with_args(def_id, args); + let field_ty = tcx.def_path_str_with_args(unsuited.def_id, unsuited.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." + non-zero-sized in the future.", + descr = unsuited.descr, )); }, ) } else { - prev_non_exhaustive_1zst = true; + prev_unsuited_1zst = true; } } } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 40a818a3c9dc7..ec26c35ffdc9f 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -403,7 +403,7 @@ pub enum FutureIncompatibilityReason { /// /// After a lint has been in this state for a while and you feel like it is ready to graduate /// to warning everyone, consider setting [`FutureIncompatibleInfo::report_in_deps`] to true. - /// (see it's documentation for more guidance) + /// (see its documentation for more guidance) /// /// After some period of time, lints with this variant can be turned into /// hard errors (and the lint removed). Preferably when there is some diff --git a/tests/ui/repr/repr-transparent-repr-c.rs b/tests/ui/repr/repr-transparent-repr-c.rs new file mode 100644 index 0000000000000..17d82870e352b --- /dev/null +++ b/tests/ui/repr/repr-transparent-repr-c.rs @@ -0,0 +1,32 @@ +#![deny(repr_transparent_external_private_fields)] + +#[repr(C)] +pub struct ReprC1Zst { + pub _f: (), +} + +pub type Sized = i32; + +#[repr(transparent)] +pub struct T1(ReprC1Zst); +#[repr(transparent)] +pub struct T2((), ReprC1Zst); +#[repr(transparent)] +pub struct T3(ReprC1Zst, ()); + +#[repr(transparent)] +pub struct T5(Sized, ReprC1Zst); +//~^ 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 T6(ReprC1Zst, 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 T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +//~| WARN this was previously accepted by the compiler + +fn main() {} diff --git a/tests/ui/repr/repr-transparent-repr-c.stderr b/tests/ui/repr/repr-transparent-repr-c.stderr new file mode 100644 index 0000000000000..c350b20248a99 --- /dev/null +++ b/tests/ui/repr/repr-transparent-repr-c.stderr @@ -0,0 +1,37 @@ +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-repr-c.rs:18:22 + | +LL | pub struct T5(Sized, ReprC1Zst); + | ^^^^^^^^^ + | + = 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 `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-repr-c.rs:1:9 + | +LL | #![deny(repr_transparent_external_private_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-repr-c.rs:23:15 + | +LL | pub struct T6(ReprC1Zst, 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 + = note: this struct contains `ReprC1Zst`, which is marked with `#[repr(C)]`, 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-repr-c.rs:28:15 + | +LL | pub struct T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type + | ^^ + | + = 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 `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. + +error: aborting due to 3 previous errors + From 24d6a9ae0db77aa466a69bdf481aee24776d6bb1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 Sep 2025 12:41:13 +0200 Subject: [PATCH 2/7] for crater: make this lint a hard error --- .../rustc_hir_analysis/src/check/check.rs | 37 +++++----- .../repr/repr-transparent-non-exhaustive.rs | 16 ----- .../repr-transparent-non-exhaustive.stderr | 67 +++++-------------- tests/ui/repr/repr-transparent-repr-c.rs | 3 - tests/ui/repr/repr-transparent-repr-c.stderr | 15 +---- 5 files changed, 32 insertions(+), 106 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index cce1744dfc7b0..48f058ccced64 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -12,9 +12,7 @@ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::{LangItem, Node, attrs, find_attr, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode, WellFormedLoc}; -use rustc_lint_defs::builtin::{ - REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS, -}; +use rustc_lint_defs::builtin::UNSUPPORTED_CALLING_CONVENTIONS; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; @@ -1603,29 +1601,24 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // 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_unsuited_1zst { - tcx.node_span_lint( - REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, - tcx.local_def_id_to_hir_id(adt.did().expect_local()), + let mut diag = tcx.dcx().struct_span_err( span, - |lint| { - lint.primary_message( - "zero-sized fields in `repr(transparent)` cannot \ + "zero-sized fields in `repr(transparent)` cannot \ contain external non-exhaustive types", - ); - let note = match unsuited.reason { - UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`", - UnsuitedReason::PrivateField => "contains private fields", - UnsuitedReason::ReprC => "is marked with `#[repr(C)]`", - }; - let field_ty = tcx.def_path_str_with_args(unsuited.def_id, unsuited.args); - lint.note(format!( - "this {descr} contains `{field_ty}`, which {note}, \ + ); + let note = match unsuited.reason { + UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`", + UnsuitedReason::PrivateField => "contains private fields", + UnsuitedReason::ReprC => "is marked with `#[repr(C)]`", + }; + let field_ty = tcx.def_path_str_with_args(unsuited.def_id, unsuited.args); + diag.note(format!( + "this {descr} contains `{field_ty}`, which {note}, \ and makes it not a breaking change to become \ non-zero-sized in the future.", - descr = unsuited.descr, - )); - }, - ) + descr = unsuited.descr, + )); + diag.emit(); } else { prev_unsuited_1zst = true; } diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.rs b/tests/ui/repr/repr-transparent-non-exhaustive.rs index 9894b89e8e418..00236886d0860 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.rs +++ b/tests/ui/repr/repr-transparent-non-exhaustive.rs @@ -36,82 +36,66 @@ pub struct T4(Sized, ExternalIndirection<(InternalPrivate, InternalNonExhaustive #[repr(transparent)] pub struct T5(Sized, 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 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 T7(Sized, NonExhaustiveEnum); //~^ 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 T8(Sized, NonExhaustiveVariant); //~^ 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 T9(Sized, InternalIndirection); //~^ 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 T10(Sized, InternalIndirection); //~^ 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 T11(Sized, InternalIndirection); //~^ 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 T12(Sized, InternalIndirection); //~^ 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 T13(Sized, ExternalIndirection); //~^ 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 T14(Sized, ExternalIndirection); //~^ 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 T15(Sized, ExternalIndirection); //~^ 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 T16(Sized, ExternalIndirection); //~^ 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 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); diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.stderr b/tests/ui/repr/repr-transparent-non-exhaustive.stderr index 808b9bc986d91..0ebbd01c6062e 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.stderr +++ b/tests/ui/repr/repr-transparent-non-exhaustive.stderr @@ -4,163 +4,126 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T5(Sized, 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 = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. -note: the lint level is defined here - --> $DIR/repr-transparent-non-exhaustive.rs:1:9 - | -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:41:22 | LL | pub struct T6(Sized, 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:47:22 + --> $DIR/repr-transparent-non-exhaustive.rs:45:22 | LL | pub struct T7(Sized, NonExhaustiveEnum); | ^^^^^^^^^^^^^^^^^ | - = 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 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:49:22 | LL | pub struct T8(Sized, NonExhaustiveVariant); | ^^^^^^^^^^^^^^^^^^^^ | - = 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 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:53:22 | LL | pub struct T9(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = 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 `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:57:23 | LL | pub struct T10(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = 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:67:23 + --> $DIR/repr-transparent-non-exhaustive.rs:61:23 | LL | pub struct T11(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = 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 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:65:23 | LL | pub struct T12(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = 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 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:69:23 | LL | pub struct T13(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = 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 `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:73:23 | LL | pub struct T14(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = 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:87:23 + --> $DIR/repr-transparent-non-exhaustive.rs:77:23 | LL | pub struct T15(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = 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 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:81:23 | LL | pub struct T16(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = 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 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:85: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 = 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:89: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 = 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:93: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 = 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:97: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 = 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 diff --git a/tests/ui/repr/repr-transparent-repr-c.rs b/tests/ui/repr/repr-transparent-repr-c.rs index 17d82870e352b..d897c44c8b6b9 100644 --- a/tests/ui/repr/repr-transparent-repr-c.rs +++ b/tests/ui/repr/repr-transparent-repr-c.rs @@ -17,16 +17,13 @@ pub struct T3(ReprC1Zst, ()); #[repr(transparent)] pub struct T5(Sized, ReprC1Zst); //~^ 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 T6(ReprC1Zst, 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 T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types -//~| WARN this was previously accepted by the compiler fn main() {} diff --git a/tests/ui/repr/repr-transparent-repr-c.stderr b/tests/ui/repr/repr-transparent-repr-c.stderr index c350b20248a99..562ca3f81ef88 100644 --- a/tests/ui/repr/repr-transparent-repr-c.stderr +++ b/tests/ui/repr/repr-transparent-repr-c.stderr @@ -4,33 +4,22 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T5(Sized, ReprC1Zst); | ^^^^^^^^^ | - = 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 `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. -note: the lint level is defined here - --> $DIR/repr-transparent-repr-c.rs:1:9 - | -LL | #![deny(repr_transparent_external_private_fields)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-repr-c.rs:23:15 + --> $DIR/repr-transparent-repr-c.rs:22:15 | LL | pub struct T6(ReprC1Zst, 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 = note: this struct contains `ReprC1Zst`, which is marked with `#[repr(C)]`, 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-repr-c.rs:28:15 + --> $DIR/repr-transparent-repr-c.rs:26:15 | LL | pub struct T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type | ^^ | - = 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 `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. error: aborting due to 3 previous errors From eee5199009b5df73931979f5efa48aff5d745836 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 8 Oct 2025 22:43:02 +0200 Subject: [PATCH 3/7] also complain about uninhabited types being ignored in repr(transparent) --- .../rustc_hir_analysis/src/check/check.rs | 38 +++++++++---------- .../repr-transparent-non-exhaustive.stderr | 32 ++++++++-------- tests/ui/repr/repr-transparent-repr-c.stderr | 6 +-- tests/ui/repr/repr-transparent-uninhabited.rs | 23 +++++++++++ .../repr/repr-transparent-uninhabited.stderr | 18 +++++++++ 5 files changed, 77 insertions(+), 40 deletions(-) create mode 100644 tests/ui/repr/repr-transparent-uninhabited.rs create mode 100644 tests/ui/repr/repr-transparent-uninhabited.stderr diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 48f058ccced64..9b86ca86a87d5 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1541,21 +1541,24 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) NonExhaustive, PrivateField, ReprC, + Uninhabited, } struct UnsuitedInfo<'tcx> { - descr: &'static str, - def_id: DefId, - args: GenericArgsRef<'tcx>, + ty: Ty<'tcx>, reason: UnsuitedReason, } fn check_unsuited_1zst<'tcx>( tcx: TyCtxt<'tcx>, - t: Ty<'tcx>, + adt: DefId, + ty: Ty<'tcx>, ) -> ControlFlow> { - match t.kind() { - ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited_1zst(tcx, t)), - ty::Array(ty, _) => check_unsuited_1zst(tcx, *ty), + if !ty.is_inhabited_from(tcx, adt, ty::TypingEnv::non_body_analysis(tcx, adt)) { + return ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::Uninhabited }); + } + match ty.kind() { + ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited_1zst(tcx, adt, t)), + ty::Array(ty, _) => check_unsuited_1zst(tcx, adt, *ty), ty::Adt(def, args) => { if !def.did().is_local() && !find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::PubTransparent(_)) @@ -1565,9 +1568,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) let has_priv = def.all_fields().any(|f| !f.vis.is_public()); if non_exhaustive || has_priv { return ControlFlow::Break(UnsuitedInfo { - descr: def.descr(), - def_id: def.did(), - args, + ty, reason: if non_exhaustive { UnsuitedReason::NonExhaustive } else { @@ -1577,16 +1578,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } } if def.repr().c() { - return ControlFlow::Break(UnsuitedInfo { - descr: def.descr(), - def_id: def.did(), - args, - reason: UnsuitedReason::ReprC, - }); + return ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::ReprC }); } def.all_fields() .map(|field| field.ty(tcx, args)) - .try_for_each(|t| check_unsuited_1zst(tcx, t)) + .try_for_each(|t| check_unsuited_1zst(tcx, adt, t)) } _ => ControlFlow::Continue(()), } @@ -1597,7 +1593,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) if !trivial { continue; } - if let Some(unsuited) = check_unsuited_1zst(tcx, ty).break_value() { + if let Some(unsuited) = check_unsuited_1zst(tcx, adt.did(), ty).break_value() { // 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_unsuited_1zst { @@ -1610,13 +1606,13 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`", UnsuitedReason::PrivateField => "contains private fields", UnsuitedReason::ReprC => "is marked with `#[repr(C)]`", + UnsuitedReason::Uninhabited => "is not (publicly) inhabited", }; - let field_ty = tcx.def_path_str_with_args(unsuited.def_id, unsuited.args); diag.note(format!( - "this {descr} contains `{field_ty}`, which {note}, \ + "this field contains `{field_ty}`, which {note}, \ and makes it not a breaking change to become \ non-zero-sized in the future.", - descr = unsuited.descr, + field_ty = unsuited.ty, )); diag.emit(); } else { diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.stderr b/tests/ui/repr/repr-transparent-non-exhaustive.stderr index 0ebbd01c6062e..b22cd9aa9e91a 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.stderr +++ b/tests/ui/repr/repr-transparent-non-exhaustive.stderr @@ -4,7 +4,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T5(Sized, 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. + = note: this field 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:41:22 @@ -12,7 +12,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:45:22 @@ -20,7 +20,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:49:22 @@ -28,7 +28,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:53:22 @@ -36,7 +36,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:57:23 @@ -44,7 +44,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:61:23 @@ -52,7 +52,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:65:23 @@ -60,7 +60,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:69:23 @@ -68,7 +68,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:73:23 @@ -76,7 +76,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:77:23 @@ -84,7 +84,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:81:23 @@ -92,7 +92,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:85:16 @@ -100,7 +100,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:89:31 @@ -108,7 +108,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:93:31 @@ -116,7 +116,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha 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. + = note: this field 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:97:32 @@ -124,7 +124,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T19Flipped(Private, 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. + = note: this field 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 diff --git a/tests/ui/repr/repr-transparent-repr-c.stderr b/tests/ui/repr/repr-transparent-repr-c.stderr index 562ca3f81ef88..11e8de3d9a5df 100644 --- a/tests/ui/repr/repr-transparent-repr-c.stderr +++ b/tests/ui/repr/repr-transparent-repr-c.stderr @@ -4,7 +4,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T5(Sized, ReprC1Zst); | ^^^^^^^^^ | - = note: this struct contains `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `ReprC1Zst`, which is marked with `#[repr(C)]`, 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-repr-c.rs:22:15 @@ -12,7 +12,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T6(ReprC1Zst, Sized); | ^^^^^^^^^ | - = note: this struct contains `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `ReprC1Zst`, which is marked with `#[repr(C)]`, 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-repr-c.rs:26:15 @@ -20,7 +20,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type | ^^ | - = note: this struct contains `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. error: aborting due to 3 previous errors diff --git a/tests/ui/repr/repr-transparent-uninhabited.rs b/tests/ui/repr/repr-transparent-uninhabited.rs new file mode 100644 index 0000000000000..578c20a0b1a40 --- /dev/null +++ b/tests/ui/repr/repr-transparent-uninhabited.rs @@ -0,0 +1,23 @@ +#![feature(never_type)] +#![deny(repr_transparent_external_private_fields)] + +enum Void {} + +pub type Sized = i32; + +#[repr(transparent)] +pub struct T1(!); +#[repr(transparent)] +pub struct T2((), Void); +#[repr(transparent)] +pub struct T3(!, ()); + +#[repr(transparent)] +pub struct T5(Sized, Void); +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + +#[repr(transparent)] +pub struct T6(!, Sized); +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + +fn main() {} diff --git a/tests/ui/repr/repr-transparent-uninhabited.stderr b/tests/ui/repr/repr-transparent-uninhabited.stderr new file mode 100644 index 0000000000000..e3d4c54fb1902 --- /dev/null +++ b/tests/ui/repr/repr-transparent-uninhabited.stderr @@ -0,0 +1,18 @@ +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-uninhabited.rs:16:22 + | +LL | pub struct T5(Sized, Void); + | ^^^^ + | + = note: this field contains `Void`, which is not (publicly) inhabited, 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-uninhabited.rs:20:15 + | +LL | pub struct T6(!, Sized); + | ^ + | + = note: this field contains `!`, which is not (publicly) inhabited, and makes it not a breaking change to become non-zero-sized in the future. + +error: aborting due to 2 previous errors + From c1dcd7eab855a7de5d2d1e8caf7ca6e71d924a04 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 Oct 2025 11:26:16 +0200 Subject: [PATCH 4/7] use non-semver-aware uninhabited check --- .../rustc_hir_analysis/src/check/check.rs | 22 ++++++++++--------- .../repr/repr-transparent-uninhabited.stderr | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 9b86ca86a87d5..57588debb8fe6 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1516,12 +1516,13 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // 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(); let trivial = layout.is_ok_and(|layout| layout.is_1zst()); - (span, trivial, ty) + let uninhabited = layout.is_ok_and(|layout| layout.is_uninhabited()); + (span, trivial, uninhabited, ty) }); - let non_trivial_fields = field_infos - .clone() - .filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }); + let non_trivial_fields = field_infos.clone().filter_map( + |(span, trivial, _uninhabited, _non_exhaustive)| if !trivial { Some(span) } else { None }, + ); let non_trivial_count = non_trivial_fields.clone().count(); if non_trivial_count >= 2 { bad_non_zero_sized_fields( @@ -1553,9 +1554,6 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) adt: DefId, ty: Ty<'tcx>, ) -> ControlFlow> { - if !ty.is_inhabited_from(tcx, adt, ty::TypingEnv::non_body_analysis(tcx, adt)) { - return ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::Uninhabited }); - } match ty.kind() { ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited_1zst(tcx, adt, t)), ty::Array(ty, _) => check_unsuited_1zst(tcx, adt, *ty), @@ -1589,11 +1587,15 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } let mut prev_unsuited_1zst = false; - for (span, trivial, ty) in field_infos { + for (span, trivial, uninhabited, ty) in field_infos { if !trivial { continue; } - if let Some(unsuited) = check_unsuited_1zst(tcx, adt.did(), ty).break_value() { + if let Some(unsuited) = + check_unsuited_1zst(tcx, adt.did(), ty).break_value().or_else(|| { + uninhabited.then_some(UnsuitedInfo { ty, reason: UnsuitedReason::Uninhabited }) + }) + { // 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_unsuited_1zst { @@ -1606,7 +1608,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`", UnsuitedReason::PrivateField => "contains private fields", UnsuitedReason::ReprC => "is marked with `#[repr(C)]`", - UnsuitedReason::Uninhabited => "is not (publicly) inhabited", + UnsuitedReason::Uninhabited => "is not inhabited", }; diag.note(format!( "this field contains `{field_ty}`, which {note}, \ diff --git a/tests/ui/repr/repr-transparent-uninhabited.stderr b/tests/ui/repr/repr-transparent-uninhabited.stderr index e3d4c54fb1902..0fd424ccf57c4 100644 --- a/tests/ui/repr/repr-transparent-uninhabited.stderr +++ b/tests/ui/repr/repr-transparent-uninhabited.stderr @@ -4,7 +4,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T5(Sized, Void); | ^^^^ | - = note: this field contains `Void`, which is not (publicly) inhabited, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `Void`, which is not inhabited, 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-uninhabited.rs:20:15 @@ -12,7 +12,7 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T6(!, Sized); | ^ | - = note: this field contains `!`, which is not (publicly) inhabited, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `!`, which is not inhabited, and makes it not a breaking change to become non-zero-sized in the future. error: aborting due to 2 previous errors From 44cb9b17548f7278dcabd9df27eb693cabd0e2fa Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 Oct 2025 11:37:41 +0200 Subject: [PATCH 5/7] revert code to be more like previous structure, but with explicit types --- .../rustc_hir_analysis/src/check/check.rs | 152 ++++++++++-------- 1 file changed, 86 insertions(+), 66 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 57588debb8fe6..39953674c6da3 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1509,6 +1509,25 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST). + // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private + // fields or `repr(C)`. We call those fields "unsuited". + struct FieldInfo<'tcx> { + span: Span, + trivial: bool, + unsuited: Option>, + } + struct UnsuitedInfo<'tcx> { + /// The source of the problem, a type that is found somewhere within the field type. + ty: Ty<'tcx>, + reason: UnsuitedReason, + } + enum UnsuitedReason { + NonExhaustive, + PrivateField, + ReprC, + Uninhabited, + } + 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); @@ -1516,13 +1535,71 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // 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(); let trivial = layout.is_ok_and(|layout| layout.is_1zst()); - let uninhabited = layout.is_ok_and(|layout| layout.is_uninhabited()); - (span, trivial, uninhabited, ty) + if !trivial { + // No need to even compute `unsuited`. + return FieldInfo { span, trivial, unsuited: None }; + } + + fn check_unsuited<'tcx>( + tcx: TyCtxt<'tcx>, + adt: DefId, + ty: Ty<'tcx>, + ) -> ControlFlow> { + match ty.kind() { + ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, adt, t)), + ty::Array(ty, _) => check_unsuited(tcx, adt, *ty), + ty::Adt(def, args) => { + if !def.did().is_local() + && !find_attr!( + tcx.get_all_attrs(def.did()), + AttributeKind::PubTransparent(_) + ) + { + let non_exhaustive = def.is_variant_list_non_exhaustive() + || def + .variants() + .iter() + .any(ty::VariantDef::is_field_list_non_exhaustive); + let has_priv = def.all_fields().any(|f| !f.vis.is_public()); + if non_exhaustive || has_priv { + return ControlFlow::Break(UnsuitedInfo { + ty, + reason: if non_exhaustive { + UnsuitedReason::NonExhaustive + } else { + UnsuitedReason::PrivateField + }, + }); + } + } + if def.repr().c() { + return ControlFlow::Break(UnsuitedInfo { + ty, + reason: UnsuitedReason::ReprC, + }); + } + def.all_fields() + .map(|field| field.ty(tcx, args)) + .try_for_each(|t| check_unsuited(tcx, adt, t)) + } + _ => ControlFlow::Continue(()), + } + } + + FieldInfo { + span, + trivial, + unsuited: check_unsuited(tcx, adt.did(), ty).break_value().or_else(|| { + // We don't need to check this recursively, a single top-level check suffices. + let uninhabited = layout.is_ok_and(|layout| layout.is_uninhabited()); + uninhabited.then_some(UnsuitedInfo { ty, reason: UnsuitedReason::Uninhabited }) + }), + } }); - let non_trivial_fields = field_infos.clone().filter_map( - |(span, trivial, _uninhabited, _non_exhaustive)| if !trivial { Some(span) } else { None }, - ); + let non_trivial_fields = field_infos + .clone() + .filter_map(|field| if !field.trivial { Some(field.span) } else { None }); let non_trivial_count = non_trivial_fields.clone().count(); if non_trivial_count >= 2 { bad_non_zero_sized_fields( @@ -1535,72 +1612,15 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) return; } - // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private - // fields or `repr(C)`. We call those fields "unsuited". Search for unsuited fields and - // error if the repr(transparent) condition relies on them. - enum UnsuitedReason { - NonExhaustive, - PrivateField, - ReprC, - Uninhabited, - } - struct UnsuitedInfo<'tcx> { - ty: Ty<'tcx>, - reason: UnsuitedReason, - } - - fn check_unsuited_1zst<'tcx>( - tcx: TyCtxt<'tcx>, - adt: DefId, - ty: Ty<'tcx>, - ) -> ControlFlow> { - match ty.kind() { - ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited_1zst(tcx, adt, t)), - ty::Array(ty, _) => check_unsuited_1zst(tcx, adt, *ty), - ty::Adt(def, args) => { - if !def.did().is_local() - && !find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::PubTransparent(_)) - { - let non_exhaustive = def.is_variant_list_non_exhaustive() - || def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive); - let has_priv = def.all_fields().any(|f| !f.vis.is_public()); - if non_exhaustive || has_priv { - return ControlFlow::Break(UnsuitedInfo { - ty, - reason: if non_exhaustive { - UnsuitedReason::NonExhaustive - } else { - UnsuitedReason::PrivateField - }, - }); - } - } - if def.repr().c() { - return ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::ReprC }); - } - def.all_fields() - .map(|field| field.ty(tcx, args)) - .try_for_each(|t| check_unsuited_1zst(tcx, adt, t)) - } - _ => ControlFlow::Continue(()), - } - } - let mut prev_unsuited_1zst = false; - for (span, trivial, uninhabited, ty) in field_infos { - if !trivial { - continue; - } - if let Some(unsuited) = - check_unsuited_1zst(tcx, adt.did(), ty).break_value().or_else(|| { - uninhabited.then_some(UnsuitedInfo { ty, reason: UnsuitedReason::Uninhabited }) - }) - { + for field in field_infos { + if let Some(unsuited) = field.unsuited { + assert!(field.trivial); // 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_unsuited_1zst { let mut diag = tcx.dcx().struct_span_err( - span, + field.span, "zero-sized fields in `repr(transparent)` cannot \ contain external non-exhaustive types", ); From b142623fe118c02008112e57b3ed57b91fa0c051 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 Oct 2025 11:57:24 +0200 Subject: [PATCH 6/7] make it a lint again --- .../rustc_hir_analysis/src/check/check.rs | 59 ++++++++------- .../repr/repr-transparent-non-exhaustive.rs | 16 ++++ .../repr-transparent-non-exhaustive.stderr | 73 ++++++++++++++----- tests/ui/repr/repr-transparent-repr-c.rs | 3 + tests/ui/repr/repr-transparent-repr-c.stderr | 15 +++- tests/ui/repr/repr-transparent-uninhabited.rs | 2 + .../repr/repr-transparent-uninhabited.stderr | 11 ++- .../uninhabited-transparent-return-abi.rs | 1 + 8 files changed, 134 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 39953674c6da3..8a0ecc5d3fc18 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -12,7 +12,9 @@ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::{LangItem, Node, attrs, find_attr, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode, WellFormedLoc}; -use rustc_lint_defs::builtin::UNSUPPORTED_CALLING_CONVENTIONS; +use rustc_lint_defs::builtin::{ + REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS, +}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; @@ -1510,7 +1512,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST). // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private - // fields or `repr(C)`. We call those fields "unsuited". + // fields or `repr(C)` or uninhabited. We call those fields "unsuited". struct FieldInfo<'tcx> { span: Span, trivial: bool, @@ -1539,6 +1541,16 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // No need to even compute `unsuited`. return FieldInfo { span, trivial, unsuited: None }; } + if layout.unwrap().is_uninhabited() { + // Uninhabited types aren't really "trivial"... + // See for some of the trouble + // this case used to cause. + return FieldInfo { + span, + trivial, + unsuited: Some(UnsuitedInfo { ty, reason: UnsuitedReason::Uninhabited }), + }; + } fn check_unsuited<'tcx>( tcx: TyCtxt<'tcx>, @@ -1586,15 +1598,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } } - FieldInfo { - span, - trivial, - unsuited: check_unsuited(tcx, adt.did(), ty).break_value().or_else(|| { - // We don't need to check this recursively, a single top-level check suffices. - let uninhabited = layout.is_ok_and(|layout| layout.is_uninhabited()); - uninhabited.then_some(UnsuitedInfo { ty, reason: UnsuitedReason::Uninhabited }) - }), - } + FieldInfo { span, trivial, unsuited: check_unsuited(tcx, adt.did(), ty).break_value() } }); let non_trivial_fields = field_infos @@ -1619,24 +1623,29 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // 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_unsuited_1zst { - let mut diag = tcx.dcx().struct_span_err( + tcx.node_span_lint( + REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, + tcx.local_def_id_to_hir_id(adt.did().expect_local()), field.span, - "zero-sized fields in `repr(transparent)` cannot \ + |lint| { + lint.primary_message( + "zero-sized fields in `repr(transparent)` cannot \ contain external non-exhaustive types", - ); - let note = match unsuited.reason { - UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`", - UnsuitedReason::PrivateField => "contains private fields", - UnsuitedReason::ReprC => "is marked with `#[repr(C)]`", - UnsuitedReason::Uninhabited => "is not inhabited", - }; - diag.note(format!( - "this field contains `{field_ty}`, which {note}, \ + ); + let note = match unsuited.reason { + UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`", + UnsuitedReason::PrivateField => "contains private fields", + UnsuitedReason::ReprC => "is marked with `#[repr(C)]`", + UnsuitedReason::Uninhabited => "is not inhabited", + }; + lint.note(format!( + "this field contains `{field_ty}`, which {note}, \ and makes it not a breaking change to become \ non-zero-sized in the future.", - field_ty = unsuited.ty, - )); - diag.emit(); + field_ty = unsuited.ty, + )); + }, + ); } else { prev_unsuited_1zst = true; } diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.rs b/tests/ui/repr/repr-transparent-non-exhaustive.rs index 00236886d0860..9894b89e8e418 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.rs +++ b/tests/ui/repr/repr-transparent-non-exhaustive.rs @@ -36,66 +36,82 @@ pub struct T4(Sized, ExternalIndirection<(InternalPrivate, InternalNonExhaustive #[repr(transparent)] pub struct T5(Sized, 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 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 T7(Sized, NonExhaustiveEnum); //~^ 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 T8(Sized, NonExhaustiveVariant); //~^ 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 T9(Sized, InternalIndirection); //~^ 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 T10(Sized, InternalIndirection); //~^ 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 T11(Sized, InternalIndirection); //~^ 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 T12(Sized, InternalIndirection); //~^ 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 T13(Sized, ExternalIndirection); //~^ 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 T14(Sized, ExternalIndirection); //~^ 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 T15(Sized, ExternalIndirection); //~^ 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 T16(Sized, ExternalIndirection); //~^ 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 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); diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.stderr b/tests/ui/repr/repr-transparent-non-exhaustive.stderr index b22cd9aa9e91a..edd39dc3adaee 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.stderr +++ b/tests/ui/repr/repr-transparent-non-exhaustive.stderr @@ -4,126 +4,163 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T5(Sized, 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 = note: this field contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +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:41:22 + --> $DIR/repr-transparent-non-exhaustive.rs:42:22 | LL | pub struct T6(Sized, 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 field 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:45:22 + --> $DIR/repr-transparent-non-exhaustive.rs:47:22 | LL | pub struct T7(Sized, NonExhaustiveEnum); | ^^^^^^^^^^^^^^^^^ | - = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = 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 field contains `NonExhaustiveEnum`, which is not inhabited, 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:49:22 + --> $DIR/repr-transparent-non-exhaustive.rs:52:22 | LL | pub struct T8(Sized, NonExhaustiveVariant); | ^^^^^^^^^^^^^^^^^^^^ | + = 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 field 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:53:22 + --> $DIR/repr-transparent-non-exhaustive.rs:57:22 | LL | pub struct T9(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = 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 field 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:57:23 + --> $DIR/repr-transparent-non-exhaustive.rs:62:23 | LL | pub struct T10(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = 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 field 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:61:23 + --> $DIR/repr-transparent-non-exhaustive.rs:67:23 | LL | pub struct T11(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = 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 field contains `InternalIndirection`, which is not inhabited, 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:65:23 + --> $DIR/repr-transparent-non-exhaustive.rs:72:23 | LL | pub struct T12(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = 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 field 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:69:23 + --> $DIR/repr-transparent-non-exhaustive.rs:77:23 | LL | pub struct T13(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = 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 field 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:73:23 + --> $DIR/repr-transparent-non-exhaustive.rs:82:23 | LL | pub struct T14(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = 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 field 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:77:23 + --> $DIR/repr-transparent-non-exhaustive.rs:87:23 | LL | pub struct T15(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = 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 field contains `ExternalIndirection`, which is not inhabited, 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:81:23 + --> $DIR/repr-transparent-non-exhaustive.rs:92:23 | LL | pub struct T16(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = 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 field 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:85:16 + --> $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 = note: this field 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:89:31 + --> $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 = note: this field 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:93:31 + --> $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 = note: this field 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:97:32 + --> $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 = note: this field 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 diff --git a/tests/ui/repr/repr-transparent-repr-c.rs b/tests/ui/repr/repr-transparent-repr-c.rs index d897c44c8b6b9..17d82870e352b 100644 --- a/tests/ui/repr/repr-transparent-repr-c.rs +++ b/tests/ui/repr/repr-transparent-repr-c.rs @@ -17,13 +17,16 @@ pub struct T3(ReprC1Zst, ()); #[repr(transparent)] pub struct T5(Sized, ReprC1Zst); //~^ 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 T6(ReprC1Zst, 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 T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +//~| WARN this was previously accepted by the compiler fn main() {} diff --git a/tests/ui/repr/repr-transparent-repr-c.stderr b/tests/ui/repr/repr-transparent-repr-c.stderr index 11e8de3d9a5df..43426d0cd6203 100644 --- a/tests/ui/repr/repr-transparent-repr-c.stderr +++ b/tests/ui/repr/repr-transparent-repr-c.stderr @@ -4,22 +4,33 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T5(Sized, ReprC1Zst); | ^^^^^^^^^ | + = 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 field contains `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-repr-c.rs:1:9 + | +LL | #![deny(repr_transparent_external_private_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-repr-c.rs:22:15 + --> $DIR/repr-transparent-repr-c.rs:23:15 | LL | pub struct T6(ReprC1Zst, 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 = note: this field contains `ReprC1Zst`, which is marked with `#[repr(C)]`, 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-repr-c.rs:26:15 + --> $DIR/repr-transparent-repr-c.rs:28:15 | LL | pub struct T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type | ^^ | + = 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 field contains `ReprC1Zst`, which is marked with `#[repr(C)]`, and makes it not a breaking change to become non-zero-sized in the future. error: aborting due to 3 previous errors diff --git a/tests/ui/repr/repr-transparent-uninhabited.rs b/tests/ui/repr/repr-transparent-uninhabited.rs index 578c20a0b1a40..7f6be9684fed2 100644 --- a/tests/ui/repr/repr-transparent-uninhabited.rs +++ b/tests/ui/repr/repr-transparent-uninhabited.rs @@ -15,9 +15,11 @@ pub struct T3(!, ()); #[repr(transparent)] pub struct T5(Sized, Void); //~^ 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 T6(!, Sized); //~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +//~| WARN this was previously accepted by the compiler fn main() {} diff --git a/tests/ui/repr/repr-transparent-uninhabited.stderr b/tests/ui/repr/repr-transparent-uninhabited.stderr index 0fd424ccf57c4..bd475ad25a7cc 100644 --- a/tests/ui/repr/repr-transparent-uninhabited.stderr +++ b/tests/ui/repr/repr-transparent-uninhabited.stderr @@ -4,14 +4,23 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T5(Sized, Void); | ^^^^ | + = 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 field contains `Void`, which is not inhabited, and makes it not a breaking change to become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-uninhabited.rs:2:9 + | +LL | #![deny(repr_transparent_external_private_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types - --> $DIR/repr-transparent-uninhabited.rs:20:15 + --> $DIR/repr-transparent-uninhabited.rs:21:15 | LL | pub struct T6(!, 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 = note: this field contains `!`, which is not inhabited, and makes it not a breaking change to become non-zero-sized in the future. error: aborting due to 2 previous errors diff --git a/tests/ui/uninhabited/uninhabited-transparent-return-abi.rs b/tests/ui/uninhabited/uninhabited-transparent-return-abi.rs index e439a82be5abb..7a658d80ee893 100644 --- a/tests/ui/uninhabited/uninhabited-transparent-return-abi.rs +++ b/tests/ui/uninhabited/uninhabited-transparent-return-abi.rs @@ -7,6 +7,7 @@ enum Void {} // Should be ABI-compatible with T, but wasn't prior to the PR adding this test. #[repr(transparent)] +#[allow(repr_transparent_external_private_fields)] struct NoReturn(T, Void); // Returned by invisible reference (in most ABIs) From 0f4ab71374fa2ca05fbfa28d2e052231eaa1bd41 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 11 Oct 2025 14:59:03 +0200 Subject: [PATCH 7/7] remove hyper from PGO so we can crater this --- src/build_helper/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/build_helper/src/lib.rs b/src/build_helper/src/lib.rs index 266eedc624582..50027488760cf 100644 --- a/src/build_helper/src/lib.rs +++ b/src/build_helper/src/lib.rs @@ -18,7 +18,6 @@ pub const LLVM_PGO_CRATES: &[&str] = &[ "ripgrep-14.1.1", "regex-automata-0.4.8", "clap_derive-4.5.32", - "hyper-1.6.0", ]; /// The default set of crates for opt-dist to collect rustc profiles.