Index: cfe/trunk/include/clang/AST/Type.h =================================================================== --- cfe/trunk/include/clang/AST/Type.h +++ cfe/trunk/include/clang/AST/Type.h @@ -770,6 +770,10 @@ /// 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. @@ -1114,6 +1118,8 @@ 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: cfe/trunk/include/clang/Basic/TokenKinds.def =================================================================== --- cfe/trunk/include/clang/Basic/TokenKinds.def +++ cfe/trunk/include/clang/Basic/TokenKinds.def @@ -455,6 +455,8 @@ TYPE_TRAIT_1(__is_polymorphic, IsPolymorphic, KEYCXX) TYPE_TRAIT_1(__is_trivial, IsTrivial, KEYCXX) TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX) +TYPE_TRAIT_1(__has_unique_object_representations, + HasUniqueObjectRepresentations, KEYCXX) // Clang-only C++ Type Traits TYPE_TRAIT_N(__is_trivially_constructible, IsTriviallyConstructible, KEYCXX) Index: cfe/trunk/include/clang/Basic/TypeTraits.h =================================================================== --- cfe/trunk/include/clang/Basic/TypeTraits.h +++ cfe/trunk/include/clang/Basic/TypeTraits.h @@ -70,7 +70,8 @@ UTT_IsUnsigned, UTT_IsVoid, UTT_IsVolatile, - UTT_Last = UTT_IsVolatile, + UTT_HasUniqueObjectRepresentations, + UTT_Last = UTT_HasUniqueObjectRepresentations, BTT_IsBaseOf, BTT_IsConvertible, BTT_IsConvertibleTo, Index: cfe/trunk/lib/AST/Type.cpp =================================================================== --- cfe/trunk/lib/AST/Type.cpp +++ cfe/trunk/lib/AST/Type.cpp @@ -2166,6 +2166,152 @@ 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; +} + +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: cfe/trunk/lib/Parse/ParseExpr.cpp =================================================================== --- cfe/trunk/lib/Parse/ParseExpr.cpp +++ cfe/trunk/lib/Parse/ParseExpr.cpp @@ -716,6 +716,7 @@ /// '__is_sealed' [MS] /// '__is_trivial' /// '__is_union' +/// '__has_unique_object_representations' /// /// [Clang] unary-type-trait: /// '__is_aggregate' Index: cfe/trunk/lib/Sema/SemaExprCXX.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExprCXX.cpp +++ cfe/trunk/lib/Sema/SemaExprCXX.cpp @@ -4175,6 +4175,7 @@ case UTT_IsDestructible: case UTT_IsNothrowDestructible: case UTT_IsTriviallyDestructible: + case UTT_HasUniqueObjectRepresentations: if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType()) return true; @@ -4614,6 +4615,8 @@ // Returns True if and only if T is a complete type at the point of the // function call. return !T->isIncompleteType(); + case UTT_HasUniqueObjectRepresentations: + return T.hasUniqueObjectRepresentations(C); } } Index: cfe/trunk/test/SemaCXX/type-traits.cpp =================================================================== --- cfe/trunk/test/SemaCXX/type-traits.cpp +++ cfe/trunk/test/SemaCXX/type-traits.cpp @@ -2352,3 +2352,236 @@ { int arr[F(__is_trivially_destructible(void))]; } { int arr[F(__is_trivially_destructible(const volatile void))]; } } + +// Instantiation of __has_unique_object_representations +template +struct has_unique_object_representations { + static const bool value = __has_unique_object_representations(T); +}; + +static_assert(!has_unique_object_representations::value, "void is never unique"); +static_assert(!has_unique_object_representations::value, "void is never unique"); +static_assert(!has_unique_object_representations::value, "void is never unique"); +static_assert(!has_unique_object_representations::value, "void is never unique"); + +static_assert(has_unique_object_representations::value, "integrals are"); +static_assert(has_unique_object_representations::value, "integrals are"); +static_assert(has_unique_object_representations::value, "integrals are"); +static_assert(has_unique_object_representations::value, "integrals are"); + +static_assert(has_unique_object_representations::value, "as are pointers"); +static_assert(has_unique_object_representations::value, "as are pointers"); +static_assert(has_unique_object_representations::value, "are pointers"); +static_assert(has_unique_object_representations::value, "as are pointers"); + +static_assert(has_unique_object_representations::value, "as are pointers"); +static_assert(has_unique_object_representations::value, "as are pointers"); +static_assert(has_unique_object_representations::value, "as are pointers"); +static_assert(has_unique_object_representations::value, "as are pointers"); + +class C {}; +using FP = int (*)(int); +using PMF = int (C::*)(int); +using PMD = int C::*; + +static_assert(has_unique_object_representations::value, "even function pointers"); +static_assert(has_unique_object_representations::value, "even function pointers"); +static_assert(has_unique_object_representations::value, "even function pointers"); +static_assert(has_unique_object_representations::value, "even function pointers"); + +static_assert(has_unique_object_representations::value, "and pointer to members"); +static_assert(has_unique_object_representations::value, "and pointer to members"); +static_assert(has_unique_object_representations::value, "and pointer to members"); +static_assert(has_unique_object_representations::value, "and pointer to members"); + +static_assert(has_unique_object_representations::value, "and pointer to members"); +static_assert(has_unique_object_representations::value, "and pointer to members"); +static_assert(has_unique_object_representations::value, "and pointer to members"); +static_assert(has_unique_object_representations::value, "and pointer to members"); + +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); +static_assert(has_unique_object_representations::value, "yes, all integral types"); + +static_assert(!has_unique_object_representations::value, "but not void!"); +static_assert(!has_unique_object_representations::value, "or nullptr_t"); +static_assert(!has_unique_object_representations::value, "definitely not Floating Point"); +static_assert(!has_unique_object_representations::value, "definitely not Floating Point"); +static_assert(!has_unique_object_representations::value, "definitely not Floating Point"); + +struct NoPadding { + int a; + int b; +}; + +static_assert(has_unique_object_representations::value, "types without padding are"); + +struct InheritsFromNoPadding : NoPadding { + int c; + int d; +}; + +static_assert(has_unique_object_representations::value, "types without padding are"); + +struct VirtuallyInheritsFromNoPadding : virtual NoPadding { + int c; + int d; +}; + +static_assert(!has_unique_object_representations::value, "No virtual inheritence"); + +struct Padding { + char a; + int b; +}; + +static_assert(!has_unique_object_representations::value, "but not with padding"); + +struct InheritsFromPadding : Padding { + int c; + int d; +}; + +static_assert(!has_unique_object_representations::value, "or its subclasses"); + +struct TailPadding { + int a; + char b; +}; + +static_assert(!has_unique_object_representations::value, "even at the end"); + +struct TinyStruct { + char a; +}; + +static_assert(has_unique_object_representations::value, "Should be no padding"); + +struct InheritsFromTinyStruct : TinyStruct { + int b; +}; + +static_assert(!has_unique_object_representations::value, "Inherit causes padding"); + +union NoPaddingUnion { + int a; + unsigned int b; +}; + +static_assert(has_unique_object_representations::value, "unions follow the same rules as structs"); + +union PaddingUnion { + int a; + long long b; +}; + +static_assert(!has_unique_object_representations::value, "unions follow the same rules as structs"); + +struct NotTriviallyCopyable { + int x; + NotTriviallyCopyable(const NotTriviallyCopyable &) {} +}; + +static_assert(!has_unique_object_representations::value, "must be trivially copyable"); + +struct HasNonUniqueMember { + float x; +}; + +static_assert(!has_unique_object_representations::value, "all members must be unique"); + +enum ExampleEnum { xExample, + yExample }; +enum LLEnum : long long { xLongExample, + yLongExample }; + +static_assert(has_unique_object_representations::value, "Enums are integrals, so unique!"); +static_assert(has_unique_object_representations::value, "Enums are integrals, so unique!"); + +enum class ExampleEnumClass { xExample, + yExample }; +enum class LLEnumClass : long long { xLongExample, + yLongExample }; + +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 +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 { + int x; +}; + +static_assert(has_unique_object_representations::value, "But inheriting from one is ok"); + +class EmptyInheritor : Compressed {}; + +static_assert(has_unique_object_representations::value, "As long as the base has items, empty is ok"); + +class Dynamic { + virtual void A(); + int i; +}; + +static_assert(!has_unique_object_representations::value, "Dynamic types are not valid"); + +class InheritsDynamic : Dynamic { + int j; +}; + +static_assert(!has_unique_object_representations::value, "Dynamic types are not valid"); + +static_assert(has_unique_object_representations::value, "Arrays are fine, as long as their value type is"); +static_assert(has_unique_object_representations::value, "Arrays are fine, as long as their value type is"); +static_assert(has_unique_object_representations::value, "Arrays are fine, as long as their value type is"); +static_assert(!has_unique_object_representations::value, "So no array of doubles!"); +static_assert(!has_unique_object_representations::value, "So no array of doubles!"); +static_assert(!has_unique_object_representations::value, "So no array of doubles!"); + +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"); +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"); +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"); +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"); + +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"); +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"); +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"); +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"); + +static auto lambda = []() {}; +static_assert(!has_unique_object_representations::value, "Lambdas are not unique"); +