@@ -1511,31 +1511,46 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15111511 }
15121512
15131513 let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, adt. did ( ) ) ;
1514- // For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
1515- // "known" respecting #[non_exhaustive] attributes.
1514+ // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
1515+ // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1516+ // fields or `repr(C)` or uninhabited. We call those fields "unsuited".
1517+ struct FieldInfo < ' tcx > {
1518+ span : Span ,
1519+ trivial : bool ,
1520+ unsuited : Option < UnsuitedInfo < ' tcx > > ,
1521+ }
1522+ struct UnsuitedInfo < ' tcx > {
1523+ /// The source of the problem, a type that is found somewhere within the field type.
1524+ ty : Ty < ' tcx > ,
1525+ reason : UnsuitedReason ,
1526+ }
1527+ enum UnsuitedReason {
1528+ NonExhaustive ,
1529+ PrivateField ,
1530+ ReprC ,
1531+ }
1532+
15161533 let field_infos = adt. all_fields ( ) . map ( |field| {
15171534 let ty = field. ty ( tcx, GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
15181535 let layout = tcx. layout_of ( typing_env. as_query_input ( ty) ) ;
15191536 // We are currently checking the type this field came from, so it must be local
15201537 let span = tcx. hir_span_if_local ( field. did ) . unwrap ( ) ;
15211538 let trivial = layout. is_ok_and ( |layout| layout. is_1zst ( ) ) ;
15221539 if !trivial {
1523- return ( span, trivial, None ) ;
1540+ // No need to even compute `unsuited`.
1541+ return FieldInfo { span, trivial, unsuited : None } ;
15241542 }
1525- // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
15261543
1527- fn check_non_exhaustive < ' tcx > (
1544+ fn check_unsuited < ' tcx > (
15281545 tcx : TyCtxt < ' tcx > ,
15291546 typing_env : ty:: TypingEnv < ' tcx > ,
1530- t : Ty < ' tcx > ,
1531- ) -> ControlFlow < ( & ' static str , DefId , GenericArgsRef < ' tcx > , bool ) > {
1547+ ty : Ty < ' tcx > ,
1548+ ) -> ControlFlow < UnsuitedInfo < ' tcx > > {
15321549 // We can encounter projections during traversal, so ensure the type is normalized.
1533- let t = tcx. try_normalize_erasing_regions ( typing_env, t) . unwrap_or ( t) ;
1534- match t. kind ( ) {
1535- ty:: Tuple ( list) => {
1536- list. iter ( ) . try_for_each ( |t| check_non_exhaustive ( tcx, typing_env, t) )
1537- }
1538- ty:: Array ( ty, _) => check_non_exhaustive ( tcx, typing_env, * ty) ,
1550+ let ty = tcx. try_normalize_erasing_regions ( typing_env, ty) . unwrap_or ( ty) ;
1551+ match ty. kind ( ) {
1552+ ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| check_unsuited ( tcx, typing_env, t) ) ,
1553+ ty:: Array ( ty, _) => check_unsuited ( tcx, typing_env, * ty) ,
15391554 ty:: Adt ( def, args) => {
15401555 if !def. did ( ) . is_local ( )
15411556 && !find_attr ! (
@@ -1550,28 +1565,36 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15501565 . any ( ty:: VariantDef :: is_field_list_non_exhaustive) ;
15511566 let has_priv = def. all_fields ( ) . any ( |f| !f. vis . is_public ( ) ) ;
15521567 if non_exhaustive || has_priv {
1553- return ControlFlow :: Break ( (
1554- def. descr ( ) ,
1555- def. did ( ) ,
1556- args,
1557- non_exhaustive,
1558- ) ) ;
1568+ return ControlFlow :: Break ( UnsuitedInfo {
1569+ ty,
1570+ reason : if non_exhaustive {
1571+ UnsuitedReason :: NonExhaustive
1572+ } else {
1573+ UnsuitedReason :: PrivateField
1574+ } ,
1575+ } ) ;
15591576 }
15601577 }
1578+ if def. repr ( ) . c ( ) {
1579+ return ControlFlow :: Break ( UnsuitedInfo {
1580+ ty,
1581+ reason : UnsuitedReason :: ReprC ,
1582+ } ) ;
1583+ }
15611584 def. all_fields ( )
15621585 . map ( |field| field. ty ( tcx, args) )
1563- . try_for_each ( |t| check_non_exhaustive ( tcx, typing_env, t) )
1586+ . try_for_each ( |t| check_unsuited ( tcx, typing_env, t) )
15641587 }
15651588 _ => ControlFlow :: Continue ( ( ) ) ,
15661589 }
15671590 }
15681591
1569- ( span, trivial, check_non_exhaustive ( tcx, typing_env, ty) . break_value ( ) )
1592+ FieldInfo { span, trivial, unsuited : check_unsuited ( tcx, typing_env, ty) . break_value ( ) }
15701593 } ) ;
15711594
15721595 let non_trivial_fields = field_infos
15731596 . clone ( )
1574- . filter_map ( |( span , trivial , _non_exhaustive ) | if !trivial { Some ( span) } else { None } ) ;
1597+ . filter_map ( |field | if !field . trivial { Some ( field . span ) } else { None } ) ;
15751598 let non_trivial_count = non_trivial_fields. clone ( ) . count ( ) ;
15761599 if non_trivial_count >= 2 {
15771600 bad_non_zero_sized_fields (
@@ -1583,36 +1606,36 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15831606 ) ;
15841607 return ;
15851608 }
1586- let mut prev_non_exhaustive_1zst = false ;
1587- for ( span, _trivial, non_exhaustive_1zst) in field_infos {
1588- if let Some ( ( descr, def_id, args, non_exhaustive) ) = non_exhaustive_1zst {
1609+
1610+ let mut prev_unsuited_1zst = false ;
1611+ for field in field_infos {
1612+ if let Some ( unsuited) = field. unsuited {
1613+ assert ! ( field. trivial) ;
15891614 // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
15901615 // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
1591- if non_trivial_count > 0 || prev_non_exhaustive_1zst {
1616+ if non_trivial_count > 0 || prev_unsuited_1zst {
15921617 tcx. node_span_lint (
15931618 REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS ,
15941619 tcx. local_def_id_to_hir_id ( adt. did ( ) . expect_local ( ) ) ,
1595- span,
1620+ field . span ,
15961621 |lint| {
15971622 lint. primary_message (
15981623 "zero-sized fields in `repr(transparent)` cannot \
15991624 contain external non-exhaustive types",
16001625 ) ;
1601- let note = if non_exhaustive {
1602- "is marked with `#[non_exhaustive]`"
1603- } else {
1604- "contains private fields"
1626+ let note = match unsuited . reason {
1627+ UnsuitedReason :: NonExhaustive => "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future." ,
1628+ UnsuitedReason :: PrivateField => "contains private fields, so it could become non-zero-sized in the future." ,
1629+ UnsuitedReason :: ReprC => "is marked with `#[repr(C)]`, which means it cannot be ignored for all ABIs." ,
16051630 } ;
1606- let field_ty = tcx. def_path_str_with_args ( def_id, args) ;
16071631 lint. note ( format ! (
1608- "this {descr} contains `{field_ty}`, which {note}, \
1609- and makes it not a breaking change to become \
1610- non-zero-sized in the future."
1632+ "this field contains `{field_ty}`, which {note}" ,
1633+ field_ty = unsuited. ty,
16111634 ) ) ;
16121635 } ,
1613- )
1636+ ) ;
16141637 } else {
1615- prev_non_exhaustive_1zst = true ;
1638+ prev_unsuited_1zst = true ;
16161639 }
16171640 }
16181641 }
0 commit comments