Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2149,6 +2149,10 @@ void CollectInheritedProtocols(const Decl *CDecl, llvm::SmallPtrSet &Protocols); + /// \brief Return true if the specified type has unique object representations + /// according to (C++17 [meta.unary.prop]p9) + bool hasUniqueObjectRepresentations(QualType Ty) const; + //===--------------------------------------------------------------------===// // Type Operators //===--------------------------------------------------------------------===// Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -808,10 +808,6 @@ /// Return true if this is a trivially copyable type (C++0x [basic.types]p9) bool isTriviallyCopyableType(const ASTContext &Context) const; - /// Return true if this has unique object representations according to (C++17 - /// [meta.unary.prop]p9) - bool hasUniqueObjectRepresentations(const ASTContext &Context) const; - // Don't promise in the API that anything besides 'const' can be // easily added. @@ -1164,9 +1160,6 @@ QualType getAtomicUnqualifiedType() const; private: - bool unionHasUniqueObjectRepresentations(const ASTContext& Context) const; - bool structHasUniqueObjectRepresentations(const ASTContext& Context) const; - // These methods are implemented in a separate translation unit; // "static"-ize them to avoid creating temporary QualTypes in the // caller. Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -1856,7 +1856,9 @@ break; case Type::MemberPointer: { const MemberPointerType *MPT = cast(T); - std::tie(Width, Align) = ABI->getMemberPointerWidthAndAlign(MPT); + CXXABI::MemberPointerInfo MPI = ABI->getMemberPointerInfo(MPT); + Width = MPI.Width; + Align = MPI.Align; break; } case Type::Complex: { @@ -2138,6 +2140,169 @@ } } +static bool unionHasUniqueObjectRepresentations(const ASTContext &Context, + const RecordDecl *RD) { + assert(RD->isUnion() && "Must be union type"); + CharUnits UnionSize = Context.getTypeSizeInChars(RD->getTypeForDecl()); + + for (const auto *Field : RD->fields()) { + if (!Context.hasUniqueObjectRepresentations(Field->getType())) + return false; + CharUnits FieldSize = Context.getTypeSizeInChars(Field->getType()); + if (FieldSize != UnionSize) + return false; + } + return true; +} + +bool isStructEmpty(QualType Ty) { + const RecordDecl *RD = Ty->castAs()->getDecl(); + + if (!RD->field_empty()) + return false; + + if (const auto *ClassDecl = dyn_cast(RD)) + return ClassDecl->isEmpty(); + + return true; +} + +static llvm::Optional +structHasUniqueObjectRepresentations(const ASTContext &Context, + const RecordDecl *RD) { + assert(!RD->isUnion() && "Must be struct/class type"); + const auto &Layout = Context.getASTRecordLayout(RD); + + CharUnits CurOffset = CharUnits::Zero(); + if (const auto *ClassDecl = dyn_cast(RD)) { + if (ClassDecl->isDynamicClass()) + return llvm::None; + + 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())) { + if (!structHasUniqueObjectRepresentations( + Context, Base.getType()->getAs()->getDecl())) + return llvm::None; + Bases.push_back(Base.getType()); + } + } + + std::sort(Bases.begin(), Bases.end(), + [&](const QualType &L, const QualType &R) { + return Layout.getBaseClassOffset(L->getAsCXXRecordDecl()) < + Layout.getBaseClassOffset(R->getAsCXXRecordDecl()); + }); + + for (const auto Base : Bases) { + CharUnits BaseOffset = + Layout.getBaseClassOffset(Base->getAsCXXRecordDecl()); + CharUnits BaseSize = + Context.getASTRecordLayout(Base->getAs()->getDecl()) + .getDataSize(); + if (BaseOffset != CurOffset) + return llvm::None; + CurOffset = BaseOffset + BaseSize; + } + } + + int64_t CurOffsetInBits = Context.toBits(CurOffset); + 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) + return llvm::None; + + CurOffsetInBits = FieldSizeInBits + FieldOffsetInBits; + } + + return CurOffsetInBits; +} + +bool ASTContext::hasUniqueObjectRepresentations(QualType Ty) const { + // C++17 [meta.unary.prop]: + // The predicate condition for a template specialization + // has_unique_object_representations shall be + // satisfied if and only if: + // (9.1) - T is trivially copyable, and + // (9.2) - any two objects of type T with the same value have the same + // object representation, where two objects + // of array or non-union class type are considered to have the same value + // if their respective sequences of + // direct subobjects have the same values, and two objects of union type + // are considered to have the same + // value if they have the same active member and the corresponding members + // have the same value. + // The set of scalar types for which this condition holds is + // implementation-defined. [ Note: If a type has padding + // bits, the condition does not hold; otherwise, the condition holds true + // for unsigned integral types. -- end note ] + assert(!Ty.isNull() && "Null QualType sent to unique object rep check"); + + // Arrays are unique only if their element type is unique. + if (Ty->isArrayType()) + return hasUniqueObjectRepresentations(getBaseElementType(Ty)); + + // (9.1) - T is trivially copyable... + if (!Ty.isTriviallyCopyableType(*this)) + return false; + + // All integrals and enums are unique. + if (Ty->isIntegralOrEnumerationType()) + return true; + + // All other pointers are unique. + if (Ty->isPointerType()) + return true; + + if (Ty->isMemberPointerType()) { + const MemberPointerType *MPT = Ty->getAs(); + return !ABI->getMemberPointerInfo(MPT).HasPadding; + } + + if (Ty->isRecordType()) { + const RecordDecl *Record = Ty->getAs()->getDecl(); + + if (Record->isUnion()) + return unionHasUniqueObjectRepresentations(*this, Record); + + Optional StructSize = + structHasUniqueObjectRepresentations(*this, Record); + + return StructSize && + StructSize.getValue() == static_cast(getTypeSize(Ty)); + } + + // FIXME: More cases to handle here (list by rsmith): + // vectors (careful about, eg, vector of 3 foo) + // _Complex int and friends + // _Atomic T + // Obj-C block pointers + // Obj-C object pointers + // and perhaps OpenCL's various builtin types (pipe, sampler_t, event_t, + // clk_event_t, queue_t, reserve_id_t) + // There're also Obj-C class types and the Obj-C selector type, but I think it + // makes sense for those to return false here. + + return false; +} + unsigned ASTContext::CountNonClassIvars(const ObjCInterfaceDecl *OI) const { unsigned count = 0; // Count ivars declared in class extension. Index: lib/AST/CXXABI.h =================================================================== --- lib/AST/CXXABI.h +++ lib/AST/CXXABI.h @@ -31,9 +31,16 @@ public: virtual ~CXXABI(); - /// Returns the width and alignment of a member pointer in bits. - virtual std::pair - getMemberPointerWidthAndAlign(const MemberPointerType *MPT) const = 0; + struct MemberPointerInfo { + uint64_t Width; + unsigned Align; + bool HasPadding; + }; + + /// Returns the width and alignment of a member pointer in bits, as well as + /// whether it has padding. + virtual MemberPointerInfo + getMemberPointerInfo(const MemberPointerType *MPT) const = 0; /// Returns the default calling convention for C++ methods. virtual CallingConv getDefaultMethodCallConv(bool isVariadic) const = 0; Index: lib/AST/ItaniumCXXABI.cpp =================================================================== --- lib/AST/ItaniumCXXABI.cpp +++ lib/AST/ItaniumCXXABI.cpp @@ -101,15 +101,17 @@ public: ItaniumCXXABI(ASTContext &Ctx) : Context(Ctx) { } - std::pair - getMemberPointerWidthAndAlign(const MemberPointerType *MPT) const override { + MemberPointerInfo + getMemberPointerInfo(const MemberPointerType *MPT) const override { const TargetInfo &Target = Context.getTargetInfo(); TargetInfo::IntType PtrDiff = Target.getPtrDiffType(0); - uint64_t Width = Target.getTypeWidth(PtrDiff); - unsigned Align = Target.getTypeAlign(PtrDiff); + MemberPointerInfo MPI; + MPI.Width = Target.getTypeWidth(PtrDiff); + MPI.Align = Target.getTypeAlign(PtrDiff); + MPI.HasPadding = false; if (MPT->isMemberFunctionPointer()) - Width = 2 * Width; - return std::make_pair(Width, Align); + MPI.Width *= 2; + return MPI; } CallingConv getDefaultMethodCallConv(bool isVariadic) const override { Index: lib/AST/MicrosoftCXXABI.cpp =================================================================== --- lib/AST/MicrosoftCXXABI.cpp +++ lib/AST/MicrosoftCXXABI.cpp @@ -76,8 +76,8 @@ public: MicrosoftCXXABI(ASTContext &Ctx) : Context(Ctx) { } - std::pair - getMemberPointerWidthAndAlign(const MemberPointerType *MPT) const override; + MemberPointerInfo + getMemberPointerInfo(const MemberPointerType *MPT) const override; CallingConv getDefaultMethodCallConv(bool isVariadic) const override { if (!isVariadic && @@ -227,7 +227,7 @@ return std::make_pair(Ptrs, Ints); } -std::pair MicrosoftCXXABI::getMemberPointerWidthAndAlign( +CXXABI::MemberPointerInfo MicrosoftCXXABI::getMemberPointerInfo( const MemberPointerType *MPT) const { // The nominal struct is laid out with pointers followed by ints and aligned // to a pointer width if any are present and an int width otherwise. @@ -237,22 +237,25 @@ unsigned Ptrs, Ints; std::tie(Ptrs, Ints) = getMSMemberPointerSlots(MPT); - uint64_t Width = Ptrs * PtrSize + Ints * IntSize; - unsigned Align; + MemberPointerInfo MPI; + MPI.HasPadding = false; + MPI.Width = Ptrs * PtrSize + Ints * IntSize; // When MSVC does x86_32 record layout, it aligns aggregate member pointers to // 8 bytes. However, __alignof usually returns 4 for data memptrs and 8 for // function memptrs. if (Ptrs + Ints > 1 && Target.getTriple().isArch32Bit()) - Align = 64; + MPI.Align = 64; else if (Ptrs) - Align = Target.getPointerAlign(0); + MPI.Align = Target.getPointerAlign(0); else - Align = Target.getIntAlign(); + MPI.Align = Target.getIntAlign(); - if (Target.getTriple().isArch64Bit()) - Width = llvm::alignTo(Width, Align); - return std::make_pair(Width, Align); + if (Target.getTriple().isArch64Bit()) { + MPI.Width = llvm::alignTo(MPI.Width, MPI.Align); + MPI.HasPadding = MPI.Width != (Ptrs * PtrSize + Ints * IntSize); + } + return MPI; } CXXABI *clang::CreateMicrosoftCXXABI(ASTContext &Ctx) { Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2201,150 +2201,6 @@ return false; } -bool QualType::unionHasUniqueObjectRepresentations( - const ASTContext &Context) const { - assert((*this)->isUnionType() && "must be union type"); - CharUnits UnionSize = Context.getTypeSizeInChars(*this); - const RecordDecl *Union = getTypePtr()->getAs()->getDecl(); - - for (const auto *Field : Union->fields()) { - if (!Field->getType().hasUniqueObjectRepresentations(Context)) - return false; - CharUnits FieldSize = Context.getTypeSizeInChars(Field->getType()); - if (FieldSize != UnionSize) - return false; - } - return true; -} - -static bool isStructEmpty(QualType Ty) { - assert(Ty.getTypePtr()->isStructureOrClassType() && - "Must be struct or class"); - const RecordDecl *RD = Ty.getTypePtr()->getAs()->getDecl(); - - if (!RD->field_empty()) - return false; - - if (const CXXRecordDecl *ClassDecl = dyn_cast(RD)) { - return ClassDecl->isEmpty(); - } - - return true; -} - -bool QualType::structHasUniqueObjectRepresentations( - const ASTContext &Context) const { - assert((*this)->isStructureOrClassType() && "Must be struct or class"); - const RecordDecl *RD = getTypePtr()->getAs()->getDecl(); - - if (isStructEmpty(*this)) - return false; - - // Check base types. - CharUnits BaseSize{}; - if (const CXXRecordDecl *ClassDecl = dyn_cast(RD)) { - for (const auto Base : ClassDecl->bases()) { - if (Base.isVirtual()) - return false; - - // Empty bases are permitted, otherwise ensure base has unique - // representation. Also, Empty Base Optimization means that an - // Empty base takes up 0 size. - if (!isStructEmpty(Base.getType())) { - if (!Base.getType().structHasUniqueObjectRepresentations(Context)) - return false; - BaseSize += Context.getTypeSizeInChars(Base.getType()); - } - } - } - - CharUnits StructSize = Context.getTypeSizeInChars(*this); - - // This struct obviously has bases that keep it from being 'empty', so - // checking fields is no longer required. Ensure that the struct size - // is the sum of the bases. - if (RD->field_empty()) - return StructSize == BaseSize; - - CharUnits CurOffset = - Context.toCharUnitsFromBits(Context.getFieldOffset(*RD->field_begin())); - - // If the first field isn't at the sum of the size of the bases, there - // is padding somewhere. - if (BaseSize != CurOffset) - return false; - - for (const auto *Field : RD->fields()) { - if (!Field->getType().hasUniqueObjectRepresentations(Context)) - return false; - CharUnits FieldSize = Context.getTypeSizeInChars(Field->getType()); - CharUnits FieldOffset = - Context.toCharUnitsFromBits(Context.getFieldOffset(Field)); - // Has padding between fields. - if (FieldOffset != CurOffset) - return false; - CurOffset += FieldSize; - } - // Check for tail padding. - return CurOffset == StructSize; -} - -bool QualType::hasUniqueObjectRepresentations(const ASTContext &Context) const { - // C++17 [meta.unary.prop]: - // The predicate condition for a template specialization - // has_unique_object_representations shall be - // satisfied if and only if: - // (9.1) - T is trivially copyable, and - // (9.2) - any two objects of type T with the same value have the same - // object representation, where two objects - // of array or non-union class type are considered to have the same value - // if their respective sequences of - // direct subobjects have the same values, and two objects of union type - // are considered to have the same - // value if they have the same active member and the corresponding members - // have the same value. - // The set of scalar types for which this condition holds is - // implementation-defined. [ Note: If a type has padding - // bits, the condition does not hold; otherwise, the condition holds true - // for unsigned integral types. -- end note ] - if (isNull()) - return false; - - // Arrays are unique only if their element type is unique. - if ((*this)->isArrayType()) - return Context.getBaseElementType(*this).hasUniqueObjectRepresentations( - Context); - - // (9.1) - T is trivially copyable, and - if (!isTriviallyCopyableType(Context)) - return false; - - // Functions are not unique. - if ((*this)->isFunctionType()) - return false; - - // All integrals and enums are unique! - if ((*this)->isIntegralOrEnumerationType()) - return true; - - // All pointers are unique, since they're just integrals. - if ((*this)->isPointerType() || (*this)->isMemberPointerType()) - return true; - - if ((*this)->isRecordType()) { - const RecordDecl *Record = getTypePtr()->getAs()->getDecl(); - - // Lambda types are not unique, so exclude them immediately. - if (Record->isLambda()) - return false; - - if (Record->isUnion()) - return unionHasUniqueObjectRepresentations(Context); - return structHasUniqueObjectRepresentations(Context); - } - return false; -} - bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const { return !Context.getLangOpts().ObjCAutoRefCount && Context.getLangOpts().ObjCWeak && Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -4616,7 +4616,7 @@ // function call. return !T->isIncompleteType(); case UTT_HasUniqueObjectRepresentations: - return T.hasUniqueObjectRepresentations(C); + return C.hasUniqueObjectRepresentations(T); } } Index: test/SemaCXX/has_unique_object_reps_member_ptr.cpp =================================================================== --- test/SemaCXX/has_unique_object_reps_member_ptr.cpp +++ test/SemaCXX/has_unique_object_reps_member_ptr.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple x86_64-linux-pc -DIS64 -fsyntax-only -verify -std=c++17 %s +// RUN: %clang_cc1 -triple x86_64-windows-pc -DIS64 -fsyntax-only -verify -std=c++17 %s +// RUN: %clang_cc1 -triple i386-linux-pc -fsyntax-only -verify -std=c++17 %s +// RUN: %clang_cc1 -triple i386-windows-pc -DW32 -fsyntax-only -verify -std=c++17 %s +// expected-no-diagnostics + +struct Base {}; +struct A : virtual Base { + virtual void n() {} +}; + +auto p = &A::n; +static_assert(__has_unique_object_representations(decltype(p))); + +struct B { + decltype(p) x; + int b; +#ifdef IS64 + // required on 64 bit to fill out the tail padding. + int c; +#endif +}; +static_assert(__has_unique_object_representations(B)); + +struct C { // has padding on Win32, but nothing else. + decltype(p) x; +}; +#ifdef W32 +static_assert(!__has_unique_object_representations(C)); +#else +static_assert(__has_unique_object_representations(C)); +#endif Index: test/SemaCXX/type-traits.cpp =================================================================== --- test/SemaCXX/type-traits.cpp +++ test/SemaCXX/type-traits.cpp @@ -2447,7 +2447,7 @@ int b; }; -static_assert(!has_unique_object_representations::value, "but not with padding"); +//static_assert(!has_unique_object_representations::value, "but not with padding"); struct InheritsFromPadding : Padding { int c; @@ -2518,12 +2518,11 @@ static_assert(has_unique_object_representations::value, "Enums are integrals, so unique!"); static_assert(has_unique_object_representations::value, "Enums are integrals, so unique!"); -// because reference types aren't object types +// because references aren't trivially copyable. static_assert(!has_unique_object_representations::value, "No references!"); static_assert(!has_unique_object_representations::value, "No references!"); static_assert(!has_unique_object_representations::value, "No references!"); static_assert(!has_unique_object_representations::value, "No references!"); - static_assert(!has_unique_object_representations::value, "No empty types!"); class Compressed : Empty { @@ -2556,6 +2555,16 @@ static_assert(!has_unique_object_representations::value, "So no array of doubles!"); static_assert(!has_unique_object_representations::value, "So no array of doubles!"); +struct __attribute__((aligned(16))) WeirdAlignment { + int i; +}; +union __attribute__((aligned(16))) WeirdAlignmentUnion { + int i; +}; +static_assert(!has_unique_object_representations::value, "Alignment causes padding"); +static_assert(!has_unique_object_representations::value, "Alignment causes padding"); +static_assert(!has_unique_object_representations::value, "Also no arrays that have padding"); + static_assert(!has_unique_object_representations::value, "Functions are not unique"); static_assert(!has_unique_object_representations::value, "Functions are not unique"); static_assert(!has_unique_object_representations::value, "Functions are not unique"); @@ -2582,6 +2591,73 @@ static_assert(!has_unique_object_representations::value, "Functions are not unique"); static_assert(!has_unique_object_representations::value, "Functions are not unique"); -static auto lambda = []() {}; -static_assert(!has_unique_object_representations::value, "Lambdas are not unique"); +void foo(){ + static auto lambda = []() {}; + static_assert(!has_unique_object_representations::value, "Lambdas follow struct rules"); + int i; + static auto lambda2 = [i]() {}; + static_assert(has_unique_object_representations::value, "Lambdas follow struct rules"); +} + +struct PaddedBitfield { + char c : 6; + char d : 1; +}; + +struct UnPaddedBitfield { + char c : 6; + char d : 2; +}; + +struct AlignedPaddedBitfield { + char c : 6; + __attribute__((aligned(1))) + char d : 2; +}; + +static_assert(!has_unique_object_representations::value, "Bitfield padding"); +static_assert(has_unique_object_representations::value, "Bitfield padding"); +static_assert(!has_unique_object_representations::value, "Bitfield padding"); + +struct BoolBitfield { + bool b : 8; +}; + +static_assert(has_unique_object_representations::value, "Bitfield bool"); + +struct BoolBitfield2 { + bool b : 16; +}; + +static_assert(!has_unique_object_representations::value, "Bitfield bool"); + +struct GreaterSizeBitfield { + //expected-warning@+1 {{width of bit-field 'n'}} + int n : 1024; +}; + +static_assert(sizeof(GreaterSizeBitfield) == 128, "Bitfield Size"); +static_assert(!has_unique_object_representations::value, "Bitfield padding"); + +struct StructWithRef { + int &I; +}; + +static_assert(has_unique_object_representations::value, "References are still unique"); + +struct NotUniqueBecauseTailPadding { + int &r; + char a; +}; +struct CanBeUniqueIfNoPadding : NotUniqueBecauseTailPadding { + char b[7]; +}; + +static_assert(!has_unique_object_representations::value, + "non trivial"); +// Can be unique on Itanium, since the is child class' data is 'folded' into the +// parent's tail padding. +static_assert(sizeof(CanBeUniqueIfNoPadding) != 16 || + has_unique_object_representations::value, + "inherit from std layout");