|
| 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,103 @@ 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 | + let adt = tcx.adt_def(def_id); |
| 824 | + let repr = adt.repr(); |
| 825 | + |
| 826 | + let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id); |
| 827 | + let is_field_public = |field: &'tcx ty::FieldDef| { |
| 828 | + (cache.document_private || field.vis.is_public()) && is_visible(field.did) |
| 829 | + }; |
| 830 | + |
| 831 | + if repr.transparent() { |
| 832 | + // `repr(transparent)` can only be applied to structs and one-variant enums. |
| 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(is_field_public), |
| 849 | + is_field_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 | + // FIXME: This check is not forward compatible! |
| 860 | + if !repr.c() |
| 861 | + && !repr.simd() |
| 862 | + && repr.int.is_none() |
| 863 | + && repr.pack.is_none() |
| 864 | + && repr.align.is_none() |
811 | 865 | { |
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 | | - ); |
| 866 | + return None; |
| 867 | + } |
836 | 868 |
|
837 | | - if render_transparent { |
838 | | - out.push("transparent"); |
| 869 | + let is_public = adt.variants().iter().all(|variant| { |
| 870 | + is_visible(variant.def_id) && variant.fields.iter().all(is_field_public) |
| 871 | + }); |
| 872 | + if !is_public { |
| 873 | + return None; |
| 874 | + } |
| 875 | + |
| 876 | + let mut result = Vec::<Cow<'_, _>>::new(); |
| 877 | + |
| 878 | + if repr.c() { |
| 879 | + result.push("C".into()); |
| 880 | + } |
| 881 | + if repr.simd() { |
| 882 | + result.push("simd".into()); |
| 883 | + } |
| 884 | + if let Some(int) = repr.int { |
| 885 | + let prefix = if int.is_signed() { 'i' } else { 'u' }; |
| 886 | + let int = match int { |
| 887 | + rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"), |
| 888 | + rustc_abi::IntegerType::Fixed(int, _) => { |
| 889 | + format!("{prefix}{}", int.size().bytes() * 8) |
839 | 890 | } |
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 | | - } |
| 891 | + }; |
| 892 | + result.push(int.into()); |
869 | 893 | } |
870 | | - attrs |
| 894 | + |
| 895 | + // Render modifiers last. |
| 896 | + if let Some(pack) = repr.pack { |
| 897 | + result.push(format!("packed({})", pack.bytes()).into()); |
| 898 | + } |
| 899 | + if let Some(align) = repr.align { |
| 900 | + result.push(format!("align({})", align.bytes()).into()); |
| 901 | + } |
| 902 | + |
| 903 | + (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", "))) |
871 | 904 | } |
872 | 905 |
|
873 | 906 | pub fn is_doc_hidden(&self) -> bool { |
|
0 commit comments