@@ -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