void CollectInheritedProtocols(const Decl *CDecl,
llvm::SmallPtrSet<ObjCProtocolDecl*, 8> &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
//===--------------------------------------------------------------------===//
/// 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.
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.
break;
case Type::MemberPointer: {
const MemberPointerType *MPT = cast<MemberPointerType>(T);
- std::tie(Width, Align) = ABI->getMemberPointerWidthAndAlign(MPT);
+ CXXABI::MemberPointerInfo MPI = ABI->getMemberPointerInfo(MPT);
+ Width = MPI.Width;
+ Align = MPI.Align;
break;
}
case Type::Complex: {
}
}
+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<RecordType>()->getDecl();
+
+ if (!RD->field_empty())
+ return false;
+
+ if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RD))
+ return ClassDecl->isEmpty();
+
+ return true;
+}
+
+static llvm::Optional<int64_t>
+structHasUniqueObjectRepresentations(const ASTContext &Context,
+ const RecordDecl *RD) {
+ assert(!RD->isUnion() && "Must be struct/class type");
+ const auto &Layout = Context.getASTRecordLayout(RD);
+
+ int64_t CurOffsetInBits = 0;
+ if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RD)) {
+ if (ClassDecl->isDynamicClass())
+ return llvm::None;
+
+ SmallVector<std::pair<QualType, int64_t>, 4> 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<int64_t> Size = structHasUniqueObjectRepresentations(
+ Context, Base.getType()->getAs<RecordType>()->getDecl());
+ if (!Size)
+ return llvm::None;
+ Bases.emplace_back(Base.getType(), Size.getValue());
+ }
+ }
+
+ std::sort(
+ Bases.begin(), Bases.end(), [&](const std::pair<QualType, int64_t> &L,
+ const std::pair<QualType, int64_t> &R) {
+ return Layout.getBaseClassOffset(L.first->getAsCXXRecordDecl()) <
+ Layout.getBaseClassOffset(R.first->getAsCXXRecordDecl());
+ });
+
+ 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)
+ 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<T> 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<MemberPointerType>();
+ return !ABI->getMemberPointerInfo(MPT).HasPadding;
+ }
+
+ if (Ty->isRecordType()) {
+ const RecordDecl *Record = Ty->getAs<RecordType>()->getDecl();
+
+ if (Record->isUnion())
+ return unionHasUniqueObjectRepresentations(*this, Record);
+
+ Optional<int64_t> StructSize =
+ structHasUniqueObjectRepresentations(*this, Record);
+
+ return StructSize &&
+ StructSize.getValue() == static_cast<int64_t>(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.
public:
virtual ~CXXABI();
- /// Returns the width and alignment of a member pointer in bits.
- virtual std::pair<uint64_t, unsigned>
- 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;
public:
ItaniumCXXABI(ASTContext &Ctx) : Context(Ctx) { }
- std::pair<uint64_t, unsigned>
- 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 {
public:
MicrosoftCXXABI(ASTContext &Ctx) : Context(Ctx) { }
- std::pair<uint64_t, unsigned>
- getMemberPointerWidthAndAlign(const MemberPointerType *MPT) const override;
+ MemberPointerInfo
+ getMemberPointerInfo(const MemberPointerType *MPT) const override;
CallingConv getDefaultMethodCallConv(bool isVariadic) const override {
if (!isVariadic &&
return std::make_pair(Ptrs, Ints);
}
-std::pair<uint64_t, unsigned> 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.
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) {
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<RecordType>()->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<RecordType>()->getDecl();
-
- if (!RD->field_empty())
- return false;
-
- if (const CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(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<RecordType>()->getDecl();
-
- if (isStructEmpty(*this))
- return false;
-
- // Check base types.
- CharUnits BaseSize{};
- if (const CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(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<T> 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<RecordType>()->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 &&
// function call.
return !T->isIncompleteType();
case UTT_HasUniqueObjectRepresentations:
- return T.hasUniqueObjectRepresentations(C);
+ return C.hasUniqueObjectRepresentations(T);
}
}
--- /dev/null
+// 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
int b;
};
-static_assert(!has_unique_object_representations<Padding>::value, "but not with padding");
+//static_assert(!has_unique_object_representations<Padding>::value, "but not with padding");
struct InheritsFromPadding : Padding {
int c;
static_assert(has_unique_object_representations<ExampleEnumClass>::value, "Enums are integrals, so unique!");
static_assert(has_unique_object_representations<LLEnumClass>::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<int &>::value, "No references!");
static_assert(!has_unique_object_representations<const int &>::value, "No references!");
static_assert(!has_unique_object_representations<volatile int &>::value, "No references!");
static_assert(!has_unique_object_representations<const volatile int &>::value, "No references!");
-
static_assert(!has_unique_object_representations<Empty>::value, "No empty types!");
class Compressed : Empty {
static_assert(!has_unique_object_representations<double[]>::value, "So no array of doubles!");
static_assert(!has_unique_object_representations<double[][42]>::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<WeirdAlignment>::value, "Alignment causes padding");
+static_assert(!has_unique_object_representations<WeirdAlignmentUnion>::value, "Alignment causes padding");
+static_assert(!has_unique_object_representations<WeirdAlignment[42]>::value, "Also no arrays that have padding");
+
static_assert(!has_unique_object_representations<int(int)>::value, "Functions are not unique");
static_assert(!has_unique_object_representations<int(int) const>::value, "Functions are not unique");
static_assert(!has_unique_object_representations<int(int) volatile>::value, "Functions are not unique");
static_assert(!has_unique_object_representations<int(int, ...) volatile &&>::value, "Functions are not unique");
static_assert(!has_unique_object_representations<int(int, ...) const volatile &&>::value, "Functions are not unique");
-static auto lambda = []() {};
-static_assert(!has_unique_object_representations<decltype(lambda)>::value, "Lambdas are not unique");
+void foo(){
+ static auto lambda = []() {};
+ static_assert(!has_unique_object_representations<decltype(lambda)>::value, "Lambdas follow struct rules");
+ int i;
+ static auto lambda2 = [i]() {};
+ static_assert(has_unique_object_representations<decltype(lambda2)>::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<PaddedBitfield>::value, "Bitfield padding");
+static_assert(has_unique_object_representations<UnPaddedBitfield>::value, "Bitfield padding");
+static_assert(!has_unique_object_representations<AlignedPaddedBitfield>::value, "Bitfield padding");
+
+struct BoolBitfield {
+ bool b : 8;
+};
+
+static_assert(has_unique_object_representations<BoolBitfield>::value, "Bitfield bool");
+
+struct BoolBitfield2 {
+ bool b : 16;
+};
+
+static_assert(!has_unique_object_representations<BoolBitfield2>::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<GreaterSizeBitfield>::value, "Bitfield padding");
+
+struct StructWithRef {
+ int &I;
+};
+
+static_assert(has_unique_object_representations<StructWithRef>::value, "References are still unique");
+
+struct NotUniqueBecauseTailPadding {
+ int &r;
+ char a;
+};
+struct CanBeUniqueIfNoPadding : NotUniqueBecauseTailPadding {
+ char b[7];
+};
+
+static_assert(!has_unique_object_representations<NotUniqueBecauseTailPadding>::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<CanBeUniqueIfNoPadding>::value,
+ "inherit from std layout");