@@ -12,9 +12,7 @@ use rustc_hir::def::{CtorKind, DefKind};
1212use rustc_hir:: { LangItem , Node , attrs, find_attr, intravisit} ;
1313use rustc_infer:: infer:: { RegionVariableOrigin , TyCtxtInferExt } ;
1414use rustc_infer:: traits:: { Obligation , ObligationCauseCode , WellFormedLoc } ;
15- use rustc_lint_defs:: builtin:: {
16- REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS , UNSUPPORTED_CALLING_CONVENTIONS ,
17- } ;
15+ use rustc_lint_defs:: builtin:: { REPR_TRANSPARENT_NON_ZST_FIELDS , UNSUPPORTED_CALLING_CONVENTIONS } ;
1816use rustc_middle:: hir:: nested_filter;
1917use rustc_middle:: middle:: resolve_bound_vars:: ResolvedArg ;
2018use rustc_middle:: middle:: stability:: EvalResult ;
@@ -1509,31 +1507,46 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15091507 }
15101508
15111509 let typing_env = ty:: TypingEnv :: non_body_analysis ( tcx, adt. did ( ) ) ;
1512- // For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
1513- // "known" respecting #[non_exhaustive] attributes.
1510+ // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST).
1511+ // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private
1512+ // fields or `repr(C)`. We call those fields "unsuited".
1513+ struct FieldInfo < ' tcx > {
1514+ span : Span ,
1515+ trivial : bool ,
1516+ unsuited : Option < UnsuitedInfo < ' tcx > > ,
1517+ }
1518+ struct UnsuitedInfo < ' tcx > {
1519+ /// The source of the problem, a type that is found somewhere within the field type.
1520+ ty : Ty < ' tcx > ,
1521+ reason : UnsuitedReason ,
1522+ }
1523+ enum UnsuitedReason {
1524+ NonExhaustive ,
1525+ PrivateField ,
1526+ ReprC ,
1527+ }
1528+
15141529 let field_infos = adt. all_fields ( ) . map ( |field| {
15151530 let ty = field. ty ( tcx, GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
15161531 let layout = tcx. layout_of ( typing_env. as_query_input ( ty) ) ;
15171532 // We are currently checking the type this field came from, so it must be local
15181533 let span = tcx. hir_span_if_local ( field. did ) . unwrap ( ) ;
15191534 let trivial = layout. is_ok_and ( |layout| layout. is_1zst ( ) ) ;
15201535 if !trivial {
1521- return ( span, trivial, None ) ;
1536+ // No need to even compute `unsuited`.
1537+ return FieldInfo { span, trivial, unsuited : None } ;
15221538 }
1523- // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
15241539
1525- fn check_non_exhaustive < ' tcx > (
1540+ fn check_unsuited < ' tcx > (
15261541 tcx : TyCtxt < ' tcx > ,
15271542 typing_env : ty:: TypingEnv < ' tcx > ,
1528- t : Ty < ' tcx > ,
1529- ) -> ControlFlow < ( & ' static str , DefId , GenericArgsRef < ' tcx > , bool ) > {
1543+ ty : Ty < ' tcx > ,
1544+ ) -> ControlFlow < UnsuitedInfo < ' tcx > > {
15301545 // We can encounter projections during traversal, so ensure the type is normalized.
1531- let t = tcx. try_normalize_erasing_regions ( typing_env, t) . unwrap_or ( t) ;
1532- match t. kind ( ) {
1533- ty:: Tuple ( list) => {
1534- list. iter ( ) . try_for_each ( |t| check_non_exhaustive ( tcx, typing_env, t) )
1535- }
1536- ty:: Array ( ty, _) => check_non_exhaustive ( tcx, typing_env, * ty) ,
1546+ let ty = tcx. try_normalize_erasing_regions ( typing_env, ty) . unwrap_or ( ty) ;
1547+ match ty. kind ( ) {
1548+ ty:: Tuple ( list) => list. iter ( ) . try_for_each ( |t| check_unsuited ( tcx, typing_env, t) ) ,
1549+ ty:: Array ( ty, _) => check_unsuited ( tcx, typing_env, * ty) ,
15371550 ty:: Adt ( def, args) => {
15381551 if !def. did ( ) . is_local ( )
15391552 && !find_attr ! (
@@ -1548,28 +1561,36 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15481561 . any ( ty:: VariantDef :: is_field_list_non_exhaustive) ;
15491562 let has_priv = def. all_fields ( ) . any ( |f| !f. vis . is_public ( ) ) ;
15501563 if non_exhaustive || has_priv {
1551- return ControlFlow :: Break ( (
1552- def. descr ( ) ,
1553- def. did ( ) ,
1554- args,
1555- non_exhaustive,
1556- ) ) ;
1564+ return ControlFlow :: Break ( UnsuitedInfo {
1565+ ty,
1566+ reason : if non_exhaustive {
1567+ UnsuitedReason :: NonExhaustive
1568+ } else {
1569+ UnsuitedReason :: PrivateField
1570+ } ,
1571+ } ) ;
15571572 }
15581573 }
1574+ if def. repr ( ) . c ( ) {
1575+ return ControlFlow :: Break ( UnsuitedInfo {
1576+ ty,
1577+ reason : UnsuitedReason :: ReprC ,
1578+ } ) ;
1579+ }
15591580 def. all_fields ( )
15601581 . map ( |field| field. ty ( tcx, args) )
1561- . try_for_each ( |t| check_non_exhaustive ( tcx, typing_env, t) )
1582+ . try_for_each ( |t| check_unsuited ( tcx, typing_env, t) )
15621583 }
15631584 _ => ControlFlow :: Continue ( ( ) ) ,
15641585 }
15651586 }
15661587
1567- ( span, trivial, check_non_exhaustive ( tcx, typing_env, ty) . break_value ( ) )
1588+ FieldInfo { span, trivial, unsuited : check_unsuited ( tcx, typing_env, ty) . break_value ( ) }
15681589 } ) ;
15691590
15701591 let non_trivial_fields = field_infos
15711592 . clone ( )
1572- . filter_map ( |( span , trivial , _non_exhaustive ) | if !trivial { Some ( span) } else { None } ) ;
1593+ . filter_map ( |field | if !field . trivial { Some ( field . span ) } else { None } ) ;
15731594 let non_trivial_count = non_trivial_fields. clone ( ) . count ( ) ;
15741595 if non_trivial_count >= 2 {
15751596 bad_non_zero_sized_fields (
@@ -1581,36 +1602,40 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
15811602 ) ;
15821603 return ;
15831604 }
1584- let mut prev_non_exhaustive_1zst = false ;
1585- for ( span, _trivial, non_exhaustive_1zst) in field_infos {
1586- if let Some ( ( descr, def_id, args, non_exhaustive) ) = non_exhaustive_1zst {
1605+
1606+ let mut prev_unsuited_1zst = false ;
1607+ for field in field_infos {
1608+ if let Some ( unsuited) = field. unsuited {
1609+ assert ! ( field. trivial) ;
15871610 // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
15881611 // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
1589- if non_trivial_count > 0 || prev_non_exhaustive_1zst {
1612+ if non_trivial_count > 0 || prev_unsuited_1zst {
15901613 tcx. node_span_lint (
1591- REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS ,
1614+ REPR_TRANSPARENT_NON_ZST_FIELDS ,
15921615 tcx. local_def_id_to_hir_id ( adt. did ( ) . expect_local ( ) ) ,
1593- span,
1616+ field . span ,
15941617 |lint| {
1618+ let title = match unsuited. reason {
1619+ UnsuitedReason :: NonExhaustive => "external non-exhaustive types" ,
1620+ UnsuitedReason :: PrivateField => "external types with private fields" ,
1621+ UnsuitedReason :: ReprC => "`repr(C)` types" ,
1622+ } ;
15951623 lint. primary_message (
1596- "zero-sized fields in `repr(transparent)` cannot \
1597- contain external non-exhaustive types",
1624+ format ! ( "zero-sized fields in `repr(transparent)` cannot contain {title}" ) ,
15981625 ) ;
1599- let note = if non_exhaustive {
1600- "is marked with `#[non_exhaustive]`"
1601- } else {
1602- "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 a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets." ,
16031630 } ;
1604- let field_ty = tcx. def_path_str_with_args ( def_id, args) ;
16051631 lint. note ( format ! (
1606- "this {descr} contains `{field_ty}`, which {note}, \
1607- and makes it not a breaking change to become \
1608- non-zero-sized in the future."
1632+ "this field contains `{field_ty}`, which {note}" ,
1633+ field_ty = unsuited. ty,
16091634 ) ) ;
16101635 } ,
1611- )
1636+ ) ;
16121637 } else {
1613- prev_non_exhaustive_1zst = true ;
1638+ prev_unsuited_1zst = true ;
16141639 }
16151640 }
16161641 }
0 commit comments