1+ use std:: borrow:: Cow ;
12use std:: hash:: Hash ;
23use std:: path:: PathBuf ;
34use std:: sync:: { Arc , OnceLock as OnceCell } ;
@@ -764,11 +765,12 @@ impl Item {
764765 Some ( tcx. visibility ( def_id) )
765766 }
766767
767- pub ( crate ) fn attributes_without_repr ( & self , tcx : TyCtxt < ' _ > , is_json : bool ) -> Vec < String > {
768+ pub ( crate ) fn attributes ( & self , tcx : TyCtxt < ' _ > , cache : & Cache , is_json : bool ) -> Vec < String > {
768769 const ALLOWED_ATTRIBUTES : & [ Symbol ] =
769770 & [ sym:: export_name, sym:: link_section, sym:: no_mangle, sym:: non_exhaustive] ;
770771
771- self . attrs
772+ let mut attrs: Vec < _ > = self
773+ . attrs
772774 . other_attrs
773775 . iter ( )
774776 . filter_map ( |attr| {
@@ -786,36 +788,24 @@ impl Item {
786788 } ) ,
787789 }
788790 } else if attr. has_any_name ( ALLOWED_ATTRIBUTES ) {
789- Some (
790- rustc_hir_pretty:: attribute_to_string ( & tcx, attr)
791- . replace ( "\\ \n " , "" )
792- . replace ( '\n' , "" )
793- . replace ( " " , " " ) ,
794- )
791+ let attr = rustc_hir_pretty:: attribute_to_string ( & tcx, attr)
792+ . replace ( "\\ \n " , "" )
793+ . replace ( '\n' , "" )
794+ . replace ( " " , " " ) ;
795+ Some ( attr)
795796 } else {
796797 None
797798 }
798799 } )
799- . collect ( )
800- }
800+ . collect ( ) ;
801801
802- pub ( crate ) fn attributes_and_repr (
803- & self ,
804- tcx : TyCtxt < ' _ > ,
805- cache : & Cache ,
806- is_json : bool ,
807- ) -> Vec < String > {
808- let mut attrs = self . attributes_without_repr ( tcx, is_json) ;
809-
810- if let Some ( repr_attr) = self . repr ( tcx, cache, is_json) {
811- attrs. push ( repr_attr) ;
802+ if let Some ( def_id) = self . def_id ( )
803+ && let Some ( attr) = repr_attribute ( tcx, cache, def_id, is_json)
804+ {
805+ attrs. push ( attr) ;
812806 }
813- attrs
814- }
815807
816- /// Returns a stringified `#[repr(...)]` attribute.
817- pub ( crate ) fn repr ( & self , tcx : TyCtxt < ' _ > , cache : & Cache , is_json : bool ) -> Option < String > {
818- repr_attributes ( tcx, cache, self . def_id ( ) ?, self . type_ ( ) , is_json)
808+ attrs
819809 }
820810
821811 pub fn is_doc_hidden ( & self ) -> bool {
@@ -827,71 +817,123 @@ impl Item {
827817 }
828818}
829819
830- pub ( crate ) fn repr_attributes (
831- tcx : TyCtxt < ' _ > ,
820+ /// Compute the *public* `#[repr]` of the item given by `DefId`.
821+ ///
822+ /// Read more about it here:
823+ /// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
824+ pub ( crate ) fn repr_attribute < ' tcx > (
825+ tcx : TyCtxt < ' tcx > ,
832826 cache : & Cache ,
833827 def_id : DefId ,
834- item_type : ItemType ,
835828 is_json : bool ,
836829) -> Option < String > {
837- use rustc_abi:: IntegerType ;
830+ let adt = match tcx. def_kind ( def_id) {
831+ DefKind :: Struct | DefKind :: Enum | DefKind :: Union => tcx. adt_def ( def_id) ,
832+ _ => return None ,
833+ } ;
834+ let repr = adt. repr ( ) ;
838835
839- if !matches ! ( item_type, ItemType :: Struct | ItemType :: Enum | ItemType :: Union ) {
836+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
837+ let is_public_field = |field : & ty:: FieldDef | {
838+ ( cache. document_private || field. vis . is_public ( ) ) && is_visible ( field. did )
839+ } ;
840+ let is_exhaustive = |def_id| !tcx. has_attr ( def_id, sym:: non_exhaustive) ;
841+
842+ if repr. transparent ( ) {
843+ // The transparent repr is public iff the non-1-ZST field is public and visible or
844+ // – in case all fields are 1-ZST fields — the type is exhaustive and at least
845+ // one field is public and visible.
846+ let is_public = ' is_public: {
847+ if is_json {
848+ break ' is_public true ;
849+ }
850+
851+ // `#[repr(transparent)]` can only be applied to structs and single-variant enums.
852+ let var = adt. variant ( rustc_abi:: FIRST_VARIANT ) ; // the first and only variant
853+
854+ // Therefore, if we have an enum, we don't care if it is `#[non_exhaustive]` or not
855+ // since the user isn't allowed to add more variants to it later anyway.
856+
857+ if !is_visible ( var. def_id ) {
858+ break ' is_public false ;
859+ }
860+
861+ // Side note: There can only ever be one or zero non-1-ZST fields.
862+ let non_1zst_field = var. fields . iter ( ) . find ( |field| {
863+ let ty = ty:: TypingEnv :: post_analysis ( tcx, field. did )
864+ . as_query_input ( tcx. type_of ( field. did ) . instantiate_identity ( ) ) ;
865+ tcx. layout_of ( ty) . is_ok_and ( |layout| !layout. is_1zst ( ) )
866+ } ) ;
867+
868+ match non_1zst_field {
869+ // We don't care if the containing variant is `#[non_exhaustive]` or not as the
870+ // user is only allowed to add more *1-ZST* fields which don't matter in the
871+ // presence of this non-1-ZST field.
872+ Some ( field) => is_public_field ( field) ,
873+ None => {
874+ is_exhaustive ( var. def_id )
875+ && ( var. fields . is_empty ( ) || var. fields . iter ( ) . any ( is_public_field) )
876+ }
877+ }
878+ } ;
879+
880+ // Since the transparent repr can't have any other reprs or
881+ // repr modifiers beside it, we can safely return early here.
882+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
883+ }
884+
885+ // Fast path which avoids looking through the variants and fields in
886+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
887+ // FIXME: This check is not very robust / forward compatible!
888+ if !repr. c ( )
889+ && !repr. simd ( )
890+ && repr. int . is_none ( )
891+ && repr. pack . is_none ( )
892+ && repr. align . is_none ( )
893+ {
840894 return None ;
841895 }
842- let adt = tcx. adt_def ( def_id) ;
843- let repr = adt. repr ( ) ;
844- let mut out = Vec :: new ( ) ;
845- if repr. c ( ) {
846- out. push ( "C" ) ;
896+
897+ // The repr is public iff all components are public, visible and exhaustive.
898+ let is_public = is_json
899+ || is_exhaustive ( def_id)
900+ && adt. variants ( ) . iter ( ) . all ( |variant| {
901+ is_exhaustive ( variant. def_id )
902+ && is_visible ( variant. def_id )
903+ && variant. fields . iter ( ) . all ( is_public_field)
904+ } ) ;
905+ if !is_public {
906+ return None ;
847907 }
848- if repr. transparent ( ) {
849- // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
850- // field is public in case all fields are 1-ZST fields.
851- let render_transparent = cache. document_private
852- || is_json
853- || adt
854- . all_fields ( )
855- . find ( |field| {
856- let ty = field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
857- tcx. layout_of ( ty:: TypingEnv :: post_analysis ( tcx, field. did ) . as_query_input ( ty) )
858- . is_ok_and ( |layout| !layout. is_1zst ( ) )
859- } )
860- . map_or_else (
861- || adt. all_fields ( ) . any ( |field| field. vis . is_public ( ) ) ,
862- |field| field. vis . is_public ( ) ,
863- ) ;
864908
865- if render_transparent {
866- out. push ( "transparent" ) ;
867- }
909+ let mut result = Vec :: < Cow < ' _ , _ > > :: new ( ) ;
910+
911+ if repr. c ( ) {
912+ result. push ( "C" . into ( ) ) ;
868913 }
869914 if repr. simd ( ) {
870- out. push ( "simd" ) ;
871- }
872- let pack_s;
873- if let Some ( pack) = repr. pack {
874- pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
875- out. push ( & pack_s) ;
915+ result. push ( "simd" . into ( ) ) ;
876916 }
877- let align_s;
878- if let Some ( align) = repr. align {
879- align_s = format ! ( "align({})" , align. bytes( ) ) ;
880- out. push ( & align_s) ;
881- }
882- let int_s;
883917 if let Some ( int) = repr. int {
884- int_s = match int {
885- IntegerType :: Pointer ( is_signed) => {
886- format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
887- }
888- IntegerType :: Fixed ( size, is_signed) => {
889- format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
918+ let prefix = if int. is_signed ( ) { 'i' } else { 'u' } ;
919+ let int = match int {
920+ rustc_abi:: IntegerType :: Pointer ( _) => format ! ( "{prefix}size" ) ,
921+ rustc_abi:: IntegerType :: Fixed ( int, _) => {
922+ format ! ( "{prefix}{}" , int. size( ) . bytes( ) * 8 )
890923 }
891924 } ;
892- out . push ( & int_s ) ;
925+ result . push ( int . into ( ) ) ;
893926 }
894- if !out. is_empty ( ) { Some ( format ! ( "#[repr({})]" , out. join( ", " ) ) ) } else { None }
927+
928+ // Render modifiers last.
929+ if let Some ( pack) = repr. pack {
930+ result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
931+ }
932+ if let Some ( align) = repr. align {
933+ result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
934+ }
935+
936+ ( !result. is_empty ( ) ) . then ( || format ! ( "#[repr({})]" , result. join( ", " ) ) )
895937}
896938
897939#[ derive( Clone , Debug ) ]
0 commit comments