Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2109,6 +2109,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 @@ -770,10 +770,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. @@ -1118,8 +1114,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 @@ -2110,6 +2110,146 @@ } } +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) { + 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; +} + +static bool structHasUniqueObjectRepresentations(const ASTContext &Context, + const RecordDecl *RD) { + assert((RD->isStruct() || RD->isClass()) && "Must be struct/class type"); + const auto &Layout = Context.getASTRecordLayout(RD); + + CharUnits CurOffset{}; + if (const auto *ClassDecl = dyn_cast(RD)) { + if (ClassDecl->isDynamicClass() || ClassDecl->getNumVBases() != 0) + return false; + + SmallVector Bases; + for (const auto Base : ClassDecl->bases()) { + // Empty types can be inherited from, as long as they are handled + // by EBO. This will be checked in the next loop. + if (!isStructEmpty(Base.getType()) && + !Context.hasUniqueObjectRepresentations(Base.getType())) + return false; + 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.getTypeSizeInChars(Base); + if (BaseOffset != CurOffset) + return false; + // Empty bases are only allowed in EBO. + if (!isStructEmpty(Base)) + CurOffset = BaseOffset + BaseSize; + } + } + + int FieldIndex = 0; + for (const auto *Field : RD->fields()) { + if (!Context.hasUniqueObjectRepresentations(Field->getType())) + return false; + CharUnits FieldSize = Context.getTypeSizeInChars(Field->getType()); + CharUnits FieldOffset = + Context.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex)); + + if (FieldOffset != CurOffset) + return false; + + CurOffset = FieldSize + FieldOffset; + + ++FieldIndex; + } + + // Handles tail-padding. >= comparision takes care of EBO cases. + return CurOffset == Layout.getSize(); +} + +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 ] + if (Ty.isNull()) + return false; + + // 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; + + // Functions are not unique. + if (Ty->isFunctionType()) + return false; + + // All integrals and enums are unique. + if (Ty->isIntegralOrEnumerationType()) + return true; + + // All other pointers are unique. + if (Ty->isPointerType() || Ty->isMemberPointerType()) + return true; + + if (Ty->isRecordType()) { + const RecordDecl *Record = Ty->getAs()->getDecl(); + + if (Record->isUnion()) + return unionHasUniqueObjectRepresentations(*this, Record); + return structHasUniqueObjectRepresentations(*this, Record); + } + + return false; +} + unsigned ASTContext::CountNonClassIvars(const ObjCInterfaceDecl *OI) const { unsigned count = 0; // Count ivars declared in class extension. Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2166,151 +2166,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; -} - -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/type-traits.cpp =================================================================== --- test/SemaCXX/type-traits.cpp +++ test/SemaCXX/type-traits.cpp @@ -2523,7 +2523,6 @@ 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,11 @@ 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 rues"); +}