@@ -2964,6 +2964,10 @@ fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) {
29642964 write ! ( w, "<div class=\" code-attribute\" >{prefix}{attr}</div>" ) . unwrap ( ) ;
29652965}
29662966
2967+ /// Compute the *public* `#[repr]` of the item given by `DefId`.
2968+ ///
2969+ /// Read more about it here:
2970+ /// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
29672971fn repr_attribute < ' tcx > (
29682972 tcx : TyCtxt < ' tcx > ,
29692973 cache : & Cache ,
@@ -2975,25 +2979,59 @@ fn repr_attribute<'tcx>(
29752979 } ;
29762980 let repr = adt. repr ( ) ;
29772981
2982+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
2983+ let is_public_field = |field : & ty:: FieldDef | {
2984+ ( cache. document_private || field. vis . is_public ( ) ) && is_visible ( field. did )
2985+ } ;
2986+
29782987 if repr. transparent ( ) {
2979- // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
2980- // field is public in case all fields are 1-ZST fields.
2981- let render_transparent = cache. document_private
2982- || adt
2983- . all_fields ( )
2984- . find ( |field| {
2985- let ty = field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
2986- tcx. layout_of ( ty:: TypingEnv :: post_analysis ( tcx, field. did ) . as_query_input ( ty) )
2987- . is_ok_and ( |layout| !layout. is_1zst ( ) )
2988- } )
2989- . map_or_else (
2990- || adt. all_fields ( ) . any ( |field| field. vis . is_public ( ) ) ,
2991- |field| field. vis . is_public ( ) ,
2992- ) ;
2988+ // The transparent repr is public iff the non-1-ZST field is public and visible or
2989+ // – in case all fields are 1-ZST fields — at least one field is public and visible.
2990+ let is_public = ' is_public: {
2991+ // `#[repr(transparent)]` can only be applied to structs and single-variant enums.
2992+ let var = adt. variant ( rustc_abi:: FIRST_VARIANT ) ; // the first and only variant
2993+
2994+ if !is_visible ( var. def_id ) {
2995+ break ' is_public false ;
2996+ }
2997+
2998+ // Side note: There can only ever be one or zero non-1-ZST fields.
2999+ let non_1zst_field = var. fields . iter ( ) . find ( |field| {
3000+ let ty = ty:: TypingEnv :: post_analysis ( tcx, field. did )
3001+ . as_query_input ( tcx. type_of ( field. did ) . instantiate_identity ( ) ) ;
3002+ tcx. layout_of ( ty) . is_ok_and ( |layout| !layout. is_1zst ( ) )
3003+ } ) ;
3004+
3005+ match non_1zst_field {
3006+ Some ( field) => is_public_field ( field) ,
3007+ None => var. fields . is_empty ( ) || var. fields . iter ( ) . any ( is_public_field) ,
3008+ }
3009+ } ;
29933010
29943011 // Since the transparent repr can't have any other reprs or
29953012 // repr modifiers beside it, we can safely return early here.
2996- return render_transparent. then ( || "#[repr(transparent)]" . into ( ) ) ;
3013+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
3014+ }
3015+
3016+ // Fast path which avoids looking through the variants and fields in
3017+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
3018+ // FIXME: This check is not very robust / forward compatible!
3019+ if !repr. c ( )
3020+ && !repr. simd ( )
3021+ && repr. int . is_none ( )
3022+ && repr. pack . is_none ( )
3023+ && repr. align . is_none ( )
3024+ {
3025+ return None ;
3026+ }
3027+
3028+ // The repr is public iff all components are public and visible.
3029+ let is_public = adt
3030+ . variants ( )
3031+ . iter ( )
3032+ . all ( |variant| is_visible ( variant. def_id ) && variant. fields . iter ( ) . all ( is_public_field) ) ;
3033+ if !is_public {
3034+ return None ;
29973035 }
29983036
29993037 let mut result = Vec :: < Cow < ' _ , _ > > :: new ( ) ;
@@ -3004,12 +3042,6 @@ fn repr_attribute<'tcx>(
30043042 if repr. simd ( ) {
30053043 result. push ( "simd" . into ( ) ) ;
30063044 }
3007- if let Some ( pack) = repr. pack {
3008- result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
3009- }
3010- if let Some ( align) = repr. align {
3011- result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
3012- }
30133045 if let Some ( int) = repr. int {
30143046 let prefix = if int. is_signed ( ) { 'i' } else { 'u' } ;
30153047 let int = match int {
@@ -3021,5 +3053,13 @@ fn repr_attribute<'tcx>(
30213053 result. push ( int. into ( ) ) ;
30223054 }
30233055
3056+ // Render modifiers last.
3057+ if let Some ( pack) = repr. pack {
3058+ result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
3059+ }
3060+ if let Some ( align) = repr. align {
3061+ result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
3062+ }
3063+
30243064 ( !result. is_empty ( ) ) . then ( || format ! ( "#[repr({})]" , result. join( ", " ) ) . into ( ) )
30253065}
0 commit comments