1+ use std:: borrow:: Cow ;
12use std:: hash:: Hash ;
23use std:: path:: PathBuf ;
34use std:: sync:: { Arc , OnceLock as OnceCell } ;
@@ -765,9 +766,7 @@ impl Item {
765766 const ALLOWED_ATTRIBUTES : & [ Symbol ] =
766767 & [ sym:: export_name, sym:: link_section, sym:: no_mangle, sym:: non_exhaustive] ;
767768
768- use rustc_abi:: IntegerType ;
769-
770- let mut attrs: Vec < String > = self
769+ let mut attrs: Vec < _ > = self
771770 . attrs
772771 . other_attrs
773772 . iter ( )
@@ -798,23 +797,40 @@ impl Item {
798797 } )
799798 . collect ( ) ;
800799
801- // Add #[repr(...)]
802- if let Some ( def_id) = self . def_id ( )
803- && let ItemType :: Struct | ItemType :: Enum | ItemType :: Union = self . type_ ( )
804- {
805- let adt = tcx. adt_def ( def_id) ;
806- let repr = adt. repr ( ) ;
807- let mut out = Vec :: new ( ) ;
808- if repr. c ( ) {
809- out. push ( "C" ) ;
810- }
811- if repr. transparent ( ) {
812- // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
813- // field is public in case all fields are 1-ZST fields.
814- let render_transparent = is_json
815- || cache. document_private
816- || adt
817- . all_fields ( )
800+ if let Some ( repr) = self . repr ( tcx, cache, is_json) {
801+ attrs. push ( repr) ;
802+ }
803+
804+ attrs
805+ }
806+
807+ /// Compute the *public* `#[repr]` of this item.
808+ ///
809+ /// Read more about it here:
810+ /// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
811+ fn repr < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , cache : & Cache , is_json : bool ) -> Option < String > {
812+ let def_id = self . def_id ( ) ?;
813+ let ( ItemType :: Struct | ItemType :: Enum | ItemType :: Union ) = self . type_ ( ) else {
814+ return None ;
815+ } ;
816+ let adt = tcx. adt_def ( def_id) ;
817+ let repr = adt. repr ( ) ;
818+
819+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
820+ let is_field_public = |field : & ' tcx ty:: FieldDef | {
821+ ( cache. document_private || field. vis . is_public ( ) ) && is_visible ( field. did )
822+ } ;
823+
824+ if repr. transparent ( ) {
825+ // `repr(transparent)` can only be applied to structs and single-variant enums.
826+ let var = adt. variant ( rustc_abi:: FIRST_VARIANT ) ;
827+ // `repr(transparent)` is public iff the non-1-ZST field is public or
828+ // at least one field is public in case all fields are 1-ZST fields.
829+ let is_public = is_json
830+ || is_visible ( var. def_id )
831+ && var
832+ . fields
833+ . iter ( )
818834 . find ( |field| {
819835 let ty =
820836 field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
@@ -824,44 +840,63 @@ impl Item {
824840 . is_ok_and ( |layout| !layout. is_1zst ( ) )
825841 } )
826842 . map_or_else (
827- || adt . all_fields ( ) . any ( |field| field . vis . is_public ( ) ) ,
828- |field| field . vis . is_public ( ) ,
843+ || var . fields . is_empty ( ) || var . fields . iter ( ) . any ( is_field_public ) ,
844+ is_field_public ,
829845 ) ;
830846
831- if render_transparent {
832- out. push ( "transparent" ) ;
847+ // Since `repr(transparent)` can't have any other reprs or
848+ // repr modifiers beside it, we can safely return early here.
849+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
850+ }
851+
852+ // Fast path which avoids looking through the variants and fields in
853+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
854+ // FIXME: This check is not forward compatible!
855+ if !repr. c ( )
856+ && !repr. simd ( )
857+ && repr. int . is_none ( )
858+ && repr. pack . is_none ( )
859+ && repr. align . is_none ( )
860+ {
861+ return None ;
862+ }
863+
864+ let is_public = is_json
865+ || adt. variants ( ) . iter ( ) . all ( |variant| {
866+ is_visible ( variant. def_id ) && variant. fields . iter ( ) . all ( is_field_public)
867+ } ) ;
868+ if !is_public {
869+ return None ;
870+ }
871+
872+ let mut result = Vec :: < Cow < ' _ , _ > > :: new ( ) ;
873+
874+ if repr. c ( ) {
875+ result. push ( "C" . into ( ) ) ;
876+ }
877+ if repr. simd ( ) {
878+ result. push ( "simd" . into ( ) ) ;
879+ }
880+ if let Some ( int) = repr. int {
881+ let prefix = if int. is_signed ( ) { 'i' } else { 'u' } ;
882+ let int = match int {
883+ rustc_abi:: IntegerType :: Pointer ( _) => format ! ( "{prefix}size" ) ,
884+ rustc_abi:: IntegerType :: Fixed ( int, _) => {
885+ format ! ( "{prefix}{}" , int. size( ) . bytes( ) * 8 )
833886 }
834- }
835- if repr. simd ( ) {
836- out. push ( "simd" ) ;
837- }
838- let pack_s;
839- if let Some ( pack) = repr. pack {
840- pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
841- out. push ( & pack_s) ;
842- }
843- let align_s;
844- if let Some ( align) = repr. align {
845- align_s = format ! ( "align({})" , align. bytes( ) ) ;
846- out. push ( & align_s) ;
847- }
848- let int_s;
849- if let Some ( int) = repr. int {
850- int_s = match int {
851- IntegerType :: Pointer ( is_signed) => {
852- format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
853- }
854- IntegerType :: Fixed ( size, is_signed) => {
855- format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
856- }
857- } ;
858- out. push ( & int_s) ;
859- }
860- if !out. is_empty ( ) {
861- attrs. push ( format ! ( "#[repr({})]" , out. join( ", " ) ) ) ;
862- }
887+ } ;
888+ result. push ( int. into ( ) ) ;
863889 }
864- attrs
890+
891+ // Render modifiers last.
892+ if let Some ( pack) = repr. pack {
893+ result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
894+ }
895+ if let Some ( align) = repr. align {
896+ result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
897+ }
898+
899+ ( !result. is_empty ( ) ) . then ( || format ! ( "#[repr({})]" , result. join( ", " ) ) )
865900 }
866901
867902 pub fn is_doc_hidden ( & self ) -> bool {
0 commit comments