On Mon, May 23, 2016 at 10:28 AM, Aaron Ballman <aa...@aaronballman.com> wrote:
> On Mon, May 23, 2016 at 1:16 PM, David Majnemer via cfe-commits > <cfe-commits@lists.llvm.org> wrote: > > 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. > > + }]; > > Can you link to MSDN documentation for these two attributes to provide > a bit more information? If not, it would be good to point out that > both of these only apply to struct, class, and union types, and any > other constraints that may be of interest to the user. > Microsoft hasn't updated the list of documented declspecs for quite some time. I'll amend our documentation. > > > +} > > + > > 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; > > + } > > So this cannot accept minor version numbers? (Presumably because minor > and patch versions should not be changing layouts, I suppose.) > It cannot. > > > + > > + 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; > > + > > Spurious newline added. > Spurious newline intended. the abi_tag attribute is most certainly _not_ a Microsoft attribute. > > > 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}} > > Missing a test that ensures the attribute accepts no arguments. > Sure, I'll add one. > > > > > 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}} > > Missing a test that ensures the attribute still fails when given no > arguments. > Sure, I'll add one. > > ~Aaron >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits