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