-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Recommit "Implement [[msvc::no_unique_address]] (#65675)" #67199
Conversation
Change the attribute docs so that there is a separate one for the MSVC attribute. This reverts commit 71f9e76.
@llvm/pr-subscribers-clang ChangesChange the attribute docs so that there is a separate one for the MSVC This reverts commit 71f9e76. Patch is 25.37 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/67199.diff 9 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 21a3b5226623cf2..3da91d25ec97347 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1798,11 +1798,24 @@ def ArmMveStrictPolymorphism : TypeAttr, TargetSpecificAttr<TargetARM> {
let Documentation = [ArmMveStrictPolymorphismDocs];
}
-def NoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetItaniumCXXABI> {
- let Spellings = [CXX11<"", "no_unique_address", 201803>];
+def NoUniqueAddress : InheritableAttr {
+ let Subjects = SubjectList<[NonBitField], ErrorDiag>;
+ // No spellings because instances of this attribute are created by
+ // MSNoUniqueAddress and ItaniumNoUniqueAddress
+ let Spellings = [];
+ let Documentation = [InternalOnly];
+}
+
+def MSNoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
let Subjects = SubjectList<[NonBitField], ErrorDiag>;
+ let Spellings = [CXX11<"msvc", "no_unique_address", 201803>];
+ let Documentation = [MSNoUniqueAddressDocs];
+}
+
+def ItaniumNoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetItaniumCXXABI> {
+ let Subjects = SubjectList<[NonBitField], ErrorDiag>;
+ let Spellings = [CXX11<"", "no_unique_address", 201803>];
let Documentation = [NoUniqueAddressDocs];
- let SimpleHandler = 1;
}
def ReturnsTwice : InheritableAttr {
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index b13baa46754cfd4..d16ce23c067982d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1408,6 +1408,15 @@ in C++11 onwards.
}];
}
+def MSNoUniqueAddressDocs : Documentation {
+ let Category = DocCatField;
+ let Content = [{
+On MSVC targets, ``[[no_unique_address]]`` is ignored; use
+``[[msvc::no_unique_address]]`` instead. Currently there is no guarantee of ABI
+compatibility or stability with MSVC.
+ }];
+}
+
def ObjCRequiresSuperDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 08ae2087cfe70eb..07aee0d87c835a8 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -4507,9 +4507,14 @@ bool FieldDecl::isZeroSize(const ASTContext &Ctx) const {
// Otherwise, [...] the circumstances under which the object has zero size
// are implementation-defined.
- // FIXME: This might be Itanium ABI specific; we don't yet know what the MS
- // ABI will do.
- return true;
+ if (!Ctx.getTargetInfo().getCXXABI().isMicrosoft())
+ return true;
+
+ // MS ABI: has nonzero size if it is a class type with class type fields,
+ // whether or not they have nonzero size
+ return !llvm::any_of(CXXRD->fields(), [](const FieldDecl *Field) {
+ return Field->getType()->getAs<RecordType>();
+ });
}
bool FieldDecl::isPotentiallyOverlapping() const {
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index 8afd88ae7be27b3..f1f2275da44dcad 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -2545,7 +2545,10 @@ struct MicrosoftRecordLayoutBuilder {
CharUnits Alignment;
};
typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetsMapTy;
- MicrosoftRecordLayoutBuilder(const ASTContext &Context) : Context(Context) {}
+ MicrosoftRecordLayoutBuilder(const ASTContext &Context,
+ EmptySubobjectMap *EmptySubobjects)
+ : Context(Context), EmptySubobjects(EmptySubobjects) {}
+
private:
MicrosoftRecordLayoutBuilder(const MicrosoftRecordLayoutBuilder &) = delete;
void operator=(const MicrosoftRecordLayoutBuilder &) = delete;
@@ -2595,6 +2598,8 @@ struct MicrosoftRecordLayoutBuilder {
llvm::SmallPtrSetImpl<const CXXRecordDecl *> &HasVtorDispSet,
const CXXRecordDecl *RD) const;
const ASTContext &Context;
+ EmptySubobjectMap *EmptySubobjects;
+
/// The size of the record being laid out.
CharUnits Size;
/// The non-virtual size of the record layout.
@@ -2908,8 +2913,7 @@ static bool recordUsesEBO(const RecordDecl *RD) {
}
void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
- const CXXRecordDecl *RD,
- const CXXRecordDecl *BaseDecl,
+ 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
@@ -2942,6 +2946,7 @@ void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
}
Bases.insert(std::make_pair(BaseDecl, BaseOffset));
Size += BaseLayout.getNonVirtualSize();
+ DataSize = Size;
PreviousBaseLayout = &BaseLayout;
}
@@ -2959,15 +2964,43 @@ void MicrosoftRecordLayoutBuilder::layoutField(const FieldDecl *FD) {
LastFieldIsNonZeroWidthBitfield = false;
ElementInfo Info = getAdjustedElementInfo(FD);
Alignment = std::max(Alignment, Info.Alignment);
- CharUnits FieldOffset;
- if (UseExternalLayout)
+
+ const CXXRecordDecl *FieldClass = FD->getType()->getAsCXXRecordDecl();
+ bool IsOverlappingEmptyField = FD->isPotentiallyOverlapping() &&
+ FieldClass->isEmpty() &&
+ FieldClass->fields().empty();
+ CharUnits FieldOffset = CharUnits::Zero();
+
+ if (UseExternalLayout) {
FieldOffset =
Context.toCharUnitsFromBits(External.getExternalFieldOffset(FD));
- else if (IsUnion)
+ } else if (IsUnion) {
FieldOffset = CharUnits::Zero();
- else
+ } else if (EmptySubobjects) {
+ if (!IsOverlappingEmptyField)
+ FieldOffset = DataSize.alignTo(Info.Alignment);
+
+ while (!EmptySubobjects->CanPlaceFieldAtOffset(FD, FieldOffset)) {
+ const CXXRecordDecl *ParentClass = cast<CXXRecordDecl>(FD->getParent());
+ bool HasBases = ParentClass && (!ParentClass->bases().empty() ||
+ !ParentClass->vbases().empty());
+ if (FieldOffset == CharUnits::Zero() && DataSize != CharUnits::Zero() &&
+ HasBases) {
+ // MSVC appears to only do this when there are base classes;
+ // otherwise it overlaps no_unique_address fields in non-zero offsets.
+ FieldOffset = DataSize.alignTo(Info.Alignment);
+ } else {
+ FieldOffset += Info.Alignment;
+ }
+ }
+ } else {
FieldOffset = Size.alignTo(Info.Alignment);
+ }
placeFieldAtOffset(FieldOffset);
+
+ if (!IsOverlappingEmptyField)
+ DataSize = std::max(DataSize, FieldOffset + Info.Size);
+
Size = std::max(Size, FieldOffset + Info.Size);
}
@@ -3013,6 +3046,7 @@ void MicrosoftRecordLayoutBuilder::layoutBitField(const FieldDecl *FD) {
Alignment = std::max(Alignment, Info.Alignment);
RemainingBitsInField = Context.toBits(Info.Size) - Width;
}
+ DataSize = Size;
}
void
@@ -3038,6 +3072,7 @@ MicrosoftRecordLayoutBuilder::layoutZeroWidthBitField(const FieldDecl *FD) {
Size = FieldOffset;
Alignment = std::max(Alignment, Info.Alignment);
}
+ DataSize = Size;
}
void MicrosoftRecordLayoutBuilder::injectVBPtr(const CXXRecordDecl *RD) {
@@ -3304,8 +3339,9 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
const ASTRecordLayout *NewEntry = nullptr;
if (isMsLayout(*this)) {
- MicrosoftRecordLayoutBuilder Builder(*this);
if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
+ EmptySubobjectMap EmptySubobjects(*this, RD);
+ MicrosoftRecordLayoutBuilder Builder(*this, &EmptySubobjects);
Builder.cxxLayout(RD);
NewEntry = new (*this) ASTRecordLayout(
*this, Builder.Size, Builder.Alignment, Builder.Alignment,
@@ -3317,6 +3353,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
Builder.EndsWithZeroSizedObject, Builder.LeadsWithZeroSizedBase,
Builder.Bases, Builder.VBases);
} else {
+ MicrosoftRecordLayoutBuilder Builder(*this, /*EmptySubobjects=*/nullptr);
Builder.layout(D);
NewEntry = new (*this) ASTRecordLayout(
*this, Builder.Size, Builder.Alignment, Builder.Alignment,
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 5a6b5efbf6c1223..cc93fe7d5461967 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4374,7 +4374,8 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
case ParsedAttr::AT_Deprecated:
case ParsedAttr::AT_FallThrough:
case ParsedAttr::AT_CXX11NoReturn:
- case ParsedAttr::AT_NoUniqueAddress:
+ case ParsedAttr::AT_MSNoUniqueAddress:
+ case ParsedAttr::AT_ItaniumNoUniqueAddress:
case ParsedAttr::AT_Likely:
case ParsedAttr::AT_Unlikely:
return true;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 090a54eedaa07d0..f3839b2c59e0dcf 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -8372,6 +8372,10 @@ static void handleNoMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(NoMergeAttr::Create(S.Context, AL));
}
+static void handleNoUniqueAddressAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ D->addAttr(NoUniqueAddressAttr::Create(S.Context, AL));
+}
+
static void handleSYCLKernelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// The 'sycl_kernel' attribute applies only to function templates.
const auto *FD = cast<FunctionDecl>(D);
@@ -9277,6 +9281,12 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_NoMerge:
handleNoMergeAttr(S, D, AL);
break;
+ case ParsedAttr::AT_MSNoUniqueAddress:
+ handleNoUniqueAddressAttr(S, D, AL);
+ break;
+ case ParsedAttr::AT_ItaniumNoUniqueAddress:
+ handleNoUniqueAddressAttr(S, D, AL);
+ break;
case ParsedAttr::AT_AvailableOnlyInDefaultEvalMethod:
handleAvailableOnlyInDefaultEvalMethod(S, D, AL);
diff --git a/clang/test/Layout/ms-no-unique-address.cpp b/clang/test/Layout/ms-no-unique-address.cpp
new file mode 100644
index 000000000000000..51cfd9a6ae3b77f
--- /dev/null
+++ b/clang/test/Layout/ms-no-unique-address.cpp
@@ -0,0 +1,381 @@
+// RUN: %clang_cc1 -std=c++2a -fsyntax-only -triple x86_64-windows-msvc -fms-compatibility -fdump-record-layouts %s | FileCheck %s
+
+namespace Empty {
+ struct A {};
+ struct A2 {};
+ struct A3 { [[msvc::no_unique_address]] A a; };
+ struct alignas(8) A4 {};
+
+ struct B {
+ [[msvc::no_unique_address]] A a;
+ char b;
+ };
+ static_assert(sizeof(B) == 1);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::B
+ // CHECK-NEXT: 0 | struct Empty::A a (empty)
+ // CHECK-NEXT: 0 | char b
+ // CHECK-NEXT: | [sizeof=1, align=1,
+ // CHECK-NEXT: | nvsize=1, nvalign=1]
+
+ struct C {
+ [[msvc::no_unique_address]] A a;
+ [[msvc::no_unique_address]] A2 a2;
+ char c;
+ };
+ static_assert(sizeof(C) == 1);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::C
+ // CHECK-NEXT: 0 | struct Empty::A a (empty)
+ // CHECK-NEXT: 0 | struct Empty::A2 a2 (empty)
+ // CHECK-NEXT: 0 | char c
+ // CHECK-NEXT: | [sizeof=1, align=1,
+ // CHECK-NEXT: | nvsize=1, nvalign=1]
+
+ struct D {
+ [[msvc::no_unique_address]] A3 a;
+ int i;
+ };
+ static_assert(sizeof(D) == 8);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::D
+ // CHECK-NEXT: 0 | struct Empty::A3 a (empty)
+ // CHECK-NEXT: 0 | struct Empty::A a (empty)
+ // CHECK-NEXT: 4 | int i
+ // CHECK-NEXT: | [sizeof=8, align=4,
+ // CHECK-NEXT: | nvsize=8, nvalign=4]
+
+ struct E {
+ [[msvc::no_unique_address]] A a1;
+ [[msvc::no_unique_address]] A a2;
+ char e;
+ };
+ static_assert(sizeof(E) == 2);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::E
+ // CHECK-NEXT: 0 | struct Empty::A a1 (empty)
+ // CHECK-NEXT: 1 | struct Empty::A a2 (empty)
+ // CHECK-NEXT: 0 | char e
+ // CHECK-NEXT: | [sizeof=2, align=1,
+ // CHECK-NEXT: | nvsize=2, nvalign=1]
+
+ struct F {
+ ~F();
+ [[msvc::no_unique_address]] A a1;
+ [[msvc::no_unique_address]] A a2;
+ char f;
+ };
+ static_assert(sizeof(F) == 2);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::F
+ // CHECK-NEXT: 0 | struct Empty::A a1 (empty)
+ // CHECK-NEXT: 1 | struct Empty::A a2 (empty)
+ // CHECK-NEXT: 0 | char f
+ // CHECK-NEXT: | [sizeof=2, align=1,
+ // CHECK-NEXT: | nvsize=2, nvalign=1]
+
+ struct G { [[msvc::no_unique_address]] A a; ~G(); };
+ static_assert(sizeof(G) == 1);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::G
+ // CHECK-NEXT: 0 | struct Empty::A a (empty)
+ // CHECK-NEXT: | [sizeof=1, align=1,
+ // CHECK-NEXT: | nvsize=1, nvalign=1]
+
+ struct H {
+ [[msvc::no_unique_address]] A a;
+ [[msvc::no_unique_address]] A b;
+ ~H();
+ };
+ static_assert(sizeof(H) == 2);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::H
+ // CHECK-NEXT: 0 | struct Empty::A a (empty)
+ // CHECK-NEXT: 1 | struct Empty::A b (empty)
+ // CHECK-NEXT: | [sizeof=2, align=1,
+ // CHECK-NEXT: | nvsize=2, nvalign=1]
+
+ struct I {
+ [[msvc::no_unique_address]] A4 a;
+ [[msvc::no_unique_address]] A4 b;
+ };
+ static_assert(sizeof(I) == 16);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::I
+ // CHECK-NEXT: 0 | struct Empty::A4 a (empty)
+ // CHECK-NEXT: 8 | struct Empty::A4 b (empty)
+ // CHECK-NEXT: | [sizeof=16, align=8,
+ // CHECK-NEXT: | nvsize=16, nvalign=8]
+
+ struct J {
+ [[msvc::no_unique_address]] A4 a;
+ A4 b;
+ };
+ static_assert(sizeof(J) == 16);
+
+ // MSVC puts a and b at the same offset.
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::J
+ // CHECK-NEXT: 0 | struct Empty::A4 a (empty)
+ // CHECK-NEXT: 8 | struct Empty::A4 b (empty)
+ // CHECK-NEXT: | [sizeof=16, align=8,
+ // CHECK-NEXT: | nvsize=16, nvalign=8]
+
+ struct K {
+ [[msvc::no_unique_address]] A4 a;
+ [[msvc::no_unique_address]] char c;
+ [[msvc::no_unique_address]] A4 b;
+ };
+ static_assert(sizeof(K) == 16);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::K
+ // CHECK-NEXT: 0 | struct Empty::A4 a (empty)
+ // CHECK-NEXT: 0 | char c
+ // CHECK-NEXT: 8 | struct Empty::A4 b (empty)
+ // CHECK-NEXT: | [sizeof=16, align=8,
+ // CHECK-NEXT: | nvsize=16, nvalign=8]
+
+ struct OversizedEmpty : A {
+ ~OversizedEmpty();
+ [[msvc::no_unique_address]] A a;
+ };
+ static_assert(sizeof(OversizedEmpty) == 1);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::OversizedEmpty
+ // CHECK-NEXT: 0 | struct Empty::A (base) (empty)
+ // CHECK-NEXT: 0 | struct Empty::A a (empty)
+ // CHECK-NEXT: | [sizeof=1, align=1,
+ // CHECK-NEXT: | nvsize=1, nvalign=1]
+
+ struct HasOversizedEmpty {
+ [[msvc::no_unique_address]] OversizedEmpty m;
+ };
+ static_assert(sizeof(HasOversizedEmpty) == 1);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::HasOversizedEmpty
+ // CHECK-NEXT: 0 | struct Empty::OversizedEmpty m (empty)
+ // CHECK-NEXT: 0 | struct Empty::A (base) (empty)
+ // CHECK-NEXT: 0 | struct Empty::A a (empty)
+ // CHECK-NEXT: | [sizeof=1, align=1,
+ // CHECK-NEXT: | nvsize=1, nvalign=1]
+
+ struct EmptyWithNonzeroDSize {
+ [[msvc::no_unique_address]] A a;
+ int x;
+ [[msvc::no_unique_address]] A b;
+ int y;
+ [[msvc::no_unique_address]] A c;
+ };
+ static_assert(sizeof(EmptyWithNonzeroDSize) == 8);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::EmptyWithNonzeroDSize
+ // CHECK-NEXT: 0 | struct Empty::A a (empty)
+ // CHECK-NEXT: 0 | int x
+ // CHECK-NEXT: 1 | struct Empty::A b (empty)
+ // CHECK-NEXT: 4 | int y
+ // CHECK-NEXT: 2 | struct Empty::A c (empty)
+ // CHECK-NEXT: | [sizeof=8, align=4,
+ // CHECK-NEXT: | nvsize=8, nvalign=4]
+
+ struct EmptyWithNonzeroDSizeNonPOD {
+ ~EmptyWithNonzeroDSizeNonPOD();
+ [[msvc::no_unique_address]] A a;
+ int x;
+ [[msvc::no_unique_address]] A b;
+ int y;
+ [[msvc::no_unique_address]] A c;
+ };
+ static_assert(sizeof(EmptyWithNonzeroDSizeNonPOD) == 8);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct Empty::EmptyWithNonzeroDSizeNonPOD
+ // CHECK-NEXT: 0 | struct Empty::A a (empty)
+ // CHECK-NEXT: 0 | int x
+ // CHECK-NEXT: 1 | struct Empty::A b (empty)
+ // CHECK-NEXT: 4 | int y
+ // CHECK-NEXT: 2 | struct Empty::A c (empty)
+ // CHECK-NEXT: | [sizeof=8, align=4,
+ // CHECK-NEXT: | nvsize=8, nvalign=4]
+}
+
+namespace POD {
+ struct A { int n; char c[3]; };
+ struct B { [[msvc::no_unique_address]] A a; char d; };
+ static_assert(sizeof(B) == 12);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct POD::B
+ // CHECK-NEXT: 0 | struct POD::A a
+ // CHECK-NEXT: 0 | int n
+ // CHECK-NEXT: 4 | char[3] c
+ // CHECK-NEXT: 8 | char d
+ // CHECK-NEXT: | [sizeof=12, align=4,
+ // CHECK-NEXT: | nvsize=12, nvalign=4]
+}
+
+namespace NonPOD {
+ struct A { int n; char c[3]; ~A(); };
+ struct B { [[msvc::no_unique_address]] A a; char d; };
+ static_assert(sizeof(B) == 12);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct NonPOD::B
+ // CHECK-NEXT: 0 | struct NonPOD::A a
+ // CHECK-NEXT: 0 | int n
+ // CHECK-NEXT: 4 | char[3] c
+ // CHECK-NEXT: 8 | char d
+ // CHECK-NEXT: | [sizeof=12, align=4,
+ // CHECK-NEXT: | nvsize=12, nvalign=4]
+}
+
+namespace VBases {
+ // The nvsize of an object includes the complete size of its empty subobjects
+ // (although it's unclear why). Ensure this corner case is handled properly.
+ struct Empty {};
+ struct alignas(8) A {}; // dsize 0, nvsize 0, size 8
+ struct B : A { char c; }; // dsize 1, nvsize 8, size 8
+ static_assert(sizeof(B) == 8);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct VBases::B
+ // CHECK-NEXT: 0 | struct VBases::A (base) (empty)
+ // CHECK-NEXT: 0 | char c
+ // CHECK-NEXT: | [sizeof=8, align=8,
+ // CHECK-NEXT: | nvsize=8, nvalign=8]
+
+ struct V { int n; };
+
+ struct C : B, virtual V {};
+ static_assert(sizeof(C) == 24);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct VBases::C
+ // CHECK-NEXT: 0 | struct VBases::B (base)
+ // CHECK-NEXT: 0 | struct VBases::A (base) (empty)
+ // CHECK-NEXT: 0 | char c
+ // CHECK-NEXT: 8 | (C vbtable pointer)
+ // CHECK-NEXT: 16 | struct VBases::V (virtual base)
+ // CHECK-NEXT: 16 | int n
+ // CHECK-NEXT: | [sizeof=24, align=8,
+ // CHECK-NEXT: | nvsize=16, nvalign=8]
+
+ struct D : virtual Empty {
+ [[msvc::no_unique_address]] Empty a;
+ };
+ static_assert(sizeof(D) == 16);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct VBases::D
+ // CHECK-NEXT: 0 | (D vbtable pointer)
+ // CHECK-NEXT: 8 | struct VBases::Empty a
+ // CHECK-NEXT: 16 | struct VBases::Empty (virtual base) (empty)
+ // CHECK-NEXT: | [sizeof=16, align=8,
+ // CHECK-NEXT: | nvsize=16, nvalign=8]
+
+ struct E : virtual V {
+ [[msvc::no_unique_address]] B b;
+ };
+ static_assert(sizeof(E) == 24);
+
+ // CHECK:*** Dumping AST Record Layout
+ // CHECK: 0 | struct VBases::E
+ // CHECK-NEXT: 0 | (E vbtable pointer)
+ // CHECK-NEXT: 8 | struct VBases::B b
+ // CHECK-NEXT: 8 | struct VBases::A (base) (empty)
+ // CHECK-NEXT: 8 | char c
+ // CHECK-NEXT: 16 | struct VBases::V (virtual base)
+ // CHECK-NEXT: 16 | int n
+ // CHECK-NEXT: | [sizeof=24, align=8,
+ // CHECK-NEXT: | nvsize=16, nvalign=8]
+
+ struct X : virtual A { [[msvc::no_unique_address]] A a; };
+ struct F : virtual ...
[truncated]
|
Previous commit attempt caused sphinx errors. Also, because we now have two attributes for the itanium and MSVC versions, there are two separate sections in the documentation, which I didn't realize before. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks awesome, thanks :)
(Please remember to update the commit message to describe the tablegen change when merging.) |
hmm this passes most of the checks in SemaCXX/cxx2a-no-unique-address.cpp, but fails "attribute 'no_unique_address' cannot have an argument list". Also fails Preprocessor/has_attribute.cpp because both attributes are recognized on itanium and windows targets. |
…ch; also clean up code
Added a section for target specific spellings in |
Thanks @amykhuang! 🎉 |
…7199) This implements the [[msvc::no_unique_address]] attribute. There is not ABI compatibility in this patch because the attribute is relatively new and there's still some uncertainty in the MSVC version. The recommit changes the attribute definitions so that instead of making two separate attributes for no_unique_address and msvc::no_unique_address, it modifies the attributes tablegen emitter to allow spellings to be target-specific. This reverts commit 71f9e76.
This implements the [[msvc::no_unique_address]] attribute.
There is not ABI compatibility in this patch because the attribute is relatively new and there's still some uncertainty in the MSVC version.
The recommit changes the attribute definitions so that instead of making two separate attributes for no_unique_address
and msvc::no_unique_address, it modifies the attributes tablegen emitter to allow spellings to be target-specific.
This reverts commit 71f9e76.