11use std:: iter;
22use std:: ops:: ControlFlow ;
33
4- use rustc_abi:: { BackendRepr , ExternAbi , TagEncoding , Variants , WrappingRange } ;
4+ use rustc_abi:: { BackendRepr , ExternAbi , TagEncoding , VariantIdx , Variants , WrappingRange } ;
55use rustc_data_structures:: fx:: FxHashSet ;
66use rustc_errors:: DiagMessage ;
77use rustc_hir:: { Expr , ExprKind , LangItem } ;
88use rustc_middle:: bug;
99use rustc_middle:: ty:: layout:: { LayoutOf , SizeSkeleton } ;
1010use rustc_middle:: ty:: {
11- self , AdtKind , GenericArgsRef , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable , TypeVisitableExt ,
11+ self , Adt , AdtKind , GenericArgsRef , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable ,
12+ TypeVisitableExt ,
1213} ;
1314use rustc_session:: { declare_lint, declare_lint_pass, impl_lint_pass} ;
1415use rustc_span:: def_id:: LocalDefId ;
@@ -23,7 +24,7 @@ use crate::lints::{
2324 AmbiguousWidePointerComparisonsAddrSuggestion , AtomicOrderingFence , AtomicOrderingLoad ,
2425 AtomicOrderingStore , ImproperCTypes , InvalidAtomicOrderingDiag , InvalidNanComparisons ,
2526 InvalidNanComparisonsSuggestion , UnpredictableFunctionPointerComparisons ,
26- UnpredictableFunctionPointerComparisonsSuggestion , UnusedComparisons ,
27+ UnpredictableFunctionPointerComparisonsSuggestion , UnusedComparisons , UsesPowerAlignment ,
2728 VariantSizeDifferencesDiag ,
2829} ;
2930use crate :: { LateContext , LateLintPass , LintContext , fluent_generated as fluent} ;
@@ -727,7 +728,60 @@ declare_lint! {
727728 "proper use of libc types in foreign item definitions"
728729}
729730
730- declare_lint_pass ! ( ImproperCTypesDefinitions => [ IMPROPER_CTYPES_DEFINITIONS ] ) ;
731+ declare_lint ! {
732+ /// The `uses_power_alignment` lint detects specific `repr(C)`
733+ /// aggregates on AIX.
734+ /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment
735+ /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment),
736+ /// which can also be set for XLC by `#pragma align(power)` or
737+ /// `-qalign=power`. Aggregates with a floating-point type as the
738+ /// recursively first field (as in "at offset 0") modify the layout of
739+ /// *subsequent* fields of the associated structs to use an alignment value
740+ /// where the floating-point type is aligned on a 4-byte boundary.
741+ ///
742+ /// The power alignment rule for structs needed for C compatibility is
743+ /// unimplementable within `repr(C)` in the compiler without building in
744+ /// handling of references to packed fields and infectious nested layouts,
745+ /// so a warning is produced in these situations.
746+ ///
747+ /// ### Example
748+ ///
749+ /// ```rust,ignore
750+ /// #[repr(C)]
751+ /// pub struct Floats {
752+ /// a: f64,
753+ /// b: u8,
754+ /// c: f64,
755+ /// }
756+ /// ```
757+ ///
758+ /// This will produce:
759+ ///
760+ /// ```text
761+ /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type
762+ /// --> <source>:5:3
763+ /// |
764+ /// 5 | c: f64,
765+ /// | ^^^^^^
766+ /// |
767+ /// = note: `#[warn(uses_power_alignment)]` on by default
768+ /// ```
769+ ///
770+ /// ### Explanation
771+ ///
772+ /// The power alignment rule specifies that the above struct has the
773+ /// following alignment:
774+ /// - offset_of!(Floats, a) == 0
775+ /// - offset_of!(Floats, b) == 8
776+ /// - offset_of!(Floats, c) == 12
777+ /// However, rust currently aligns `c` at offset_of!(Floats, c) == 16.
778+ /// Thus, a warning should be produced for the above struct in this case.
779+ USES_POWER_ALIGNMENT ,
780+ Warn ,
781+ "Structs do not follow the power alignment rule under repr(C)"
782+ }
783+
784+ declare_lint_pass ! ( ImproperCTypesDefinitions => [ IMPROPER_CTYPES_DEFINITIONS , USES_POWER_ALIGNMENT ] ) ;
731785
732786#[ derive( Clone , Copy ) ]
733787pub ( crate ) enum CItemKind {
@@ -1539,6 +1593,71 @@ impl ImproperCTypesDefinitions {
15391593 vis. check_type_for_ffi_and_report_errors ( span, fn_ptr_ty, true , false ) ;
15401594 }
15411595 }
1596+
1597+ fn check_arg_for_power_alignment < ' tcx > (
1598+ & mut self ,
1599+ cx : & LateContext < ' tcx > ,
1600+ ty : Ty < ' tcx > ,
1601+ ) -> bool {
1602+ // Structs (under repr(C)) follow the power alignment rule if:
1603+ // - the first field of the struct is a floating-point type that
1604+ // is greater than 4-bytes, or
1605+ // - the first field of the struct is an aggregate whose
1606+ // recursively first field is a floating-point type greater than
1607+ // 4 bytes.
1608+ if cx. tcx . sess . target . os != "aix" {
1609+ return false ;
1610+ }
1611+ if ty. is_floating_point ( ) && ty. primitive_size ( cx. tcx ) . bytes ( ) > 4 {
1612+ return true ;
1613+ } else if let Adt ( adt_def, _) = ty. kind ( )
1614+ && adt_def. is_struct ( )
1615+ {
1616+ let struct_variant = adt_def. variant ( VariantIdx :: ZERO ) ;
1617+ // Within a nested struct, all fields are examined to correctly
1618+ // report if any fields after the nested struct within the
1619+ // original struct are misaligned.
1620+ for struct_field in & struct_variant. fields {
1621+ let field_ty = cx. tcx . type_of ( struct_field. did ) . instantiate_identity ( ) ;
1622+ if self . check_arg_for_power_alignment ( cx, field_ty) {
1623+ return true ;
1624+ }
1625+ }
1626+ }
1627+ return false ;
1628+ }
1629+
1630+ fn check_struct_for_power_alignment < ' tcx > (
1631+ & mut self ,
1632+ cx : & LateContext < ' tcx > ,
1633+ item : & ' tcx hir:: Item < ' tcx > ,
1634+ ) {
1635+ let adt_def = cx. tcx . adt_def ( item. owner_id . to_def_id ( ) ) ;
1636+ if adt_def. repr ( ) . c ( )
1637+ && !adt_def. repr ( ) . packed ( )
1638+ && cx. tcx . sess . target . os == "aix"
1639+ && !adt_def. all_fields ( ) . next ( ) . is_none ( )
1640+ {
1641+ let struct_variant_data = item. expect_struct ( ) . 0 ;
1642+ for ( index, ..) in struct_variant_data. fields ( ) . iter ( ) . enumerate ( ) {
1643+ // Struct fields (after the first field) are checked for the
1644+ // power alignment rule, as fields after the first are likely
1645+ // to be the fields that are misaligned.
1646+ if index != 0 {
1647+ let first_field_def = struct_variant_data. fields ( ) [ index] ;
1648+ let def_id = first_field_def. def_id ;
1649+ let ty = cx. tcx . type_of ( def_id) . instantiate_identity ( ) ;
1650+ if self . check_arg_for_power_alignment ( cx, ty) {
1651+ cx. emit_span_lint (
1652+ USES_POWER_ALIGNMENT ,
1653+ first_field_def. span ,
1654+ UsesPowerAlignment ,
1655+ ) ;
1656+ }
1657+ }
1658+ }
1659+ }
1660+ }
15421661}
15431662
15441663/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
@@ -1562,8 +1681,13 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
15621681 }
15631682 // See `check_fn`..
15641683 hir:: ItemKind :: Fn { .. } => { }
1684+ // Structs are checked based on if they follow the power alignment
1685+ // rule (under repr(C)).
1686+ hir:: ItemKind :: Struct ( ..) => {
1687+ self . check_struct_for_power_alignment ( cx, item) ;
1688+ }
15651689 // See `check_field_def`..
1566- hir:: ItemKind :: Union ( ..) | hir:: ItemKind :: Struct ( .. ) | hir :: ItemKind :: Enum ( ..) => { }
1690+ hir:: ItemKind :: Union ( ..) | hir:: ItemKind :: Enum ( ..) => { }
15671691 // Doesn't define something that can contain a external type to be checked.
15681692 hir:: ItemKind :: Impl ( ..)
15691693 | hir:: ItemKind :: TraitAlias ( ..)
0 commit comments