Recommit "Implement [[msvc::no_unique_address]] (#65675)" (#67199)

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 71f9e7695b.
This commit is contained in:
Amy Huang
2023-09-28 14:29:32 -07:00
committed by GitHub
parent aa5a1b267a
commit 0faee97a92
12 changed files with 570 additions and 16 deletions

View File

@@ -450,6 +450,12 @@ def TargetELF : TargetSpec {
def TargetSupportsInitPriority : TargetSpec {
let CustomCode = [{ !Target.getTriple().isOSzOS() }];
}
class TargetSpecificSpelling<TargetSpec target, list<Spelling> spellings> {
TargetSpec Target = target;
list<Spelling> Spellings = spellings;
}
// Attribute subject match rules that are used for #pragma clang attribute.
//
// A instance of AttrSubjectMatcherRule represents an individual match rule.
@@ -576,6 +582,8 @@ class Attr {
list<Argument> Args = [];
// Accessors which should be generated for the attribute.
list<Accessor> Accessors = [];
// Specify targets for spellings.
list<TargetSpecificSpelling> TargetSpecificSpellings = [];
// Set to true for attributes with arguments which require delayed parsing.
bit LateParsed = 0;
// Set to false to prevent an attribute from being propagated from a template
@@ -1798,11 +1806,14 @@ 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>;
let Spellings = [CXX11<"", "no_unique_address", 201803>, CXX11<"msvc", "no_unique_address", 201803>];
let TargetSpecificSpellings = [
TargetSpecificSpelling<TargetItaniumCXXABI, [CXX11<"", "no_unique_address", 201803>]>,
TargetSpecificSpelling<TargetMicrosoftCXXABI, [CXX11<"msvc", "no_unique_address", 201803>]>,
];
let Documentation = [NoUniqueAddressDocs];
let SimpleHandler = 1;
}
def ReturnsTwice : InheritableAttr {

View File

@@ -1405,6 +1405,10 @@ Example usage:
``[[no_unique_address]]`` is a standard C++20 attribute. Clang supports its use
in C++11 onwards.
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.
}];
}

View File

