Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -66,6 +66,11 @@ For example, the ``clang`` binary will be called ``clang-7`` instead of ``clang-7.0``. +- Clang implements a collection of recent fixes to the C++ standard's definition + of "standard-layout". In particular, a class is only considered to be + standard-layout if all base classes and the first data member (or bit-field) + can be laid out at offset zero. + - ... New Compiler Flags Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -348,7 +348,12 @@ /// one pure virtual function, (that can come from a base class). unsigned Abstract : 1; - /// \brief True when this class has standard layout. + /// \brief True when this class is standard-layout, per the applicable + /// language rules (including DRs). + unsigned IsStandardLayout : 1; + + /// \brief True when this class was standard-layout under the C++11 + /// definition. /// /// C++11 [class]p7. A standard-layout class is a class that: /// * has no non-static data members of type non-standard-layout class (or @@ -362,13 +367,19 @@ /// classes with non-static data members, and /// * has no base classes of the same type as the first non-static data /// member. - unsigned IsStandardLayout : 1; + unsigned IsCXX11StandardLayout : 1; - /// \brief True when there are no non-empty base classes. - /// + /// \brief True when any base class has any declared non-static data + /// members or bit-fields. /// This is a helper bit of state used to implement IsStandardLayout more /// efficiently. - unsigned HasNoNonEmptyBases : 1; + unsigned HasBasesWithFields : 1; + + /// \brief True when any base class has any declared non-static data + /// members. + /// This is a helper bit of state used to implement IsCXX11StandardLayout + /// more efficiently. + unsigned HasBasesWithNonStaticDataMembers : 1; /// \brief True when there are private non-static data members. unsigned HasPrivateFields : 1; @@ -696,6 +707,12 @@ /// deserializing the friends from an external AST source. FriendDecl *getFirstFriend() const; + /// Determine whether this class has an empty base class subobject of type X + /// or of one of the types that might be at offset 0 within X (per the C++ + /// "standard layout" rules). + bool hasSubobjectAtOffsetZeroOfEmptyBaseType(ASTContext &Ctx, + const CXXRecordDecl *X); + protected: CXXRecordDecl(Kind K, TagKind TK, const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, @@ -1301,10 +1318,14 @@ /// not overridden. bool isAbstract() const { return data().Abstract; } - /// \brief Determine whether this class has standard layout per - /// (C++ [class]p7) + /// \brief Determine whether this class is standard-layout per + /// C++ [class]p7. bool isStandardLayout() const { return data().IsStandardLayout; } + /// \brief Determine whether this class was standard-layout per + /// C++11 [class]p7, specifically using the C++11 rules without any DRs. + bool isCXX11StandardLayout() const { return data().IsCXX11StandardLayout; } + /// \brief Determine whether this class, or any of its class subobjects, /// contains a mutable field. bool hasMutableFields() const { return data().HasMutableFields; } Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -798,7 +798,8 @@ /// Return true if this is a POD type according to the more relaxed rules /// of the C++11 standard, regardless of the current compilation's language. - /// (C++0x [basic.types]p9) + /// (C++0x [basic.types]p9). Note that, unlike + /// CXXRecordDecl::isCXX11StandardLayout, this takes DRs into account. bool isCXX11PODType(const ASTContext &Context) const; /// Return true if this is a trivial type per (C++0x [basic.types]p9) Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -1071,7 +1071,10 @@ ToData.Polymorphic = FromData.Polymorphic; ToData.Abstract = FromData.Abstract; ToData.IsStandardLayout = FromData.IsStandardLayout; - ToData.HasNoNonEmptyBases = FromData.HasNoNonEmptyBases; + ToData.IsCXX11StandardLayout = FromData.IsCXX11StandardLayout; + ToData.HasBasesWithFields = FromData.HasBasesWithFields; + ToData.HasBasesWithNonStaticDataMembers = + FromData.HasBasesWithNonStaticDataMembers; ToData.HasPrivateFields = FromData.HasPrivateFields; ToData.HasProtectedFields = FromData.HasProtectedFields; ToData.HasPublicFields = FromData.HasPublicFields; Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -74,7 +74,8 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) : UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0), Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false), - Abstract(false), IsStandardLayout(true), HasNoNonEmptyBases(true), + Abstract(false), IsStandardLayout(true), IsCXX11StandardLayout(true), + HasBasesWithFields(false), HasBasesWithNonStaticDataMembers(false), HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false), HasOnlyCMembers(true), HasInClassInitializer(false), @@ -161,6 +162,25 @@ return R; } +/// Determine whether a class has a repeated base class. This is intended for +/// use when determining if a class is standard-layout, so makes no attempt to +/// handle virtual bases. +static bool hasRepeatedBaseClass(const CXXRecordDecl *StartRD) { + llvm::SmallPtrSet SeenBaseTypes; + SmallVector WorkList = {StartRD}; + while (!WorkList.empty()) { + const CXXRecordDecl *RD = WorkList.pop_back_val(); + for (const CXXBaseSpecifier &BaseSpec : RD->bases()) { + if (const CXXRecordDecl *B = BaseSpec.getType()->getAsCXXRecordDecl()) { + if (!SeenBaseTypes.insert(B).second) + return true; + WorkList.push_back(B); + } + } + } + return false; +} + void CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases) { @@ -200,26 +220,37 @@ auto *BaseClassDecl = cast(BaseType->getAs()->getDecl()); - if (!BaseClassDecl->isEmpty()) { - if (!data().Empty) { - // C++0x [class]p7: - // A standard-layout class is a class that: - // [...] - // -- either has no non-static data members in the most derived - // class and at most one base class with non-static data members, - // or has no base classes with non-static data members, and - // If this is the second non-empty base, then neither of these two - // clauses can be true. + // C++2a [class]p7: + // A standard-layout class is a class that: + // [...] + // -- has all non-static data members and bit-fields in the class and + // its base classes first declared in the same class + if (BaseClassDecl->data().HasBasesWithFields || + !BaseClassDecl->field_empty()) { + if (data().HasBasesWithFields) + // Two bases have members or bit-fields: not standard-layout. data().IsStandardLayout = false; - } + data().HasBasesWithFields = true; + } + + // C++11 [class]p7: + // A standard-layout class is a class that: + // -- [...] has [...] at most one base class with non-static data + // members + if (BaseClassDecl->data().HasBasesWithNonStaticDataMembers || + BaseClassDecl->hasDirectFields()) { + if (data().HasBasesWithNonStaticDataMembers) + data().IsCXX11StandardLayout = false; + data().HasBasesWithNonStaticDataMembers = true; + } + if (!BaseClassDecl->isEmpty()) { // C++14 [meta.unary.prop]p4: // T is a class type [...] with [...] no base class B for which // is_empty::value is false. data().Empty = false; - data().HasNoNonEmptyBases = false; } - + // C++1z [dcl.init.agg]p1: // An aggregate is a class with [...] no private or protected base classes if (Base->getAccessSpecifier() != AS_public) @@ -236,6 +267,8 @@ // -- has no non-standard-layout base classes if (!BaseClassDecl->isStandardLayout()) data().IsStandardLayout = false; + if (!BaseClassDecl->isCXX11StandardLayout()) + data().IsCXX11StandardLayout = false; // Record if this base is the first non-literal field or base. if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C)) @@ -287,6 +320,7 @@ // A standard-layout class is a class that: [...] // -- has [...] no virtual base classes data().IsStandardLayout = false; + data().IsCXX11StandardLayout = false; // C++11 [dcl.constexpr]p4: // In the definition of a constexpr constructor [...] @@ -401,6 +435,16 @@ addedClassSubobject(BaseClassDecl); } + + // C++2a [class]p7: + // A class S is a standard-layout class if it: + // -- has at most one base class subobject of any given type + // + // Note that we only need to check this for classes with more than one base + // class. If there's only one base class, and it's standard layout, then + // we know there are no repeated base classes. + if (data().IsStandardLayout && NumBases > 1 && hasRepeatedBaseClass(this)) + data().IsStandardLayout = false; if (VBases.empty()) { data().IsParsingBaseSpecifiers = false; @@ -501,6 +545,81 @@ data().Abstract = true; } +bool CXXRecordDecl::hasSubobjectAtOffsetZeroOfEmptyBaseType( + ASTContext &Ctx, const CXXRecordDecl *XFirst) { + if (!getNumBases()) + return false; + + llvm::SmallPtrSet Bases; + llvm::SmallPtrSet M; + SmallVector WorkList; + + // Visit a type that we have determined is an element of M(S). + auto Visit = [&](const CXXRecordDecl *RD) -> bool { + RD = RD->getCanonicalDecl(); + + // C++2a [class]p8: + // A class S is a standard-layout class if it [...] has no element of the + // set M(S) of types as a base class. + // + // If we find a subobject of an empty type, it might also be a base class, + // so we'll need to walk the base classes to check. + if (!RD->data().HasBasesWithFields) { + // Walk the bases the first time, stopping if we find the type. Build a + // set of them so we don't need to walk them again. + if (Bases.empty()) { + bool RDIsBase = !forallBases([&](const CXXRecordDecl *Base) -> bool { + Base = Base->getCanonicalDecl(); + if (RD == Base) + return false; + Bases.insert(Base); + return true; + }); + if (RDIsBase) + return true; + } else { + if (Bases.count(RD)) + return true; + } + } + + if (M.insert(RD).second) + WorkList.push_back(RD); + return false; + }; + + if (Visit(XFirst)) + return true; + + while (!WorkList.empty()) { + const CXXRecordDecl *X = WorkList.pop_back_val(); + + // FIXME: We don't check the bases of X. That matches the standard, but + // that sure looks like a wording bug. + + // -- If X is a non-union class type with a non-static data member + // [recurse to] the first non-static data member of X + // -- If X is a union type, [recurse to union members] + for (auto *FD : X->fields()) { + // FIXME: Should we really care about the type of the first non-static + // data member of a non-union if there are preceding unnamed bit-fields? + if (FD->isUnnamedBitfield()) + continue; + + // -- If X is n array type, [visit the element type] + QualType T = Ctx.getBaseElementType(FD->getType()); + if (auto *RD = T->getAsCXXRecordDecl()) + if (Visit(RD)) + return true; + + if (!X->isUnion()) + break; + } + } + + return false; +} + void CXXRecordDecl::addedMember(Decl *D) { if (!D->isImplicit() && !isa(D) && @@ -555,6 +674,7 @@ // A standard-layout class is a class that: [...] // -- has no virtual functions data().IsStandardLayout = false; + data().IsCXX11StandardLayout = false; } } @@ -732,8 +852,18 @@ return; } + ASTContext &Context = getASTContext(); + // Handle non-static data members. if (const auto *Field = dyn_cast(D)) { + // C++2a [class]p7: + // A standard-layout class is a class that: + // [...] + // -- has all non-static data members and bit-fields in the class and + // its base classes first declared in the same class + if (data().HasBasesWithFields) + data().IsStandardLayout = false; + // C++ [class.bit]p2: // A declaration for a bit-field that omits the identifier declares an // unnamed bit-field. Unnamed bit-fields are not members and cannot be @@ -741,6 +871,13 @@ if (Field->isUnnamedBitfield()) return; + // C++11 [class]p7: + // A standard-layout class is a class that: + // -- either has no non-static data members in the most derived class + // [...] or has no base classes with non-static data members + if (data().HasBasesWithNonStaticDataMembers) + data().IsCXX11StandardLayout = false; + // C++ [dcl.init.aggr]p1: // An aggregate is an array or a class (clause 9) with [...] no // private or protected non-static data members (clause 11). @@ -751,6 +888,11 @@ data().PlainOldData = false; } + // Track whether this is the first field. We use this when checking + // whether the class is standard-layout below. + bool IsFirstField = !data().HasPrivateFields && + !data().HasProtectedFields && !data().HasPublicFields; + // C++0x [class]p7: // A standard-layout class is a class that: // [...] @@ -762,8 +904,10 @@ case AS_none: llvm_unreachable("Invalid access specifier"); }; if ((data().HasPrivateFields + data().HasProtectedFields + - data().HasPublicFields) > 1) + data().HasPublicFields) > 1) { data().IsStandardLayout = false; + data().IsCXX11StandardLayout = false; + } // Keep track of the presence of mutable fields. if (Field->isMutable()) { @@ -784,7 +928,6 @@ // // Automatic Reference Counting: the presence of a member of Objective-C pointer type // that does not explicitly have no lifetime makes the class a non-POD. - ASTContext &Context = getASTContext(); QualType T = Context.getBaseElementType(Field->getType()); if (T->isObjCRetainableType() || T.isObjCGCStrong()) { if (T.hasNonTrivialObjCLifetime()) { @@ -824,6 +967,7 @@ // A standard-layout class is a class that: // -- has no non-static data members of type [...] reference, data().IsStandardLayout = false; + data().IsCXX11StandardLayout = false; // C++1z [class.copy.ctor]p10: // A defaulted copy constructor for a class X is defined as deleted if X has: @@ -980,31 +1124,32 @@ // class (or array of such types) [...] if (!FieldRec->isStandardLayout()) data().IsStandardLayout = false; + if (!FieldRec->isCXX11StandardLayout()) + data().IsCXX11StandardLayout = false; - // C++0x [class]p7: + // C++2a [class]p7: // A standard-layout class is a class that: // [...] + // -- has no element of the set M(S) of types as a base class. + if (data().IsStandardLayout && (isUnion() || IsFirstField) && + hasSubobjectAtOffsetZeroOfEmptyBaseType(Context, FieldRec)) + data().IsStandardLayout = false; + + // C++11 [class]p7: + // A standard-layout class is a class that: // -- has no base classes of the same type as the first non-static - // data member. - // We don't want to expend bits in the state of the record decl - // tracking whether this is the first non-static data member so we - // cheat a bit and use some of the existing state: the empty bit. - // Virtual bases and virtual methods make a class non-empty, but they - // also make it non-standard-layout so we needn't check here. - // A non-empty base class may leave the class standard-layout, but not - // if we have arrived here, and have at least one non-static data - // member. If IsStandardLayout remains true, then the first non-static - // data member must come through here with Empty still true, and Empty - // will subsequently be set to false below. - if (data().IsStandardLayout && data().Empty) { + // data member + if (data().IsCXX11StandardLayout && IsFirstField) { + // FIXME: We should check all base classes here, not just direct + // base classes. for (const auto &BI : bases()) { if (Context.hasSameUnqualifiedType(BI.getType(), T)) { - data().IsStandardLayout = false; + data().IsCXX11StandardLayout = false; break; } } } - + // Keep track of the presence of mutable fields. if (FieldRec->hasMutableFields()) { data().HasMutableFields = true; @@ -1067,17 +1212,6 @@ data().DefaultedMoveAssignmentIsDeleted = true; } - // C++0x [class]p7: - // A standard-layout class is a class that: - // [...] - // -- either has no non-static data members in the most derived - // class and at most one base class with non-static data members, - // or has no base classes with non-static data members, and - // At this point we know that we have a non-static data member, so the last - // clause holds. - if (!data().HasNoNonEmptyBases) - data().IsStandardLayout = false; - // C++14 [meta.unary.prop]p4: // T is a class type [...] with [...] no non-static data members other // than bit-fields of length 0... Index: lib/AST/RecordLayoutBuilder.cpp =================================================================== --- lib/AST/RecordLayoutBuilder.cpp +++ lib/AST/RecordLayoutBuilder.cpp @@ -2134,7 +2134,7 @@ // mode; fortunately, that is true because we want to assign // consistently semantics to the type-traits intrinsics (or at // least as many of them as possible). - return RD->isTrivial() && RD->isStandardLayout(); + return RD->isTrivial() && RD->isCXX11StandardLayout(); } llvm_unreachable("bad tail-padding use kind"); Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1563,7 +1563,9 @@ Data.Polymorphic = Record.readInt(); Data.Abstract = Record.readInt(); Data.IsStandardLayout = Record.readInt(); - Data.HasNoNonEmptyBases = Record.readInt(); + Data.IsCXX11StandardLayout = Record.readInt(); + Data.HasBasesWithFields = Record.readInt(); + Data.HasBasesWithNonStaticDataMembers = Record.readInt(); Data.HasPrivateFields = Record.readInt(); Data.HasProtectedFields = Record.readInt(); Data.HasPublicFields = Record.readInt(); @@ -1702,7 +1704,9 @@ MATCH_FIELD(Polymorphic) MATCH_FIELD(Abstract) MATCH_FIELD(IsStandardLayout) - MATCH_FIELD(HasNoNonEmptyBases) + MATCH_FIELD(IsCXX11StandardLayout) + MATCH_FIELD(HasBasesWithFields) + MATCH_FIELD(HasBasesWithNonStaticDataMembers) MATCH_FIELD(HasPrivateFields) MATCH_FIELD(HasProtectedFields) MATCH_FIELD(HasPublicFields) Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5995,7 +5995,9 @@ Record->push_back(Data.Polymorphic); Record->push_back(Data.Abstract); Record->push_back(Data.IsStandardLayout); - Record->push_back(Data.HasNoNonEmptyBases); + Record->push_back(Data.IsCXX11StandardLayout); + Record->push_back(Data.HasBasesWithFields); + Record->push_back(Data.HasBasesWithNonStaticDataMembers); Record->push_back(Data.HasPrivateFields); Record->push_back(Data.HasProtectedFields); Record->push_back(Data.HasPublicFields); Index: test/CXX/drs/dr14xx.cpp =================================================================== --- test/CXX/drs/dr14xx.cpp +++ test/CXX/drs/dr14xx.cpp @@ -7,6 +7,8 @@ // expected-no-diagnostics #endif +// dr1425: na abi + namespace dr1460 { // dr1460: 3.5 #if __cplusplus >= 201103L namespace DRExample { Index: test/CXX/drs/dr16xx.cpp =================================================================== --- test/CXX/drs/dr16xx.cpp +++ test/CXX/drs/dr16xx.cpp @@ -3,6 +3,11 @@ // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +#if __cplusplus < 201103L +// expected-error@+1 {{variadic macro}} +#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__) +#endif + namespace dr1611 { // dr1611: dup 1658 struct A { A(int); }; struct B : virtual A { virtual void f() = 0; }; @@ -219,3 +224,28 @@ // assignment case is superseded by dr2180 } + +namespace dr1672 { // dr1672: 7 + struct Empty {}; + struct A : Empty {}; + struct B { Empty e; }; + struct C : A { B b; int n; }; + struct D : A { int n; B b; }; + + static_assert(!__is_standard_layout(C), ""); + static_assert(__is_standard_layout(D), ""); + + struct E { B b; int n; }; + struct F { int n; B b; }; + union G { B b; int n; }; + union H { int n; B b; }; + + struct X {}; + template struct Y : X, A { T t; }; + + static_assert(!__is_standard_layout(Y), ""); + static_assert(__is_standard_layout(Y), ""); + static_assert(!__is_standard_layout(Y), ""); + static_assert(!__is_standard_layout(Y), ""); + static_assert(!__is_standard_layout(Y), ""); +} Index: test/CXX/drs/dr18xx.cpp =================================================================== --- test/CXX/drs/dr18xx.cpp +++ test/CXX/drs/dr18xx.cpp @@ -4,9 +4,44 @@ // RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors #if __cplusplus < 201103L -// expected-no-diagnostics +// expected-error@+1 {{variadic macro}} +#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__) #endif +namespace dr1813 { // dr1813: 7 + struct B { int i; }; + struct C : B {}; + struct D : C {}; + struct E : D { char : 4; }; + + static_assert(__is_standard_layout(B), ""); + static_assert(__is_standard_layout(C), ""); + static_assert(__is_standard_layout(D), ""); + static_assert(!__is_standard_layout(E), ""); + + struct Q {}; + struct S : Q {}; + struct T : Q {}; + struct U : S, T {}; + + static_assert(__is_standard_layout(Q), ""); + static_assert(__is_standard_layout(S), ""); + static_assert(__is_standard_layout(T), ""); + static_assert(!__is_standard_layout(U), ""); +} + +namespace dr1881 { // dr1881: 7 + struct A { int a : 4; }; + struct B : A { int b : 3; }; + static_assert(__is_standard_layout(A), ""); + static_assert(!__is_standard_layout(B), ""); + + struct C { int : 0; }; + struct D : C { int : 0; }; + static_assert(__is_standard_layout(C), ""); + static_assert(!__is_standard_layout(D), ""); +} + void dr1891() { // dr1891: 4 #if __cplusplus >= 201103L int n; Index: test/CXX/drs/dr21xx.cpp =================================================================== --- test/CXX/drs/dr21xx.cpp +++ test/CXX/drs/dr21xx.cpp @@ -3,6 +3,22 @@ // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +#if __cplusplus < 201103L +// expected-error@+1 {{variadic macro}} +#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__) +#endif + +namespace dr2120 { // dr2120: 7 + struct A {}; + struct B : A {}; + struct C { A a; }; + struct D { C c[5]; }; + struct E : B { D d; }; + static_assert(__is_standard_layout(B), ""); + static_assert(__is_standard_layout(D), ""); + static_assert(!__is_standard_layout(E), ""); +} + namespace dr2180 { // dr2180: yes class A { A &operator=(const A &); // expected-note 0-2{{here}} Index: test/CXX/drs/dr22xx.cpp =================================================================== --- test/CXX/drs/dr22xx.cpp +++ test/CXX/drs/dr22xx.cpp @@ -3,7 +3,7 @@ // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -namespace dr2229 { // dr2229: yes +namespace dr2229 { // dr2229: 7 struct AnonBitfieldQualifiers { const unsigned : 1; // expected-error {{anonymous bit-field cannot have qualifiers}} volatile unsigned : 1; // expected-error {{anonymous bit-field cannot have qualifiers}} Index: test/Layout/watchos-standard-layout.cpp =================================================================== --- test/Layout/watchos-standard-layout.cpp +++ test/Layout/watchos-standard-layout.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -fsyntax-only -triple armv7k-apple-darwin-watchos -fdump-record-layouts %s | FileCheck %s + +// WatchOS, 64-bit iOS, and WebAssembly use the C++11 definition of POD to +// determine whether we can reuse the tail padding of a struct (POD is +// "trivially copyable and standard layout"). The definition of standard +// layout changed some time around C++17; check that we still use the old +// ABI rule. + +// B is not standard-layout, but it was under C++11's rule, so we pack +// C::d into its tail padding anyway. +struct A { int : 0; }; +struct B : A { int n; char c[3]; }; +struct C : B { char d; }; +int c = sizeof(C); +static_assert(!__is_standard_layout(B)); + +// CHECK:*** Dumping AST Record Layout +// CHECK: 0 | struct C +// CHECK-NEXT: 0 | struct B (base) +// CHECK-NEXT: 0 | struct A (base) (empty) +// CHECK-NEXT: 0:- | int +// CHECK-NEXT: 0 | int n +// CHECK-NEXT: 4 | char [3] c +// CHECK-NEXT: 8 | char d +// CHECK-NEXT: | [sizeof=12, dsize=9, align=4, +// CHECK-NEXT: | nvsize=9, nvalign=4] + +// F is not standard-layout due to the repeated D base class, but it was under +// C++11's rule, so we pack G::d into its tail padding anyway. +struct D {}; +struct E : D {}; +struct F : D, E { int n; char c[3]; }; +struct G : F { G(const G&); char d; }; +int g = sizeof(G); +static_assert(!__is_standard_layout(F)); + +// CHECK:*** Dumping AST Record Layout +// CHECK: 0 | struct G +// CHECK-NEXT: 0 | struct F (base) +// CHECK-NEXT: 0 | struct D (base) (empty) +// CHECK-NEXT: 1 | struct E (base) (empty) +// CHECK-NEXT: 1 | struct D (base) (empty) +// CHECK-NEXT: 0 | int n +// CHECK-NEXT: 4 | char [3] c +// CHECK-NEXT: 8 | char d +// CHECK-NEXT: | [sizeof=12, dsize=9, align=4, +// CHECK-NEXT: | nvsize=9, nvalign=4] Index: test/SemaCXX/type-traits.cpp =================================================================== --- test/SemaCXX/type-traits.cpp +++ test/SemaCXX/type-traits.cpp @@ -1353,6 +1353,59 @@ int t43[F(__is_standard_layout(AnIncompleteType[1]))]; // expected-error {{incomplete type}} int t44[F(__is_standard_layout(void))]; int t45[F(__is_standard_layout(const volatile void))]; + + struct HasAnonEmptyBitfield { int : 0; }; + struct HasAnonBitfield { int : 4; }; + struct DerivesFromBitfield : HasAnonBitfield {}; + struct DerivesFromBitfieldWithBitfield : HasAnonBitfield { int : 5; }; + struct DerivesFromBitfieldTwice : DerivesFromBitfield, HasAnonEmptyBitfield {}; + + int t50[T(__is_standard_layout(HasAnonEmptyBitfield))]; + int t51[T(__is_standard_layout(HasAnonBitfield))]; + int t52[T(__is_standard_layout(DerivesFromBitfield))]; + int t53[F(__is_standard_layout(DerivesFromBitfieldWithBitfield))]; + int t54[F(__is_standard_layout(DerivesFromBitfieldTwice))]; + + struct Empty {}; + struct HasEmptyBase : Empty {}; + struct HoldsEmptyBase { Empty e; }; + struct HasRepeatedEmptyBase : Empty, HasEmptyBase {}; // expected-warning {{inaccessible}} + struct HasEmptyBaseAsMember : Empty { Empty e; }; + struct HasEmptyBaseAsSubobjectOfMember1 : Empty { HoldsEmptyBase e; }; + struct HasEmptyBaseAsSubobjectOfMember2 : Empty { HasEmptyBase e; }; + struct HasEmptyBaseAsSubobjectOfMember3 : Empty { HoldsEmptyBase e[2]; }; + struct HasEmptyIndirectBaseAsMember : HasEmptyBase { Empty e; }; + struct HasEmptyIndirectBaseAsSecondMember : HasEmptyBase { int n; Empty e; }; + struct HasEmptyIndirectBaseAfterBitfield : HasEmptyBase { int : 4; Empty e; }; + + int t60[T(__is_standard_layout(Empty))]; + int t61[T(__is_standard_layout(HasEmptyBase))]; + int t62[F(__is_standard_layout(HasRepeatedEmptyBase))]; + int t63[F(__is_standard_layout(HasEmptyBaseAsMember))]; + int t64[F(__is_standard_layout(HasEmptyBaseAsSubobjectOfMember1))]; + int t65[T(__is_standard_layout(HasEmptyBaseAsSubobjectOfMember2))]; // FIXME: standard bug? + int t66[F(__is_standard_layout(HasEmptyBaseAsSubobjectOfMember3))]; + int t67[F(__is_standard_layout(HasEmptyIndirectBaseAsMember))]; + int t68[T(__is_standard_layout(HasEmptyIndirectBaseAsSecondMember))]; + int t69[F(__is_standard_layout(HasEmptyIndirectBaseAfterBitfield))]; // FIXME: standard bug? + + struct StructWithEmptyFields { + int n; + HoldsEmptyBase e[3]; + }; + union UnionWithEmptyFields { + int n; + HoldsEmptyBase e[3]; + }; + struct HasEmptyIndirectBaseAsSecondStructMember : HasEmptyBase { + StructWithEmptyFields u; + }; + struct HasEmptyIndirectBaseAsSecondUnionMember : HasEmptyBase { + UnionWithEmptyFields u; + }; + + int t70[T(__is_standard_layout(HasEmptyIndirectBaseAsSecondStructMember))]; + int t71[F(__is_standard_layout(HasEmptyIndirectBaseAsSecondUnionMember))]; } void is_signed() Index: www/cxx_dr_status.html =================================================================== --- www/cxx_dr_status.html +++ www/cxx_dr_status.html @@ -8365,7 +8365,7 @@ 1425 CD3 Base-class subobjects of standard-layout structs - Unknown + N/A (ABI constraint) 1426 @@ -9847,7 +9847,7 @@ 1672 CD4 Layout compatibility with multiple empty bases - Unknown + SVN 1673 @@ -10693,7 +10693,7 @@ 1813 CD4 Direct vs indirect bases in standard-layout classes - Unknown + SVN 1814 @@ -11101,7 +11101,7 @@ 1881 CD4 Standard-layout classes and unnamed bit-fields - Unknown + SVN 1882 @@ -12535,7 +12535,7 @@ 2120 CD4 Array as first non-static data member in standard-layout class - Unknown + SVN 2121 @@ -13189,7 +13189,7 @@ 2229 tentatively ready Volatile unnamed bit-fields - Unknown + SVN 2230 Index: www/make_cxx_dr_status =================================================================== --- www/make_cxx_dr_status +++ www/make_cxx_dr_status @@ -129,6 +129,9 @@ elif status == 'na lib': avail = 'N/A (Library DR)' avail_style = ' class="na"' + elif status == 'na abi': + avail = 'N/A (ABI constraint)' + avail_style = ' class="na"' elif status.startswith('sup '): dup = status.split(' ', 1)[1] avail = 'Superseded by %s' % (dup, dup)