diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index cf5be0c3219a63..591fa87d1878b6 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2633,16 +2633,66 @@ static bool unionHasUniqueObjectRepresentations(const ASTContext &Context, return !RD->field_empty(); } -static bool isStructEmpty(QualType Ty) { - const RecordDecl *RD = Ty->castAs()->getDecl(); +static int64_t getSubobjectOffset(const FieldDecl *Field, + const ASTContext &Context, + const clang::ASTRecordLayout & /*Layout*/) { + return Context.getFieldOffset(Field); +} - if (!RD->field_empty()) - return false; +static int64_t getSubobjectOffset(const CXXRecordDecl *RD, + const ASTContext &Context, + const clang::ASTRecordLayout &Layout) { + return Context.toBits(Layout.getBaseClassOffset(RD)); +} - if (const auto *ClassDecl = dyn_cast(RD)) - return ClassDecl->isEmpty(); +static llvm::Optional +structHasUniqueObjectRepresentations(const ASTContext &Context, + const RecordDecl *RD); - return true; +static llvm::Optional +getSubobjectSizeInBits(const FieldDecl *Field, const ASTContext &Context) { + if (Field->getType()->isRecordType()) { + const RecordDecl *RD = Field->getType()->getAsRecordDecl(); + if (!RD->isUnion()) + return structHasUniqueObjectRepresentations(Context, RD); + } + if (!Field->getType()->isReferenceType() && + !Context.hasUniqueObjectRepresentations(Field->getType())) + return llvm::None; + + int64_t FieldSizeInBits = + Context.toBits(Context.getTypeSizeInChars(Field->getType())); + if (Field->isBitField()) { + int64_t BitfieldSize = Field->getBitWidthValue(Context); + if (BitfieldSize > FieldSizeInBits) + return llvm::None; + FieldSizeInBits = BitfieldSize; + } + return FieldSizeInBits; +} + +static llvm::Optional +getSubobjectSizeInBits(const CXXRecordDecl *RD, const ASTContext &Context) { + return structHasUniqueObjectRepresentations(Context, RD); +} + +template +static llvm::Optional structSubobjectsHaveUniqueObjectRepresentations( + const RangeT &Subobjects, int64_t CurOffsetInBits, + const ASTContext &Context, const clang::ASTRecordLayout &Layout) { + for (const auto *Subobject : Subobjects) { + llvm::Optional SizeInBits = + getSubobjectSizeInBits(Subobject, Context); + if (!SizeInBits) + return llvm::None; + if (*SizeInBits != 0) { + int64_t Offset = getSubobjectOffset(Subobject, Context, Layout); + if (Offset != CurOffsetInBits) + return llvm::None; + CurOffsetInBits += *SizeInBits; + } + } + return CurOffsetInBits; } static llvm::Optional @@ -2656,58 +2706,32 @@ structHasUniqueObjectRepresentations(const ASTContext &Context, if (ClassDecl->isDynamicClass()) return llvm::None; - SmallVector, 4> Bases; + SmallVector Bases; for (const auto &Base : ClassDecl->bases()) { // Empty types can be inherited from, and non-empty types can potentially // have tail padding, so just make sure there isn't an error. - if (!isStructEmpty(Base.getType())) { - llvm::Optional Size = structHasUniqueObjectRepresentations( - Context, Base.getType()->castAs()->getDecl()); - if (!Size) - return llvm::None; - Bases.emplace_back(Base.getType(), Size.getValue()); - } + Bases.emplace_back(Base.getType()->getAsCXXRecordDecl()); } - llvm::sort(Bases, [&](const std::pair &L, - const std::pair &R) { - return Layout.getBaseClassOffset(L.first->getAsCXXRecordDecl()) < - Layout.getBaseClassOffset(R.first->getAsCXXRecordDecl()); + llvm::sort(Bases, [&](const CXXRecordDecl *L, const CXXRecordDecl *R) { + return Layout.getBaseClassOffset(L) < Layout.getBaseClassOffset(R); }); - for (const auto &Base : Bases) { - int64_t BaseOffset = Context.toBits( - Layout.getBaseClassOffset(Base.first->getAsCXXRecordDecl())); - int64_t BaseSize = Base.second; - if (BaseOffset != CurOffsetInBits) - return llvm::None; - CurOffsetInBits = BaseOffset + BaseSize; - } - } - - for (const auto *Field : RD->fields()) { - if (!Field->getType()->isReferenceType() && - !Context.hasUniqueObjectRepresentations(Field->getType())) - return llvm::None; - - int64_t FieldSizeInBits = - Context.toBits(Context.getTypeSizeInChars(Field->getType())); - if (Field->isBitField()) { - int64_t BitfieldSize = Field->getBitWidthValue(Context); - - if (BitfieldSize > FieldSizeInBits) - return llvm::None; - FieldSizeInBits = BitfieldSize; - } - - int64_t FieldOffsetInBits = Context.getFieldOffset(Field); - - if (FieldOffsetInBits != CurOffsetInBits) + llvm::Optional OffsetAfterBases = + structSubobjectsHaveUniqueObjectRepresentations(Bases, CurOffsetInBits, + Context, Layout); + if (!OffsetAfterBases) return llvm::None; - - CurOffsetInBits = FieldSizeInBits + FieldOffsetInBits; + CurOffsetInBits = *OffsetAfterBases; } + llvm::Optional OffsetAfterFields = + structSubobjectsHaveUniqueObjectRepresentations( + RD->fields(), CurOffsetInBits, Context, Layout); + if (!OffsetAfterFields) + return llvm::None; + CurOffsetInBits = *OffsetAfterFields; + return CurOffsetInBits; } diff --git a/clang/test/SemaCXX/has_unique_object_reps_no_unique_addr.cpp b/clang/test/SemaCXX/has_unique_object_reps_no_unique_addr.cpp new file mode 100644 index 00000000000000..a64ca8277a1ebe --- /dev/null +++ b/clang/test/SemaCXX/has_unique_object_reps_no_unique_addr.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify -std=c++2a %s +// expected-no-diagnostics + +struct Empty {}; + +struct A { + [[no_unique_address]] Empty e; + char x; +}; + +static_assert(__has_unique_object_representations(A)); + +struct B { + char x; + [[no_unique_address]] Empty e; +}; + +static_assert(__has_unique_object_representations(B)); + +struct C { + char x; + [[no_unique_address]] Empty e1; + [[no_unique_address]] Empty e2; +}; + +static_assert(!__has_unique_object_representations(C)); + +namespace TailPaddingReuse { +struct A { +private: + int a; + +public: + char b; +}; + +struct B { + [[no_unique_address]] A a; + char c[3]; +}; +} // namespace TailPaddingReuse +static_assert(__has_unique_object_representations(TailPaddingReuse::B));