@@ -116,6 +116,14 @@ public:
/// Check if this attribute is allowed when compiling for the given target.
virtual bool existsInTarget(const TargetInfo &Target) const { return true; }
/// Check if this attribute's spelling is allowed when compiling for the given
/// target.
virtual bool spellingExistsInTarget(const TargetInfo &Target,
const unsigned SpellingListIndex) const {
return true;
}
/// Convert the spelling index of Attr to a semantic spelling enum value.
virtual unsigned
spellingIndexToSemanticSpelling(const ParsedAttr &Attr) const {

View File

@@ -4513,9 +4513,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 {

View File

@@ -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 @@ public:
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,

View File

@@ -4466,6 +4466,14 @@ bool Parser::ParseCXX11AttributeArgs(
if (!Attrs.empty() &&
IsBuiltInOrStandardCXX11Attribute(AttrName, ScopeName)) {
ParsedAttr &Attr = Attrs.back();
// Ignore attributes that don't exist for the target.
if (!Attr.existsInTarget(getTargetInfo())) {
Diag(LParenLoc, diag::warn_unknown_attribute_ignored) << AttrName;
Attr.setInvalid(true);
return true;
}
// If the attribute is a standard or built-in attribute and we are
// parsing an argument list, we need to determine whether this attribute
// was allowed to have an argument list (such as [[deprecated]]), and how

View File

@@ -193,7 +193,9 @@ bool ParsedAttr::isTypeAttr() const { return getInfo().IsType; }
bool ParsedAttr::isStmtAttr() const { return getInfo().IsStmt; }
bool ParsedAttr::existsInTarget(const TargetInfo &Target) const {
return getInfo().existsInTarget(Target);
return getInfo().existsInTarget(Target) &&
getInfo().spellingExistsInTarget(Target,
getAttributeSpellingListIndex());
}
bool ParsedAttr::isKnownToGCC() const { return getInfo().IsKnownToGCC; }

View File

@@ -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,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_NoMerge:
handleNoMergeAttr(S, D, AL);
break;
case ParsedAttr::AT_NoUniqueAddress:
handleNoUniqueAddressAttr(S, D, AL);
break;
case ParsedAttr::AT_AvailableOnlyInDefaultEvalMethod:
handleAvailableOnlyInDefaultEvalMethod(S, D, AL);

View File

@@ -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 A {
[[msvc::no_unique_address]] A a;
[[msvc::no_unique_address]] X x;
};
static_assert(sizeof(F) == 24);
// MSVC places x after a and the total size is 48.
// CHECK:*** Dumping AST Record Layout
// CHECK: 0 | struct VBases::F
// CHECK-NEXT: 0 | (F vbtable pointer)
// CHECK-NEXT: 8 | struct VBases::A a (empty)
// CHECK-NEXT: 8 | struct VBases::X x
// CHECK-NEXT: 8 | (X vbtable pointer)
// CHECK-NEXT: 16 | struct VBases::A a (empty)
// CHECK-NEXT: 24 | struct VBases::A (virtual base) (empty)
// CHECK-NEXT: 24 | struct VBases::A (virtual base) (empty)
// CHECK-NEXT: | [sizeof=24, align=8,
// CHECK-NEXT: | nvsize=24, nvalign=8]
struct G : virtual Empty {
int i;
[[msvc::no_unique_address]] A a;
};
static_assert(sizeof(G) == 16);
// MSVC places a at offset 12.
// CHECK:*** Dumping AST Record Layout
// CHECK: 0 | struct VBases::G
// CHECK-NEXT: 0 | (G vbtable pointer)
// CHECK-NEXT: 8 | int i
// CHECK-NEXT: 8 | struct VBases::A a (empty)
// CHECK-NEXT: 16 | struct VBases::Empty (virtual base) (empty)
// CHECK-NEXT: | [sizeof=16, align=8,
// CHECK-NEXT: | nvsize=16, nvalign=8]
}
namespace ZeroSize {
struct empty {};
union empty_union {};
struct empty_union_container {
[[msvc::no_unique_address]] empty_union x;
};
union union_of_empty {
[[msvc::no_unique_address]] empty x;
};
struct struct_of_empty {
[[msvc::no_unique_address]] empty x;
};
struct union_of_empty_container {
[[msvc::no_unique_address]] union_of_empty x;
};
static_assert(sizeof(union_of_empty_container) == 1);
// CHECK:*** Dumping AST Record Layout
// CHECK: 0 | struct ZeroSize::union_of_empty_container
// CHECK-NOT: (empty)
// CHECK: 0 | union ZeroSize::union_of_empty x (empty)
// CHECK: 0 | struct ZeroSize::empty x (empty)
// CHECK: | [sizeof=1, align=1,
// CHECK: | nvsize=1, nvalign=1]
struct struct_of_empty_container {
[[msvc::no_unique_address]] struct_of_empty x;
};
static_assert(sizeof(struct_of_empty_container) == 1);
// CHECK:*** Dumping AST Record Layout
// CHECK: 0 | struct ZeroSize::struct_of_empty_container
// CHECK-NOT: (empty)
// CHECK: 0 | struct ZeroSize::struct_of_empty x (empty)
// CHECK: 0 | struct ZeroSize::empty x (empty)
// CHECK: | [sizeof=1, align=1,
// CHECK: | nvsize=1, nvalign=1]
}

View File

@@ -43,6 +43,7 @@ CXX11(fallthrough)
CXX11(likely)
CXX11(maybe_unused)
CXX11(no_unique_address)
CXX11(msvc::no_unique_address)
CXX11(nodiscard)
CXX11(noreturn)
CXX11(unlikely)
@@ -55,7 +56,9 @@ CXX11(unlikely)
// CHECK: likely: 201803L
// CHECK: maybe_unused: 201603L
// ITANIUM: no_unique_address: 201803L
// WINDOWS: no_unique_address: 0
// WINDOWS: no_unique_address: 0
// ITANIUM: msvc::no_unique_address: 0
// WINDOWS: msvc::no_unique_address: 201803L
// CHECK: nodiscard: 201907L
// CHECK: noreturn: 200809L
// CHECK: unlikely: 201803L

View File

@@ -0,0 +1,19 @@
// RUN: %clang_cc1 -std=c++2a %s -verify=unsupported -triple x86_64-linux-gnu
// RUN: %clang_cc1 -std=c++2a %s -verify -triple x86_64-windows -fms-compatibility
[[msvc::no_unique_address]] int a; // expected-error {{only applies to non-bit-field non-static data members}} unsupported-warning {{unknown}}
[[msvc::no_unique_address]] void f(); // expected-error {{only applies to non-bit-field non-static data members}} unsupported-warning {{unknown}}
struct [[msvc::no_unique_address]] S { // expected-error {{only applies to non-bit-field non-static data members}} unsupported-warning {{unknown}}
[[msvc::no_unique_address]] int a; // unsupported-warning {{unknown}}
[[msvc::no_unique_address]] void f(); // expected-error {{only applies to non-bit-field non-static data members}} unsupported-warning {{unknown}}
[[msvc::no_unique_address]] static int sa;// expected-error {{only applies to non-bit-field non-static data members}} unsupported-warning {{unknown}}
[[msvc::no_unique_address]] static void sf(); // expected-error {{only applies to non-bit-field non-static data members}} unsupported-warning {{unknown}}
[[msvc::no_unique_address]] int b : 3; // expected-error {{only applies to non-bit-field non-static data members}} unsupported-warning {{unknown}}
[[msvc::no_unique_address, msvc::no_unique_address]] int duplicated; // ok
// unsupported-warning@-1 2{{unknown}}
[[msvc::no_unique_address]] [[msvc::no_unique_address]] int duplicated2; // unsupported-warning 2{{unknown}}
[[msvc::no_unique_address()]] int arglist; // expected-error {{cannot have an argument list}} unsupported-warning {{unknown}}
int [[msvc::no_unique_address]] c; // expected-error {{cannot be applied to types}} unsupported-error {{cannot be applied to types}}
};

View File

@@ -3451,9 +3451,11 @@ static void GenerateHasAttrSpellingStringSwitch(
int Version = 1;
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr);
std::string Name = "";
for (const auto &Spelling : Spellings) {
if (Spelling.variety() == Variety &&
(Spelling.nameSpace().empty() || Scope == Spelling.nameSpace())) {
Name = Spelling.name();
Version = static_cast<int>(
Spelling.getSpellingRecord().getValueAsInt("Version"));
// Verify that explicitly specified CXX11 and C23 spellings (i.e.
@@ -3477,6 +3479,26 @@ static void GenerateHasAttrSpellingStringSwitch(
GenerateTargetSpecificAttrChecks(R, Arches, Test, nullptr);
// If this is the C++11 variety, also add in the LangOpts test.
if (Variety == "CXX11")
Test += " && LangOpts.CPlusPlus11";
} else if (!Attr->getValueAsListOfDefs("TargetSpecificSpellings").empty()) {
// Add target checks if this spelling is target-specific.
const std::vector<Record *> TargetSpellings =
Attr->getValueAsListOfDefs("TargetSpecificSpellings");
for (const auto &TargetSpelling : TargetSpellings) {
// Find spelling that matches current scope and name.
for (const auto &Spelling : GetFlattenedSpellings(*TargetSpelling)) {
if (Scope == Spelling.nameSpace() && Name == Spelling.name()) {
const Record *Target = TargetSpelling->getValueAsDef("Target");
std::vector<StringRef> Arches =
Target->getValueAsListOfStrings("Arches");
GenerateTargetSpecificAttrChecks(Target, Arches, Test,
/*FnName=*/nullptr);
break;
}
}
}
if (Variety == "CXX11")
Test += " && LangOpts.CPlusPlus11";
} else if (Variety == "CXX11")
@@ -4267,6 +4289,51 @@ static void GenerateTargetRequirements(const Record &Attr,
OS << "}\n\n";
}
static void
GenerateSpellingTargetRequirements(const Record &Attr,
const std::vector<Record *> &TargetSpellings,
raw_ostream &OS) {
// If there are no target specific spellings, use the default target handler.
if (TargetSpellings.empty())
return;
std::string Test;
bool UsesT = false;
const std::vector<FlattenedSpelling> SpellingList =
GetFlattenedSpellings(Attr);
for (unsigned TargetIndex = 0; TargetIndex < TargetSpellings.size();
++TargetIndex) {
const auto &TargetSpelling = TargetSpellings[TargetIndex];
std::vector<FlattenedSpelling> Spellings =
GetFlattenedSpellings(*TargetSpelling);
Test += "((SpellingListIndex == ";
for (unsigned Index = 0; Index < Spellings.size(); ++Index) {
Test +=
llvm::itostr(getSpellingListIndex(SpellingList, Spellings[Index]));
if (Index != Spellings.size() - 1)
Test += " ||\n SpellingListIndex == ";
else
Test += ") && ";
}
const Record *Target = TargetSpelling->getValueAsDef("Target");
std::vector<StringRef> Arches = Target->getValueAsListOfStrings("Arches");
std::string FnName = "isTargetSpelling";
UsesT |= GenerateTargetSpecificAttrChecks(Target, Arches, Test, &FnName);
Test += ")";
if (TargetIndex != TargetSpellings.size() - 1)
Test += " || ";
}
OS << "bool spellingExistsInTarget(const TargetInfo &Target,\n";
OS << " const unsigned SpellingListIndex) const "
"override {\n";
if (UsesT)
OS << " const llvm::Triple &T = Target.getTriple(); (void)T;\n";
OS << " return " << Test << ";\n", OS << "}\n\n";
}
static void GenerateSpellingIndexToSemanticSpelling(const Record &Attr,
raw_ostream &OS) {
// If the attribute does not have a semantic form, we can bail out early.
@@ -4481,6 +4548,8 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
GenerateMutualExclusionsChecks(Attr, Records, OS, MergeDeclOS, MergeStmtOS);
GenerateLangOptRequirements(Attr, OS);
GenerateTargetRequirements(Attr, Dupes, OS);
GenerateSpellingTargetRequirements(
Attr, Attr.getValueAsListOfDefs("TargetSpecificSpellings"), OS);
GenerateSpellingIndexToSemanticSpelling(Attr, OS);
PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS);
GenerateHandleDeclAttribute(Attr, OS);