r270457 - [MS ABI] Implement __declspec(empty_bases) and __declspec(layout_version)

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Mon May 23 10:28:21 PDT 2016


On Mon, May 23, 2016 at 1:16 PM, David Majnemer via cfe-commits
<cfe-commits at 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.

> +}
> +
>  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.)

> +
> +  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.

>    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.

>
> 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.

~Aaron


More information about the cfe-commits mailing list