diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2958,6 +2958,9 @@ /// Determines whether this is an unnamed bitfield. bool isUnnamedBitfield() const { return isBitField() && !getDeclName(); } + /// Determins whether this field has no unique address + bool hasNoUniqueAddress() const; + /// Determines whether this field is a /// representative for an anonymous struct or union. Such fields are /// unnamed and are implicitly generated by the implementation to diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3494,6 +3494,14 @@ let Documentation = [MSConstexprDocs]; } +def MSNoUniqueAddress : InheritableAttr, TargetSpecificAttr { + let LangOpts = [MicrosoftExt]; + let Spellings = [CXX11<"msvc", "no_unique_address", 201803>]; + let Subjects = SubjectList<[NonBitField], ErrorDiag>; + let Documentation = [NoUniqueAddressDocs]; + let SimpleHandler = 1; +} + def MSNoVTable : InheritableAttr, TargetSpecificAttr { let Spellings = [Declspec<"novtable">]; let Subjects = SubjectList<[CXXRecord]>; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4268,6 +4268,10 @@ nullptr, false, ICIS_NoInit); } +bool FieldDecl::hasNoUniqueAddress() const { + return hasAttr() || hasAttr(); +} + bool FieldDecl::isAnonymousStructOrUnion() const { if (!isImplicit() || getDeclName()) return false; @@ -4295,7 +4299,7 @@ // C++2a [intro.object]p7: // An object has nonzero size if it // -- is not a potentially-overlapping subobject, or - if (!hasAttr()) + if (!hasNoUniqueAddress()) return false; // -- is not of class type, or diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -474,7 +474,7 @@ // We are able to place the member variable at this offset. // Make sure to update the empty field subobject map. - UpdateEmptyFieldSubobjects(FD, Offset, FD->hasAttr()); + UpdateEmptyFieldSubobjects(FD, Offset, FD->hasNoUniqueAddress()); return true; } @@ -1853,7 +1853,7 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, bool InsertExtraPadding) { auto *FieldClass = D->getType()->getAsCXXRecordDecl(); - bool PotentiallyOverlapping = D->hasAttr() && FieldClass; + bool PotentiallyOverlapping = D->hasNoUniqueAddress() && FieldClass; bool IsOverlappingEmptyField = PotentiallyOverlapping && FieldClass->isEmpty(); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -2015,7 +2015,7 @@ AggValueSlot::Overlap_t CodeGenFunction::getOverlapForFieldInit(const FieldDecl *FD) { - if (!FD->hasAttr() || !FD->getType()->isRecordType()) + if (!FD->hasNoUniqueAddress() || !FD->getType()->isRecordType()) return AggValueSlot::DoesNotOverlap; // If the field lies entirely within the enclosing class's nvsize, its tail diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -755,7 +755,7 @@ return false; // After emitting a non-empty field with [[no_unique_address]], we may // need to overwrite its tail padding. - if (Field->hasAttr()) + if (Field->hasNoUniqueAddress()) AllowOverwrite = true; } else { // Otherwise we have a bitfield. @@ -856,7 +856,7 @@ return false; // After emitting a non-empty field with [[no_unique_address]], we may // need to overwrite its tail padding. - if (Field->hasAttr()) + if (Field->hasNoUniqueAddress()) AllowOverwrite = true; } else { // Otherwise we have a bitfield. diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -744,7 +744,7 @@ Prior->Data = getByteArrayType(bitsToCharUnits(llvm::alignTo( cast(Prior->Data)->getIntegerBitWidth(), 8))); else { - assert(Prior->FD->hasAttr() && + assert(Prior->FD->hasNoUniqueAddress() && "should not have reused this field's tail padding"); Prior->Data = getByteArrayType( Context.getTypeInfoDataSizeInChars(Prior->FD->getType()).Width); diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -578,8 +578,7 @@ // according to the Itanium ABI. The exception applies only to records, // not arrays of records, so we must also check whether we stripped off an // array type above. - if (isa(RT->getDecl()) && - (WasArray || !FD->hasAttr())) + if (isa(RT->getDecl()) && (WasArray || !FD->hasNoUniqueAddress())) return false; return isEmptyRecord(Context, FT, AllowArrays); @@ -7524,8 +7523,7 @@ // do count. So do anonymous bitfields that aren't zero-sized. // Like isSingleElementStruct(), ignore C++20 empty data members. - if (FD->hasAttr() && - isEmptyRecord(getContext(), FD->getType(), true)) + if (FD->hasNoUniqueAddress() && isEmptyRecord(getContext(), FD->getType(), true)) continue; // Unlike isSingleElementStruct(), arrays do not count. diff --git a/clang/test/AST/msvc-attrs.cpp b/clang/test/AST/msvc-attrs.cpp --- a/clang/test/AST/msvc-attrs.cpp +++ b/clang/test/AST/msvc-attrs.cpp @@ -1,6 +1,14 @@ // RUN: %clang_cc1 -fms-extensions -std=c++20 -ast-dump %s | FileCheck %s -// RUN: not %clang_cc1 -Werror=ignored-attributes -ast-dump %s 2>&1 | grep "1 error generated" +// RUN: not %clang_cc1 -Werror=ignored-attributes -ast-dump %s 2>&1 | grep "3 errors generated" -// CHECK: msvc-attrs.cpp:[[@LINE+2]]:21, col:61> col:27 constexpr New1 'void *(void *)' +struct Empty {}; + +// CHECK: col:27 constexpr New1 'void *(void *)' // CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} [[msvc::constexpr]] void *New1(void *where) { return where; } + +struct StructWithNUAField { + [[msvc::no_unique_address]] Empty e; + int f1; +}; +static_assert(sizeof(StructWithNUAField) == sizeof(int)); // expected to fail without -fms-extensions