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 @@ -2971,6 +2971,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 @@ -3500,6 +3500,14 @@ // Microsoft-related attributes +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 @@ -4269,6 +4269,10 @@ nullptr, false, ICIS_NoInit); } +bool FieldDecl::hasNoUniqueAddress() const { + return hasAttr() || hasAttr(); +} + bool FieldDecl::isAnonymousStructOrUnion() const { if (!isImplicit() || getDeclName()) return false; @@ -4296,7 +4300,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(); @@ -2694,6 +2694,8 @@ } // Capture required alignment as a side-effect. RequiredAlignment = std::max(RequiredAlignment, FieldRequiredAlignment); + if (auto *FC = FD->getType()->getAsCXXRecordDecl(); FC && FD->hasNoUniqueAddress() && FC->isEmpty()) + Info.Size = CharUnits::Zero(); } // Respect pragma pack, attribute pack and declspec align if (!MaxFieldAlignment.isZero()) 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); @@ -7523,8 +7522,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 new file mode 100644 --- /dev/null +++ b/clang/test/AST/msvc-attrs.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fms-extensions -fc++-abi=microsoft -triple=x86_64-pc-windows-msvc -std=c++20 -ast-dump %s | FileCheck %s +// RUN: not %clang_cc1 -Werror=ignored-attributes -ast-dump %s 2> %t.stderr.txt +// RUN: FileCheck -check-prefix CHECK-DIAG-NO-MSX %s < %t.stderr.txt + +struct Empty {}; + +struct StructWithNUAField { + // CHECK: FieldDecl 0x{{[0-9a-f]+}} col:37 e 'Empty':'Empty' + // CHECK-NEXT: MSNoUniqueAddressAttr + [[msvc::no_unique_address]] Empty e; + int f1; +}; + +// CHECK-DIAG-NO-MSX: msvc-attrs.cpp:[[@LINE+1]]:1: error: static assertion failed due to requirement 'sizeof(StructWithNUAField) == sizeof(int)' +static_assert(sizeof(StructWithNUAField) == sizeof(int));