@@ -1510,8 +1510,26 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15101510 return ;
15111511 }
15121512
1513- // For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
1514- // "known" respecting #[non_exhaustive] attributes.
1513+ // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
1514+ // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1515+ // fields or `repr(C)` or uninhabited. We call those fields "unsuited".
1516+ struct FieldInfo < ' tcx > {
1517+ span : Span ,
1518+ trivial : bool ,
1519+ unsuited : Option < UnsuitedInfo < ' tcx > > ,
1520+ }
1521+ struct UnsuitedInfo < ' tcx > {
1522+ /// The source of the problem, a type that is found somewhere within the field type.
1523+ ty : Ty < ' tcx > ,
1524+ reason : UnsuitedReason ,
1525+ }
1526+ enum UnsuitedReason {
1527+ NonExhaustive ,
1528+ PrivateField ,
1529+ ReprC ,
1530+ Uninhabited ,
1531+ }
1532+
15151533 let field_infos = adt. all_fields ( ) . map ( |field| {
15161534 let ty = field. ty ( tcx, GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
15171535 let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, field. did ) ;
@@ -1520,17 +1538,28 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15201538 let span = tcx. hir_span_if_local ( field. did ) . unwrap ( ) ;
15211539 let trivial = layout. is_ok_and ( |layout| layout. is_1zst ( ) ) ;
15221540 if !trivial {
1523- return ( span, trivial, None ) ;
1541+ // No need to even compute `unsuited`.
1542+ return FieldInfo { span, trivial, unsuited : None } ;
1543+ }
1544+ if layout. unwrap ( ) . is_uninhabited ( ) {
1545+ // Uninhabited types aren't really "trivial"...
1546+ // See <https://github.com/rust-lang/rust/issues/135802> for some of the trouble
1547+ // this case used to cause.
1548+ return FieldInfo {
1549+ span,
1550+ trivial,
1551+ unsuited : Some ( UnsuitedInfo { ty, reason : UnsuitedReason :: Uninhabited } ) ,
1552+ } ;
15241553 }
1525- // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
15261554
1527- fn check_non_exhaustive < ' tcx > (
1555+ fn check_unsuited < ' tcx > (
15281556 tcx : TyCtxt < ' tcx > ,
1529- t : Ty < ' tcx > ,
1530- ) -> ControlFlow < ( & ' static str , DefId , GenericArgsRef < ' tcx > , bool ) > {
1531- match t. kind ( ) {
1532- ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| check_non_exhaustive ( tcx, t) ) ,
1533- ty:: Array ( ty, _) => check_non_exhaustive ( tcx, * ty) ,
1557+ adt : DefId ,
1558+ ty : Ty < ' tcx > ,
1559+ ) -> ControlFlow < UnsuitedInfo < ' tcx > > {
1560+ match ty. kind ( ) {
1561+ ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| check_unsuited ( tcx, adt, t) ) ,
1562+ ty:: Array ( ty, _) => check_unsuited ( tcx, adt, * ty) ,
15341563 ty:: Adt ( def, args) => {
15351564 if !def. did ( ) . is_local ( )
15361565 && !find_attr ! (
@@ -1545,28 +1574,36 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15451574 . any ( ty:: VariantDef :: is_field_list_non_exhaustive) ;
15461575 let has_priv = def. all_fields ( ) . any ( |f| !f. vis . is_public ( ) ) ;
15471576 if non_exhaustive || has_priv {
1548- return ControlFlow :: Break ( (
1549- def. descr ( ) ,
1550- def. did ( ) ,
1551- args,
1552- non_exhaustive,
1553- ) ) ;
1577+ return ControlFlow :: Break ( UnsuitedInfo {
1578+ ty,
1579+ reason : if non_exhaustive {
1580+ UnsuitedReason :: NonExhaustive
1581+ } else {
1582+ UnsuitedReason :: PrivateField
1583+ } ,
1584+ } ) ;
15541585 }
15551586 }
1587+ if def. repr ( ) . c ( ) {
1588+ return ControlFlow :: Break ( UnsuitedInfo {
1589+ ty,
1590+ reason : UnsuitedReason :: ReprC ,
1591+ } ) ;
1592+ }
15561593 def. all_fields ( )
15571594 . map ( |field| field. ty ( tcx, args) )
1558- . try_for_each ( |t| check_non_exhaustive ( tcx, t) )
1595+ . try_for_each ( |t| check_unsuited ( tcx, adt , t) )
15591596 }
15601597 _ => ControlFlow :: Continue ( ( ) ) ,
15611598 }
15621599 }
15631600
1564- ( span, trivial, check_non_exhaustive ( tcx, ty) . break_value ( ) )
1601+ FieldInfo { span, trivial, unsuited : check_unsuited ( tcx, adt . did ( ) , ty) . break_value ( ) }
15651602 } ) ;
15661603
15671604 let non_trivial_fields = field_infos
15681605 . clone ( )
1569- . filter_map ( |( span , trivial , _non_exhaustive ) | if !trivial { Some ( span) } else { None } ) ;
1606+ . filter_map ( |field | if !field . trivial { Some ( field . span ) } else { None } ) ;
15701607 let non_trivial_count = non_trivial_fields. clone ( ) . count ( ) ;
15711608 if non_trivial_count >= 2 {
15721609 bad_non_zero_sized_fields (
@@ -1578,36 +1615,39 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15781615 ) ;
15791616 return ;
15801617 }
1581- let mut prev_non_exhaustive_1zst = false ;
1582- for ( span, _trivial, non_exhaustive_1zst) in field_infos {
1583- if let Some ( ( descr, def_id, args, non_exhaustive) ) = non_exhaustive_1zst {
1618+
1619+ let mut prev_unsuited_1zst = false ;
1620+ for field in field_infos {
1621+ if let Some ( unsuited) = field. unsuited {
1622+ assert ! ( field. trivial) ;
15841623 // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
15851624 // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
1586- if non_trivial_count > 0 || prev_non_exhaustive_1zst {
1625+ if non_trivial_count > 0 || prev_unsuited_1zst {
15871626 tcx. node_span_lint (
15881627 REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS ,
15891628 tcx. local_def_id_to_hir_id ( adt. did ( ) . expect_local ( ) ) ,
1590- span,
1629+ field . span ,
15911630 |lint| {
15921631 lint. primary_message (
15931632 "zero-sized fields in `repr(transparent)` cannot \
15941633 contain external non-exhaustive types",
15951634 ) ;
1596- let note = if non_exhaustive {
1597- "is marked with `#[non_exhaustive]`"
1598- } else {
1599- "contains private fields"
1635+ let note = match unsuited. reason {
1636+ UnsuitedReason :: NonExhaustive => "is marked with `#[non_exhaustive]`" ,
1637+ UnsuitedReason :: PrivateField => "contains private fields" ,
1638+ UnsuitedReason :: ReprC => "is marked with `#[repr(C)]`" ,
1639+ UnsuitedReason :: Uninhabited => "is not inhabited" ,
16001640 } ;
1601- let field_ty = tcx. def_path_str_with_args ( def_id, args) ;
16021641 lint. note ( format ! (
1603- "this {descr} contains `{field_ty}`, which {note}, \
1642+ "this field contains `{field_ty}`, which {note}, \
16041643 and makes it not a breaking change to become \
1605- non-zero-sized in the future."
1644+ non-zero-sized in the future.",
1645+ field_ty = unsuited. ty,
16061646 ) ) ;
16071647 } ,
1608- )
1648+ ) ;
16091649 } else {
1610- prev_non_exhaustive_1zst = true ;
1650+ prev_unsuited_1zst = true ;
16111651 }
16121652 }
16131653 }
0 commit comments