@@ -709,17 +709,15 @@ impl Item {
709709 Some ( tcx. visibility ( def_id) )
710710 }
711711
712- pub ( crate ) fn attributes (
712+ pub ( crate ) fn attributes < ' tcx > (
713713 & self ,
714- tcx : TyCtxt < ' _ > ,
714+ tcx : TyCtxt < ' tcx > ,
715715 cache : & Cache ,
716716 keep_as_is : bool ,
717717 ) -> Vec < String > {
718718 const ALLOWED_ATTRIBUTES : & [ Symbol ] =
719719 & [ sym:: export_name, sym:: link_section, sym:: no_mangle, sym:: non_exhaustive] ;
720720
721- use rustc_abi:: IntegerType ;
722-
723721 let mut attrs: Vec < String > = self
724722 . attrs
725723 . other_attrs
@@ -739,67 +737,122 @@ impl Item {
739737 }
740738 } )
741739 . collect ( ) ;
742- if !keep_as_is
743- && let Some ( def_id) = self . def_id ( )
744- && let ItemType :: Struct | ItemType :: Enum | ItemType :: Union = self . type_ ( )
745- {
746- let adt = tcx. adt_def ( def_id) ;
747- let repr = adt. repr ( ) ;
748- let mut out = Vec :: new ( ) ;
749- if repr. c ( ) {
750- out. push ( "C" ) ;
751- }
752- if repr. transparent ( ) {
753- // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
754- // field is public in case all fields are 1-ZST fields.
755- let render_transparent = cache. document_private
756- || adt
757- . all_fields ( )
758- . find ( |field| {
759- let ty =
760- field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
761- tcx. layout_of ( tcx. param_env ( field. did ) . and ( ty) )
762- . is_ok_and ( |layout| !layout. is_1zst ( ) )
763- } )
764- . map_or_else (
765- || adt. all_fields ( ) . any ( |field| field. vis . is_public ( ) ) ,
766- |field| field. vis . is_public ( ) ,
767- ) ;
768740
769- if render_transparent {
770- out. push ( "transparent" ) ;
771- }
772- }
773- if repr. simd ( ) {
774- out. push ( "simd" ) ;
775- }
776- let pack_s;
777- if let Some ( pack) = repr. pack {
778- pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
779- out. push ( & pack_s) ;
780- }
781- let align_s;
782- if let Some ( align) = repr. align {
783- align_s = format ! ( "align({})" , align. bytes( ) ) ;
784- out. push ( & align_s) ;
785- }
786- let int_s;
787- if let Some ( int) = repr. int {
788- int_s = match int {
789- IntegerType :: Pointer ( is_signed) => {
790- format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
741+ if !keep_as_is && let Some ( repr) = self . repr ( tcx, cache) {
742+ attrs. push ( repr) ;
743+ }
744+
745+ attrs
746+ }
747+
748+ /// Compute the *public* `#[repr]` of this item.
749+ ///
750+ /// Read more about it here:
751+ /// https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type
752+ fn repr < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , cache : & Cache ) -> Option < String > {
753+ let def_id = self . def_id ( ) ?;
754+ let ( ItemType :: Struct | ItemType :: Enum | ItemType :: Union ) = self . type_ ( ) else {
755+ return None ;
756+ } ;
757+
758+ let adt = tcx. adt_def ( def_id) ;
759+ let repr = adt. repr ( ) ;
760+
761+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
762+ let is_field_public =
763+ |field : & ' tcx ty:: FieldDef | field. vis . is_public ( ) && is_visible ( field. did ) ;
764+
765+ if repr. transparent ( ) {
766+ // `repr(transparent)` is public iff the non-1-ZST field is public or
767+ // at least one field is public in case all fields are 1-ZST fields.
768+ let is_public = cache. document_private
769+ || adt. variants ( ) . iter ( ) . all ( |variant| {
770+ if !is_visible ( variant. def_id ) {
771+ return false ;
791772 }
792- IntegerType :: Fixed ( size, is_signed) => {
793- format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
773+
774+ let field = variant. fields . iter ( ) . find ( |field| {
775+ let args = ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ;
776+ let ty = field. ty ( tcx, args) ;
777+ tcx. layout_of ( tcx. param_env ( field. did ) . and ( ty) )
778+ . is_ok_and ( |layout| !layout. is_1zst ( ) )
779+ } ) ;
780+
781+ if let Some ( field) = field {
782+ return is_field_public ( field) ;
794783 }
795- } ;
796- out. push ( & int_s) ;
797- }
798- if !out. is_empty ( ) {
799- attrs. push ( format ! ( "#[repr({})]" , out. join( ", " ) ) ) ;
800- }
784+
785+ adt. variants ( ) . iter ( ) . all ( |variant| {
786+ variant. fields . is_empty ( ) || variant. fields . iter ( ) . any ( is_field_public)
787+ } )
788+ } ) ;
789+
790+ // Since `repr(transparent)` can't have any other reprs or
791+ // repr modifiers beside it, we can safely return early here.
792+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
801793 }
802- attrs
794+
795+ // Fast path which avoids looking through the variants and fields in
796+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
797+ if !repr. c ( )
798+ && !repr. simd ( )
799+ && repr. int . is_none ( )
800+ && repr. pack . is_none ( )
801+ && repr. align . is_none ( )
802+ {
803+ return None ;
804+ }
805+
806+ let is_public = cache. document_private
807+ || if adt. is_enum ( ) {
808+ // FIXME(fmease): Should we take the visibility of fields of variants into account?
809+ // FIXME(fmease): `any` or `all`?
810+ adt. variants ( ) . is_empty ( )
811+ || adt. variants ( ) . iter ( ) . any ( |variant| is_visible ( variant. def_id ) )
812+ } else {
813+ // FIXME(fmease): `all` or `any`?
814+ adt. all_fields ( ) . all ( is_field_public)
815+ } ;
816+ if !is_public {
817+ return None ;
818+ }
819+
820+ let mut result = Vec :: new ( ) ;
821+
822+ if repr. c ( ) {
823+ result. push ( "C" ) ;
824+ }
825+ if repr. simd ( ) {
826+ result. push ( "simd" ) ;
827+ }
828+ let int_s;
829+ if let Some ( int) = repr. int {
830+ int_s = match int {
831+ rustc_abi:: IntegerType :: Pointer ( is_signed) => {
832+ format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
833+ }
834+ rustc_abi:: IntegerType :: Fixed ( size, is_signed) => {
835+ format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
836+ }
837+ } ;
838+ result. push ( & int_s) ;
839+ }
840+ let pack_s;
841+ if let Some ( pack) = repr. pack {
842+ pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
843+ result. push ( & pack_s) ;
844+ }
845+ let align_s;
846+ if let Some ( align) = repr. align {
847+ align_s = format ! ( "align({})" , align. bytes( ) ) ;
848+ result. push ( & align_s) ;
849+ }
850+
851+ if result. is_empty ( ) {
852+ return None ;
853+ }
854+
855+ Some ( format ! ( "#[repr({})]" , result. join( ", " ) ) )
803856 }
804857
805858 pub fn is_doc_hidden ( & self ) -> bool {
0 commit comments