@@ -1509,20 +1509,97 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15091509 }
15101510
15111511 // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
1512+ // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1513+ // fields or `repr(C)`. We call those fields "unsuited".
1514+ struct FieldInfo < ' tcx > {
1515+ span : Span ,
1516+ trivial : bool ,
1517+ unsuited : Option < UnsuitedInfo < ' tcx > > ,
1518+ }
1519+ struct UnsuitedInfo < ' tcx > {
1520+ /// The source of the problem, a type that is found somewhere within the field type.
1521+ ty : Ty < ' tcx > ,
1522+ reason : UnsuitedReason ,
1523+ }
1524+ enum UnsuitedReason {
1525+ NonExhaustive ,
1526+ PrivateField ,
1527+ ReprC ,
1528+ Uninhabited ,
1529+ }
1530+
15121531 let field_infos = adt. all_fields ( ) . map ( |field| {
15131532 let ty = field. ty ( tcx, GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
15141533 let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, field. did ) ;
15151534 let layout = tcx. layout_of ( typing_env. as_query_input ( ty) ) ;
15161535 // We are currently checking the type this field came from, so it must be local
15171536 let span = tcx. hir_span_if_local ( field. did ) . unwrap ( ) ;
15181537 let trivial = layout. is_ok_and ( |layout| layout. is_1zst ( ) ) ;
1519- let uninhabited = layout. is_ok_and ( |layout| layout. is_uninhabited ( ) ) ;
1520- ( span, trivial, uninhabited, ty)
1538+ if !trivial {
1539+ // No need to even compute `unsuited`.
1540+ return FieldInfo { span, trivial, unsuited : None } ;
1541+ }
1542+
1543+ fn check_unsuited < ' tcx > (
1544+ tcx : TyCtxt < ' tcx > ,
1545+ adt : DefId ,
1546+ ty : Ty < ' tcx > ,
1547+ ) -> ControlFlow < UnsuitedInfo < ' tcx > > {
1548+ match ty. kind ( ) {
1549+ ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| check_unsuited ( tcx, adt, t) ) ,
1550+ ty:: Array ( ty, _) => check_unsuited ( tcx, adt, * ty) ,
1551+ ty:: Adt ( def, args) => {
1552+ if !def. did ( ) . is_local ( )
1553+ && !find_attr ! (
1554+ tcx. get_all_attrs( def. did( ) ) ,
1555+ AttributeKind :: PubTransparent ( _)
1556+ )
1557+ {
1558+ let non_exhaustive = def. is_variant_list_non_exhaustive ( )
1559+ || def
1560+ . variants ( )
1561+ . iter ( )
1562+ . any ( ty:: VariantDef :: is_field_list_non_exhaustive) ;
1563+ let has_priv = def. all_fields ( ) . any ( |f| !f. vis . is_public ( ) ) ;
1564+ if non_exhaustive || has_priv {
1565+ return ControlFlow :: Break ( UnsuitedInfo {
1566+ ty,
1567+ reason : if non_exhaustive {
1568+ UnsuitedReason :: NonExhaustive
1569+ } else {
1570+ UnsuitedReason :: PrivateField
1571+ } ,
1572+ } ) ;
1573+ }
1574+ }
1575+ if def. repr ( ) . c ( ) {
1576+ return ControlFlow :: Break ( UnsuitedInfo {
1577+ ty,
1578+ reason : UnsuitedReason :: ReprC ,
1579+ } ) ;
1580+ }
1581+ def. all_fields ( )
1582+ . map ( |field| field. ty ( tcx, args) )
1583+ . try_for_each ( |t| check_unsuited ( tcx, adt, t) )
1584+ }
1585+ _ => ControlFlow :: Continue ( ( ) ) ,
1586+ }
1587+ }
1588+
1589+ FieldInfo {
1590+ span,
1591+ trivial,
1592+ unsuited : check_unsuited ( tcx, adt. did ( ) , ty) . break_value ( ) . or_else ( || {
1593+ // We don't need to check this recursively, a single top-level check suffices.
1594+ let uninhabited = layout. is_ok_and ( |layout| layout. is_uninhabited ( ) ) ;
1595+ uninhabited. then_some ( UnsuitedInfo { ty, reason : UnsuitedReason :: Uninhabited } )
1596+ } ) ,
1597+ }
15211598 } ) ;
15221599
1523- let non_trivial_fields = field_infos. clone ( ) . filter_map (
1524- | ( span , trivial , _uninhabited , _non_exhaustive ) | if !trivial { Some ( span ) } else { None } ,
1525- ) ;
1600+ let non_trivial_fields = field_infos
1601+ . clone ( )
1602+ . filter_map ( |field| if !field . trivial { Some ( field . span ) } else { None } ) ;
15261603 let non_trivial_count = non_trivial_fields. clone ( ) . count ( ) ;
15271604 if non_trivial_count >= 2 {
15281605 bad_non_zero_sized_fields (
@@ -1535,72 +1612,15 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15351612 return ;
15361613 }
15371614
1538- // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1539- // fields or `repr(C)`. We call those fields "unsuited". Search for unsuited fields and
1540- // error if the repr(transparent) condition relies on them.
1541- enum UnsuitedReason {
1542- NonExhaustive ,
1543- PrivateField ,
1544- ReprC ,
1545- Uninhabited ,
1546- }
1547- struct UnsuitedInfo < ' tcx > {
1548- ty : Ty < ' tcx > ,
1549- reason : UnsuitedReason ,
1550- }
1551-
1552- fn check_unsuited_1zst < ' tcx > (
1553- tcx : TyCtxt < ' tcx > ,
1554- adt : DefId ,
1555- ty : Ty < ' tcx > ,
1556- ) -> ControlFlow < UnsuitedInfo < ' tcx > > {
1557- match ty. kind ( ) {
1558- ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| check_unsuited_1zst ( tcx, adt, t) ) ,
1559- ty:: Array ( ty, _) => check_unsuited_1zst ( tcx, adt, * ty) ,
1560- ty:: Adt ( def, args) => {
1561- if !def. did ( ) . is_local ( )
1562- && !find_attr ! ( tcx. get_all_attrs( def. did( ) ) , AttributeKind :: PubTransparent ( _) )
1563- {
1564- let non_exhaustive = def. is_variant_list_non_exhaustive ( )
1565- || def. variants ( ) . iter ( ) . any ( ty:: VariantDef :: is_field_list_non_exhaustive) ;
1566- let has_priv = def. all_fields ( ) . any ( |f| !f. vis . is_public ( ) ) ;
1567- if non_exhaustive || has_priv {
1568- return ControlFlow :: Break ( UnsuitedInfo {
1569- ty,
1570- reason : if non_exhaustive {
1571- UnsuitedReason :: NonExhaustive
1572- } else {
1573- UnsuitedReason :: PrivateField
1574- } ,
1575- } ) ;
1576- }
1577- }
1578- if def. repr ( ) . c ( ) {
1579- return ControlFlow :: Break ( UnsuitedInfo { ty, reason : UnsuitedReason :: ReprC } ) ;
1580- }
1581- def. all_fields ( )
1582- . map ( |field| field. ty ( tcx, args) )
1583- . try_for_each ( |t| check_unsuited_1zst ( tcx, adt, t) )
1584- }
1585- _ => ControlFlow :: Continue ( ( ) ) ,
1586- }
1587- }
1588-
15891615 let mut prev_unsuited_1zst = false ;
1590- for ( span, trivial, uninhabited, ty) in field_infos {
1591- if !trivial {
1592- continue ;
1593- }
1594- if let Some ( unsuited) =
1595- check_unsuited_1zst ( tcx, adt. did ( ) , ty) . break_value ( ) . or_else ( || {
1596- uninhabited. then_some ( UnsuitedInfo { ty, reason : UnsuitedReason :: Uninhabited } )
1597- } )
1598- {
1616+ for field in field_infos {
1617+ if let Some ( unsuited) = field. unsuited {
1618+ assert ! ( field. trivial) ;
15991619 // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
16001620 // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
16011621 if non_trivial_count > 0 || prev_unsuited_1zst {
16021622 let mut diag = tcx. dcx ( ) . struct_span_err (
1603- span,
1623+ field . span ,
16041624 "zero-sized fields in `repr(transparent)` cannot \
16051625 contain external non-exhaustive types",
16061626 ) ;
0 commit comments