Author: majnemer Date: Mon May 23 12:16:12 2016 New Revision: 270457 URL: http://llvm.org/viewvc/llvm-project?rev=270457&view=rev Log: [MS ABI] Implement __declspec(empty_bases) and __declspec(layout_version)
The layout_version attribute is pretty straightforward: use the layout rules from version XYZ of MSVC when used like struct __declspec(layout_version(XYZ)) S {}; The empty_bases attribute is more interesting. It tries to get the C++ empty base optimization to fire more often by tweaking the MSVC ABI rules in subtle ways: 1. Disable the leading and trailing zero-sized object flags if a class is marked __declspec(empty_bases) and is empty. This means that given: struct __declspec(empty_bases) A {}; struct __declspec(empty_bases) B {}; struct C : A, B {}; 'C' will have size 1 and nvsize 0 despite not being annotated __declspec(empty_bases). 2. When laying out virtual or non-virtual bases, disable the injection of padding between classes if the most derived class is marked __declspec(empty_bases). This means that given: struct A {}; struct B {}; struct __declspec(empty_bases) C : A, B {}; 'C' will have size 1 and nvsize 0. 3. When calculating the offset of a non-virtual base, choose offset zero if the most derived class is marked __declspec(empty_bases) and the base is empty _and_ has an nvsize of 0. Because of the ABI rules, this does not mean that empty bases reliably get placed at offset 0! For example: struct A {}; struct B {}; struct __declspec(empty_bases) C : A, B { virtual ~C(); }; 'C' will be pointer sized to account for the vfptr at offset 0. 'A' and 'B' will _not_ be at offset 0 despite being empty! Instead, they will be located right after the vfptr. This occurs due to the interaction betweeen non-virtual base layout and virtual function pointer injection: injection occurs after the nv-bases and shifts them down by the size of a pointer. Added: cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp cfe/trunk/test/SemaCXX/ms-empty_bases.cpp cfe/trunk/test/SemaCXX/ms-layout_version.cpp Modified: cfe/trunk/include/clang/AST/RecordLayout.h cfe/trunk/include/clang/Basic/Attr.td cfe/trunk/include/clang/Basic/AttrDocs.td cfe/trunk/lib/AST/RecordLayout.cpp cfe/trunk/lib/AST/RecordLayoutBuilder.cpp cfe/trunk/lib/Sema/SemaDeclAttr.cpp Modified: cfe/trunk/include/clang/AST/RecordLayout.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/RecordLayout.h?rev=270457&r1=270456&r2=270457&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/RecordLayout.h (original) +++ cfe/trunk/include/clang/AST/RecordLayout.h Mon May 23 12:16:12 2016 @@ -104,10 +104,10 @@ private: /// a primary base class. bool HasExtendableVFPtr : 1; - /// HasZeroSizedSubObject - True if this class contains a zero sized member - /// or base or a base with a zero sized member or base. Only used for - /// MS-ABI. - bool HasZeroSizedSubObject : 1; + /// EndsWithZeroSizedObject - True if this class contains a zero sized + /// member or base or a base with a zero sized member or base. + /// Only used for MS-ABI. + bool EndsWithZeroSizedObject : 1; /// \brief True if this class is zero sized or first base is zero sized or /// has this property. Only used for MS-ABI. @@ -154,7 +154,7 @@ private: const CXXRecordDecl *PrimaryBase, bool IsPrimaryBaseVirtual, const CXXRecordDecl *BaseSharingVBPtr, - bool HasZeroSizedSubObject, + bool EndsWithZeroSizedObject, bool LeadsWithZeroSizedBase, const BaseOffsetsMapTy& BaseOffsets, const VBaseOffsetsMapTy& VBaseOffsets); @@ -283,8 +283,8 @@ public: return RequiredAlignment; } - bool hasZeroSizedSubObject() const { - return CXXInfo && CXXInfo->HasZeroSizedSubObject; + bool endsWithZeroSizedObject() const { + return CXXInfo && CXXInfo->EndsWithZeroSizedObject; } bool leadsWithZeroSizedBase() const { Modified: cfe/trunk/include/clang/Basic/Attr.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=270457&r1=270456&r2=270457&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/Attr.td (original) +++ cfe/trunk/include/clang/Basic/Attr.td Mon May 23 12:16:12 2016 @@ -745,6 +745,12 @@ def Destructor : InheritableAttr { let Documentation = [Undocumented]; } +def EmptyBases : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> { + let Spellings = [Declspec<"empty_bases">]; + let Subjects = SubjectList<[CXXRecord]>; + let Documentation = [EmptyBasesDocs]; +} + def EnableIf : InheritableAttr { let Spellings = [GNU<"enable_if">]; let Subjects = SubjectList<[Function]>; @@ -868,6 +874,13 @@ def Restrict : InheritableAttr { let Documentation = [Undocumented]; } +def LayoutVersion : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> { + let Spellings = [Declspec<"layout_version">]; + let Args = [UnsignedArgument<"Version">]; + let Subjects = SubjectList<[CXXRecord]>; + let Documentation = [LayoutVersionDocs]; +} + def MaxFieldAlignment : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly. let Spellings = []; Modified: cfe/trunk/include/clang/Basic/AttrDocs.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrDocs.td?rev=270457&r1=270456&r2=270457&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td (original) +++ cfe/trunk/include/clang/Basic/AttrDocs.td Mon May 23 12:16:12 2016 @@ -1553,6 +1553,24 @@ manipulating bits of the enumerator when }]; } +def EmptyBasesDocs : Documentation { + let Category = DocCatType; + let Content = [{ +The empty_bases attribute permits the compiler to utilize the +empty-base-optimization more frequently. +It is only supported when using the Microsoft C++ ABI. + }]; +} + +def LayoutVersionDocs : Documentation { + let Category = DocCatType; + let Content = [{ +The layout_version attribute requests that the compiler utilize the class +layout rules of a particular compiler version. +It is only supported when using the Microsoft C++ ABI. + }]; +} + def MSInheritanceDocs : Documentation { let Category = DocCatType; let Heading = "__single_inhertiance, __multiple_inheritance, __virtual_inheritance"; Modified: cfe/trunk/lib/AST/RecordLayout.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/RecordLayout.cpp?rev=270457&r1=270456&r2=270457&view=diff ============================================================================== --- cfe/trunk/lib/AST/RecordLayout.cpp (original) +++ cfe/trunk/lib/AST/RecordLayout.cpp Mon May 23 12:16:12 2016 @@ -58,7 +58,7 @@ ASTRecordLayout::ASTRecordLayout(const A const CXXRecordDecl *PrimaryBase, bool IsPrimaryBaseVirtual, const CXXRecordDecl *BaseSharingVBPtr, - bool HasZeroSizedSubObject, + bool EndsWithZeroSizedObject, bool LeadsWithZeroSizedBase, const BaseOffsetsMapTy& BaseOffsets, const VBaseOffsetsMapTy& VBaseOffsets) @@ -82,7 +82,7 @@ ASTRecordLayout::ASTRecordLayout(const A CXXInfo->VBPtrOffset = vbptroffset; CXXInfo->HasExtendableVFPtr = hasExtendableVFPtr; CXXInfo->BaseSharingVBPtr = BaseSharingVBPtr; - CXXInfo->HasZeroSizedSubObject = HasZeroSizedSubObject; + CXXInfo->EndsWithZeroSizedObject = EndsWithZeroSizedObject; CXXInfo->LeadsWithZeroSizedBase = LeadsWithZeroSizedBase; Modified: cfe/trunk/lib/AST/RecordLayoutBuilder.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/RecordLayoutBuilder.cpp?rev=270457&r1=270456&r2=270457&view=diff ============================================================================== --- cfe/trunk/lib/AST/RecordLayoutBuilder.cpp (original) +++ cfe/trunk/lib/AST/RecordLayoutBuilder.cpp Mon May 23 12:16:12 2016 @@ -2228,7 +2228,8 @@ public: /// laid out. void initializeCXXLayout(const CXXRecordDecl *RD); void layoutNonVirtualBases(const CXXRecordDecl *RD); - void layoutNonVirtualBase(const CXXRecordDecl *BaseDecl, + void layoutNonVirtualBase(const CXXRecordDecl *RD, + const CXXRecordDecl *BaseDecl, const ASTRecordLayout &BaseLayout, const ASTRecordLayout *&PreviousBaseLayout); void injectVFPtr(const CXXRecordDecl *RD); @@ -2334,7 +2335,7 @@ MicrosoftRecordLayoutBuilder::getAdjuste if (!MaxFieldAlignment.isZero()) Info.Alignment = std::min(Info.Alignment, MaxFieldAlignment); // Track zero-sized subobjects here where it's already available. - EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject(); + EndsWithZeroSizedObject = Layout.endsWithZeroSizedObject(); // Respect required alignment, this is necessary because we may have adjusted // the alignment in the case of pragam pack. Note that the required alignment // doesn't actually apply to the struct alignment at this point. @@ -2369,7 +2370,7 @@ MicrosoftRecordLayoutBuilder::getAdjuste if (auto RT = FD->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) { auto const &Layout = Context.getASTRecordLayout(RT->getDecl()); - EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject(); + EndsWithZeroSizedObject = Layout.endsWithZeroSizedObject(); FieldRequiredAlignment = std::max(FieldRequiredAlignment, Layout.getRequiredAlignment()); } @@ -2502,7 +2503,7 @@ MicrosoftRecordLayoutBuilder::layoutNonV LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase(); } // Lay out the base. - layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout); + layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout); } // Figure out if we need a fresh VFPtr for this class. if (!PrimaryBase && RD->isDynamicClass()) @@ -2531,7 +2532,7 @@ MicrosoftRecordLayoutBuilder::layoutNonV LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase(); } // Lay out the base. - layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout); + layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout); VBPtrOffset = Bases[BaseDecl] + BaseLayout.getNonVirtualSize(); } // Set our VBPtroffset if we know it at this point. @@ -2543,15 +2544,32 @@ MicrosoftRecordLayoutBuilder::layoutNonV } } +static bool recordUsesEBO(const RecordDecl *RD) { + if (!isa<CXXRecordDecl>(RD)) + return false; + if (RD->hasAttr<EmptyBasesAttr>()) + return true; + if (auto *LVA = RD->getAttr<LayoutVersionAttr>()) + // TODO: Double check with the next version of MSVC. + if (LVA->getVersion() <= LangOptions::MSVC2015) + return false; + // TODO: Some later version of MSVC will change the default behavior of the + // compiler to enable EBO by default. When this happens, we will need an + // additional isCompatibleWithMSVC check. + return false; +} + void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase( + const CXXRecordDecl *RD, const CXXRecordDecl *BaseDecl, const ASTRecordLayout &BaseLayout, const ASTRecordLayout *&PreviousBaseLayout) { // Insert padding between two bases if the left first one is zero sized or // contains a zero sized subobject and the right is zero sized or one leads // with a zero sized base. - if (PreviousBaseLayout && PreviousBaseLayout->hasZeroSizedSubObject() && - BaseLayout.leadsWithZeroSizedBase()) + bool MDCUsesEBO = recordUsesEBO(RD); + if (PreviousBaseLayout && PreviousBaseLayout->endsWithZeroSizedObject() && + BaseLayout.leadsWithZeroSizedBase() && !MDCUsesEBO) Size++; ElementInfo Info = getAdjustedElementInfo(BaseLayout); CharUnits BaseOffset; @@ -2560,14 +2578,23 @@ void MicrosoftRecordLayoutBuilder::layou bool FoundBase = false; if (UseExternalLayout) { FoundBase = External.getExternalNVBaseOffset(BaseDecl, BaseOffset); - if (FoundBase) + if (FoundBase) { assert(BaseOffset >= Size && "base offset already allocated"); + Size = BaseOffset; + } } - if (!FoundBase) - BaseOffset = Size.alignTo(Info.Alignment); + if (!FoundBase) { + if (MDCUsesEBO && BaseDecl->isEmpty() && + BaseLayout.getNonVirtualSize() == CharUnits::Zero()) { + BaseOffset = CharUnits::Zero(); + } else { + // Otherwise, lay the base out at the end of the MDC. + BaseOffset = Size = Size.alignTo(Info.Alignment); + } + } Bases.insert(std::make_pair(BaseDecl, BaseOffset)); - Size = BaseOffset + BaseLayout.getNonVirtualSize(); + Size += BaseLayout.getNonVirtualSize(); PreviousBaseLayout = &BaseLayout; } @@ -2746,8 +2773,9 @@ void MicrosoftRecordLayoutBuilder::layou // with a zero sized base. The padding between virtual bases is 4 // bytes (in both 32 and 64 bits modes) and always involves rounding up to // the required alignment, we don't know why. - if ((PreviousBaseLayout && PreviousBaseLayout->hasZeroSizedSubObject() && - BaseLayout.leadsWithZeroSizedBase()) || HasVtordisp) { + if ((PreviousBaseLayout && PreviousBaseLayout->endsWithZeroSizedObject() && + BaseLayout.leadsWithZeroSizedBase() && !recordUsesEBO(RD)) || + HasVtordisp) { Size = Size.alignTo(VtorDispAlignment) + VtorDispSize; Alignment = std::max(VtorDispAlignment, Alignment); } @@ -2785,8 +2813,10 @@ void MicrosoftRecordLayoutBuilder::final Size = Size.alignTo(RoundingAlignment); } if (Size.isZero()) { - EndsWithZeroSizedObject = true; - LeadsWithZeroSizedBase = true; + if (!recordUsesEBO(RD) || !cast<CXXRecordDecl>(RD)->isEmpty()) { + EndsWithZeroSizedObject = true; + LeadsWithZeroSizedBase = true; + } // Zero-sized structures have size equal to their alignment if a // __declspec(align) came into play. if (RequiredAlignment >= MinEmptyStructSize) @@ -2919,7 +2949,7 @@ ASTContext::getASTRecordLayout(const Rec NewEntry = new (*this) ASTRecordLayout( *this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment, Builder.HasOwnVFPtr, Builder.HasOwnVFPtr || Builder.PrimaryBase, - Builder.VBPtrOffset, Builder.NonVirtualSize, + Builder.VBPtrOffset, Builder.DataSize, Builder.FieldOffsets.data(), Builder.FieldOffsets.size(), Builder.NonVirtualSize, Builder.Alignment, CharUnits::Zero(), Builder.PrimaryBase, false, Builder.SharedVBPtrBase, Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=270457&r1=270456&r2=270457&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Mon May 23 12:16:12 2016 @@ -4968,6 +4968,24 @@ static void handleX86ForceAlignArgPointe Attr.getAttributeSpellingListIndex())); } +static void handleLayoutVersion(Sema &S, Decl *D, const AttributeList &Attr) { + uint32_t Version; + Expr *VersionExpr = static_cast<Expr *>(Attr.getArgAsExpr(0)); + if (!checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), Version)) + return; + + // TODO: Investigate what happens with the next major version of MSVC. + if (Version != LangOptions::MSVC2015) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds) + << Attr.getName() << Version << VersionExpr->getSourceRange(); + return; + } + + D->addAttr(::new (S.Context) + LayoutVersionAttr(Attr.getRange(), S.Context, Version, + Attr.getAttributeSpellingListIndex())); +} + DLLImportAttr *Sema::mergeDLLImportAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex) { if (D->hasAttr<DLLExportAttr>()) { @@ -5749,6 +5767,12 @@ static void ProcessDeclAttribute(Sema &S break; // Microsoft attributes: + case AttributeList::AT_EmptyBases: + handleSimpleAttribute<EmptyBasesAttr>(S, D, Attr); + break; + case AttributeList::AT_LayoutVersion: + handleLayoutVersion(S, D, Attr); + break; case AttributeList::AT_MSNoVTable: handleSimpleAttribute<MSNoVTableAttr>(S, D, Attr); break; @@ -5767,6 +5791,7 @@ static void ProcessDeclAttribute(Sema &S case AttributeList::AT_Thread: handleDeclspecThreadAttr(S, D, Attr); break; + case AttributeList::AT_AbiTag: handleAbiTagAttr(S, D, Attr); break; Added: cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp?rev=270457&view=auto ============================================================================== --- cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp (added) +++ cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp Mon May 23 12:16:12 2016 @@ -0,0 +1,266 @@ +// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple i686-pc-win32 -fms-extensions -fdump-record-layouts -fsyntax-only %s 2>/dev/null \ +// RUN: | FileCheck %s +// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple x86_64-pc-win32 -fms-extensions -fdump-record-layouts -fsyntax-only %s 2>/dev/null \ +// RUN: | FileCheck %s + +namespace test1 { + +struct A { + int a; +}; +struct B { + int b; +}; +struct C {}; +struct __declspec(align(16)) D {}; +struct __declspec(empty_bases) X : A, D, B, C { +}; + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::A +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::D (empty) +// CHECK-NEXT: | [sizeof=16, align=16, +// CHECK-NEXT: | nvsize=0, nvalign=16] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::B +// CHECK-NEXT: 0 | int b +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::C (empty) +// CHECK-NEXT: | [sizeof=1, align=1, +// CHECK-NEXT: | nvsize=0, nvalign=1] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::X +// CHECK-NEXT: 0 | struct test1::A (base) +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: 0 | struct test1::D (base) (empty) +// CHECK-NEXT: 0 | struct test1::C (base) (empty) +// CHECK-NEXT: 4 | struct test1::B (base) +// CHECK-NEXT: 4 | int b +// CHECK-NEXT: | [sizeof=16, align=16, +// CHECK-NEXT: | nvsize=16, nvalign=16] + +int _ = sizeof(X); +} + +namespace test2 { +struct A { + int a; +}; +struct __declspec(empty_bases) B {}; +struct C : A { + B b; +}; + +struct D {}; +struct E { + int e; +}; +struct F : D, E {}; + +struct G : C, F {}; + +int _ = sizeof(G); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::A +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::B (empty) +// CHECK-NEXT: | [sizeof=1, align=1, +// CHECK-NEXT: | nvsize=0, nvalign=1] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::C +// CHECK-NEXT: 0 | struct test2::A (base) +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: 4 | struct test2::B b (empty) +// CHECK-NEXT: | [sizeof=8, align=4, +// CHECK-NEXT: | nvsize=8, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::D (empty) +// CHECK-NEXT: | [sizeof=1, align=1, +// CHECK-NEXT: | nvsize=0, nvalign=1] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::E +// CHECK-NEXT: 0 | int e +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::F +// CHECK-NEXT: 0 | struct test2::D (base) (empty) +// CHECK-NEXT: 0 | struct test2::E (base) +// CHECK-NEXT: 0 | int e +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::G +// CHECK-NEXT: 0 | struct test2::C (base) +// CHECK-NEXT: 0 | struct test2::A (base) +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: 4 | struct test2::B b (empty) +// CHECK-NEXT: 8 | struct test2::F (base) +// CHECK-NEXT: 8 | struct test2::D (base) (empty) +// CHECK-NEXT: 8 | struct test2::E (base) +// CHECK-NEXT: 8 | int e +// CHECK-NEXT: | [sizeof=12, align=4, +// CHECK-NEXT: | nvsize=12, nvalign=4] +} + +namespace test3 { +struct A { + int a; +}; +struct B {}; +struct C : A { + B b; +}; + +struct D {}; +struct E { + int e; +}; +struct F : D, E {}; + +struct __declspec(empty_bases) G : C, F {}; + +int _ = sizeof(G); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::A +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::B (empty) +// CHECK-NEXT: | [sizeof=1, align=1, +// CHECK-NEXT: | nvsize=0, nvalign=1] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::C +// CHECK-NEXT: 0 | struct test3::A (base) +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: 4 | struct test3::B b (empty) +// CHECK-NEXT: | [sizeof=8, align=4, +// CHECK-NEXT: | nvsize=8, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::D (empty) +// CHECK-NEXT: | [sizeof=1, align=1, +// CHECK-NEXT: | nvsize=0, nvalign=1] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::E +// CHECK-NEXT: 0 | int e +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::F +// CHECK-NEXT: 0 | struct test3::D (base) (empty) +// CHECK-NEXT: 0 | struct test3::E (base) +// CHECK-NEXT: 0 | int e +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::G +// CHECK-NEXT: 0 | struct test3::C (base) +// CHECK-NEXT: 0 | struct test3::A (base) +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: 4 | struct test3::B b (empty) +// CHECK-NEXT: 8 | struct test3::F (base) +// CHECK-NEXT: 8 | struct test3::D (base) (empty) +// CHECK-NEXT: 8 | struct test3::E (base) +// CHECK-NEXT: 8 | int e +// CHECK-NEXT: | [sizeof=12, align=4, +// CHECK-NEXT: | nvsize=12, nvalign=4] +} + +namespace test4 { +struct A { + int a; +}; +struct B {}; +struct C : A { + B b; +}; + +struct __declspec(empty_bases) D {}; +struct E { + int e; +}; +struct F : D, E {}; + +struct G : C, F {}; + +int _ = sizeof(G); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::A +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::B (empty) +// CHECK-NEXT: | [sizeof=1, align=1, +// CHECK-NEXT: | nvsize=0, nvalign=1] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::C +// CHECK-NEXT: 0 | struct test4::A (base) +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: 4 | struct test4::B b (empty) +// CHECK-NEXT: | [sizeof=8, align=4, +// CHECK-NEXT: | nvsize=8, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::D (empty) +// CHECK-NEXT: | [sizeof=1, align=1, +// CHECK-NEXT: | nvsize=0, nvalign=1] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::E +// CHECK-NEXT: 0 | int e +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::F +// CHECK-NEXT: 0 | struct test4::D (base) (empty) +// CHECK-NEXT: 0 | struct test4::E (base) +// CHECK-NEXT: 0 | int e +// CHECK-NEXT: | [sizeof=4, align=4, +// CHECK-NEXT: | nvsize=4, nvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::G +// CHECK-NEXT: 0 | struct test4::C (base) +// CHECK-NEXT: 0 | struct test4::A (base) +// CHECK-NEXT: 0 | int a +// CHECK-NEXT: 4 | struct test4::B b (empty) +// CHECK-NEXT: 8 | struct test4::F (base) +// CHECK-NEXT: 8 | struct test4::D (base) (empty) +// CHECK-NEXT: 8 | struct test4::E (base) +// CHECK-NEXT: 8 | int e +// CHECK-NEXT: | [sizeof=12, align=4, +// CHECK-NEXT: | nvsize=12, nvalign=4] +} Added: cfe/trunk/test/SemaCXX/ms-empty_bases.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/ms-empty_bases.cpp?rev=270457&view=auto ============================================================================== --- cfe/trunk/test/SemaCXX/ms-empty_bases.cpp (added) +++ cfe/trunk/test/SemaCXX/ms-empty_bases.cpp Mon May 23 12:16:12 2016 @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -triple i386-pc-win32 %s -fsyntax-only -verify -fms-extensions -Wno-microsoft -std=c++11 + +struct __declspec(empty_bases) S {}; +enum __declspec(empty_bases) E {}; // expected-warning{{'empty_bases' attribute only applies to classes}} +int __declspec(empty_bases) I; // expected-warning{{'empty_bases' attribute only applies to classes}} +typedef struct T __declspec(empty_bases) U; // expected-warning{{'empty_bases' attribute only applies to classes}} +auto z = []() __declspec(empty_bases) { return nullptr; }; // expected-warning{{'empty_bases' attribute only applies to classes}} Added: cfe/trunk/test/SemaCXX/ms-layout_version.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/ms-layout_version.cpp?rev=270457&view=auto ============================================================================== --- cfe/trunk/test/SemaCXX/ms-layout_version.cpp (added) +++ cfe/trunk/test/SemaCXX/ms-layout_version.cpp Mon May 23 12:16:12 2016 @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple i386-pc-win32 %s -fsyntax-only -verify -fms-extensions -Wno-microsoft -std=c++11 + +struct __declspec(layout_version(19)) S {}; +enum __declspec(layout_version(19)) E {}; // expected-warning{{'layout_version' attribute only applies to classes}} +int __declspec(layout_version(19)) I; // expected-warning{{'layout_version' attribute only applies to classes}} +typedef struct T __declspec(layout_version(19)) U; // expected-warning{{'layout_version' attribute only applies to classes}} +auto z = []() __declspec(layout_version(19)) { return nullptr; }; // expected-warning{{'layout_version' attribute only applies to classes}} + +struct __declspec(layout_version(18)) X {}; // expected-error{{'layout_version' attribute parameter 18 is out of bounds}} +struct __declspec(layout_version(20)) Y {}; // expected-error{{'layout_version' attribute parameter 20 is out of bounds}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits