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 @@ -3487,6 +3487,21 @@ // Microsoft-related attributes +def MSConstexpr : DeclOrTypeAttr { + let LangOpts = [MicrosoftExt]; + let Spellings = [CXX11<"msvc", "constexpr">]; +// let Subjects = [Function]; + 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/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3478,6 +3478,14 @@ }]; } +def MSConstexprDocs : Documentation { + let Category = DocCatStmt; + let Content = [{ +This attribute is an alias of ``constexpr`` for functions. +Available only as Microsoft extension (``-fms-extensions``). + }]; +} + def MSNoVTableDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3363,6 +3363,9 @@ def warn_attribute_ignored_on_non_definition : Warning<"%0 attribute ignored on a non-definition declaration">, InGroup; +def warn_attribute_ignored_on_non_function : + Warning<"%0 attribute ignored on a non-function">, + InGroup; def warn_attribute_ignored_on_inline : Warning<"%0 attribute ignored on inline function">, InGroup; 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/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4992,6 +4992,9 @@ case ParsedAttr::AT_PreserveAll: D->addAttr(::new (S.Context) PreserveAllAttr(S.Context, AL)); return; + case ParsedAttr::AT_MSConstexpr: + D->addAttr(::new (S.Context) MSConstexprAttr(S.Context, AL)); + return; default: llvm_unreachable("unexpected attribute kind"); } @@ -6975,6 +6978,14 @@ D->addAttr(::new (S.Context) ThreadAttr(S.Context, AL)); } +static void handleMSConstexprAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (auto *FD = dyn_cast(D)) + FD->setConstexprKind(ConstexprSpecKind::Constexpr); + else + S.Diag(AL.getLoc(), diag::warn_attribute_ignored_on_non_function) << AL; + D->addAttr(::new (S.Context) MSConstexprAttr(S.Context, AL)); +} + static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) { SmallVector Tags; for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) { @@ -8912,6 +8923,9 @@ case ParsedAttr::AT_Thread: handleDeclspecThreadAttr(S, D, AL); break; + case ParsedAttr::AT_MSConstexpr: + handleMSConstexprAttr(S, D, AL); + break; // HLSL attributes: case ParsedAttr::AT_HLSLNumThreads: 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,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 "3 errors generated" + +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