[llvm] r244164 - Add a TrailingObjects template class.
James Y Knight
jyknight at google.com
Wed Aug 5 15:57:34 PDT 2015
Author: jyknight
Date: Wed Aug 5 17:57:34 2015
New Revision: 244164
URL: http://llvm.org/viewvc/llvm-project?rev=244164&view=rev
Log:
Add a TrailingObjects template class.
This is intended to help support the idiom of a class that has some
other objects (or multiple arrays of different types of objects)
appended on the end, which is used quite heavily in clang.
Differential Revision: http://reviews.llvm.org/D11272
Added:
llvm/trunk/include/llvm/Support/TrailingObjects.h
llvm/trunk/unittests/Support/TrailingObjectsTest.cpp
Modified:
llvm/trunk/include/llvm/Support/type_traits.h
llvm/trunk/lib/IR/AttributeImpl.h
llvm/trunk/lib/IR/Attributes.cpp
llvm/trunk/unittests/Support/CMakeLists.txt
Added: llvm/trunk/include/llvm/Support/TrailingObjects.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/TrailingObjects.h?rev=244164&view=auto
==============================================================================
--- llvm/trunk/include/llvm/Support/TrailingObjects.h (added)
+++ llvm/trunk/include/llvm/Support/TrailingObjects.h Wed Aug 5 17:57:34 2015
@@ -0,0 +1,231 @@
+//===--- TrailingObjects.h - Variable-length classes ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This header defines support for implementing classes that have
+/// some trailing object (or arrays of objects) appended to them. The
+/// main purpose is to make it obvious where this idiom is being used,
+/// and to make the usage more idiomatic and more difficult to get
+/// wrong.
+///
+/// The TrailingObject template abstracts away the reinterpret_cast,
+/// pointer arithmetic, and size calculations used for the allocation
+/// and access of appended arrays of objects, as well as asserts that
+/// the alignment of the classes involved are appropriate for the
+/// usage. Additionally, it ensures that the base type is final --
+/// deriving from a class that expects data appended immediately after
+/// it is typically not safe.
+///
+/// Users are expected to derive from this template, and provide
+/// numTrailingObjects implementations for each trailing type,
+/// e.g. like this sample:
+///
+/// \code
+/// class VarLengthObj : private TrailingObjects<VarLengthObj, int, double> {
+/// friend TrailingObjects;
+///
+/// unsigned NumInts, NumDoubles;
+/// size_t numTrailingObjects(OverloadToken<int>) const { return NumInts; }
+/// size_t numTrailingObjects(OverloadToken<double>) const {
+/// return NumDoubles;
+/// }
+/// };
+/// \endcode
+///
+/// You can access the appended arrays via 'getTrailingObjects', and
+/// determine the size needed for allocation via
+/// 'additionalSizeToAlloc' and 'totalSizeToAlloc'.
+///
+/// All the methods implemented by this class are are intended for use
+/// by the implementation of the class, not as part of its interface
+/// (thus, private inheritance is suggested).
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_TRAILINGOBJECTS_H
+#define LLVM_SUPPORT_TRAILINGOBJECTS_H
+
+#include <new>
+#include <type_traits>
+#include "llvm/Support/AlignOf.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/type_traits.h"
+
+namespace llvm {
+
+/// The base class for TrailingObjects* classes.
+class TrailingObjectsBase {
+protected:
+ /// OverloadToken's purpose is to allow specifying function overloads
+ /// for different types, without actually taking the types as
+ /// parameters. (Necessary because member function templates cannot
+ /// be specialized, so overloads must be used instead of
+ /// specialization.)
+ template <typename T> struct OverloadToken {};
+};
+
+// Internally used to indicate that the user didn't supply this value,
+// so the explicit-specialization for fewer args will be used.
+class NoTrailingTypeArg {};
+
+// TODO: Consider using a single variadic implementation instead of
+// multiple copies of the TrailingObjects template? [but, variadic
+// template recursive implementations are annoying...]
+
+/// This is the two-type version of the TrailingObjects template; see
+/// file docstring for details.
+template <typename BaseTy, typename TrailingTy1,
+ typename TrailingTy2 = NoTrailingTypeArg>
+class TrailingObjects : public TrailingObjectsBase {
+private:
+ // Contains static_assert statements for the alignment of the
+ // types. Must not be at class-level, because BaseTy isn't complete
+ // at class instantiation time, but will be by the time this
+ // function is instantiated.
+ static void verifyTrailingObjectsAssertions() {
+ static_assert(llvm::AlignOf<BaseTy>::Alignment >=
+ llvm::AlignOf<TrailingTy1>::Alignment,
+ "TrailingTy1 requires more alignment than BaseTy provides");
+ static_assert(
+ llvm::AlignOf<TrailingTy1>::Alignment >=
+ llvm::AlignOf<TrailingTy2>::Alignment,
+ "TrailingTy2 requires more alignment than TrailingTy1 provides");
+
+#ifdef LLVM_IS_FINAL
+ static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final.");
+#endif
+ }
+
+ // The next four functions are internal helpers for getTrailingObjects.
+ static const TrailingTy1 *getTrailingObjectsImpl(const BaseTy *Obj,
+ OverloadToken<TrailingTy1>) {
+ return reinterpret_cast<const TrailingTy1 *>(Obj + 1);
+ }
+
+ static TrailingTy1 *getTrailingObjectsImpl(BaseTy *Obj,
+ OverloadToken<TrailingTy1>) {
+ return reinterpret_cast<TrailingTy1 *>(Obj + 1);
+ }
+
+ static const TrailingTy2 *getTrailingObjectsImpl(const BaseTy *Obj,
+ OverloadToken<TrailingTy2>) {
+ return reinterpret_cast<const TrailingTy2 *>(
+ getTrailingObjectsImpl(Obj, OverloadToken<TrailingTy1>()) +
+ Obj->numTrailingObjects(OverloadToken<TrailingTy1>()));
+ }
+
+ static TrailingTy2 *getTrailingObjectsImpl(BaseTy *Obj,
+ OverloadToken<TrailingTy2>) {
+ return reinterpret_cast<TrailingTy2 *>(
+ getTrailingObjectsImpl(Obj, OverloadToken<TrailingTy1>()) +
+ Obj->numTrailingObjects(OverloadToken<TrailingTy1>()));
+ }
+
+protected:
+ /// Returns a pointer to the trailing object array of the given type
+ /// (which must be one of those specified in the class template). The
+ /// array may have zero or more elements in it.
+ template <typename T> const T *getTrailingObjects() const {
+ verifyTrailingObjectsAssertions();
+ // Forwards to an impl function with overloads, since member
+ // function templates can't be specialized.
+ return getTrailingObjectsImpl(static_cast<const BaseTy *>(this),
+ OverloadToken<T>());
+ }
+
+ /// Returns a pointer to the trailing object array of the given type
+ /// (which must be one of those specified in the class template). The
+ /// array may have zero or more elements in it.
+ template <typename T> T *getTrailingObjects() {
+ verifyTrailingObjectsAssertions();
+ // Forwards to an impl function with overloads, since member
+ // function templates can't be specialized.
+ return getTrailingObjectsImpl(static_cast<BaseTy *>(this),
+ OverloadToken<T>());
+ }
+
+ /// Returns the size of the trailing data, if an object were
+ /// allocated with the given counts (The counts are in the same order
+ /// as the template arguments). This does not include the size of the
+ /// base object. The template arguments must be the same as those
+ /// used in the class; they are supplied here redundantly only so
+ /// that it's clear what the counts are counting in callers.
+ template <typename Ty1, typename Ty2,
+ typename std::enable_if<std::is_same<Ty1, TrailingTy1>::value &&
+ std::is_same<Ty2, TrailingTy2>::value,
+ int>::type = 0>
+ static LLVM_CONSTEXPR size_t additionalSizeToAlloc(size_t Count1, size_t Count2) {
+ return sizeof(TrailingTy1) * Count1 + sizeof(TrailingTy2) * Count2;
+ }
+
+ /// Returns the total size of an object if it were allocated with the
+ /// given trailing object counts. This is the same as
+ /// additionalSizeToAlloc, except it *does* include the size of the base
+ /// object.
+ template <typename Ty1, typename Ty2>
+ static LLVM_CONSTEXPR size_t totalSizeToAlloc(size_t Count1, size_t Count2) {
+ return sizeof(BaseTy) + additionalSizeToAlloc<Ty1, Ty2>(Count1, Count2);
+ }
+};
+
+/// This is the one-type version of the TrailingObjects template. See
+/// the two-type version for more documentation.
+template <typename BaseTy, typename TrailingTy1>
+class TrailingObjects<BaseTy, TrailingTy1, NoTrailingTypeArg>
+ : public TrailingObjectsBase {
+private:
+ static void verifyTrailingObjectsAssertions() {
+ static_assert(llvm::AlignOf<BaseTy>::Alignment >=
+ llvm::AlignOf<TrailingTy1>::Alignment,
+ "TrailingTy1 requires more alignment than BaseTy provides");
+
+#ifdef LLVM_IS_FINAL
+ static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final.");
+#endif
+ }
+
+ static const TrailingTy1 *getTrailingObjectsImpl(const BaseTy *Obj,
+ OverloadToken<TrailingTy1>) {
+ return reinterpret_cast<const TrailingTy1 *>(Obj + 1);
+ }
+
+ static TrailingTy1 *getTrailingObjectsImpl(BaseTy *Obj,
+ OverloadToken<TrailingTy1>) {
+ return reinterpret_cast<TrailingTy1 *>(Obj + 1);
+ }
+
+protected:
+ template <typename T> const T *getTrailingObjects() const {
+ verifyTrailingObjectsAssertions();
+ return getTrailingObjectsImpl(static_cast<const BaseTy *>(this),
+ OverloadToken<T>());
+ }
+
+ template <typename T> T *getTrailingObjects() {
+ verifyTrailingObjectsAssertions();
+ return getTrailingObjectsImpl(static_cast<BaseTy *>(this),
+ OverloadToken<T>());
+ }
+
+ template <typename Ty1,
+ typename std::enable_if<std::is_same<Ty1, TrailingTy1>::value,
+ int>::type = 0>
+ static LLVM_CONSTEXPR size_t additionalSizeToAlloc(size_t Count1) {
+ return sizeof(TrailingTy1) * Count1;
+ }
+
+ template <typename Ty1>
+ static LLVM_CONSTEXPR size_t totalSizeToAlloc(size_t Count1) {
+ return sizeof(BaseTy) + additionalSizeToAlloc<Ty1>(Count1);
+ }
+};
+
+} // end namespace llvm
+
+#endif
Modified: llvm/trunk/include/llvm/Support/type_traits.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/type_traits.h?rev=244164&r1=244163&r2=244164&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/type_traits.h (original)
+++ llvm/trunk/include/llvm/Support/type_traits.h Wed Aug 5 17:57:34 2015
@@ -93,6 +93,15 @@ struct add_const_past_pointer<
}
+// If the compiler supports detecting whether a class is final, define
+// an LLVM_IS_FINAL macro. If it cannot be defined properly, this
+// macro will be left undefined.
+#if __cplusplus >= 201402L
+#define LLVM_IS_FINAL(Ty) std::is_final<Ty>()
+#elif __has_feature(is_final) || LLVM_GNUC_PREREQ(4, 7, 0)
+#define LLVM_IS_FINAL(Ty) __is_final(Ty)
+#endif
+
#ifdef LLVM_DEFINED_HAS_FEATURE
#undef __has_feature
#endif
Modified: llvm/trunk/lib/IR/AttributeImpl.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/AttributeImpl.h?rev=244164&r1=244163&r2=244164&view=diff
==============================================================================
--- llvm/trunk/lib/IR/AttributeImpl.h (original)
+++ llvm/trunk/lib/IR/AttributeImpl.h Wed Aug 5 17:57:34 2015
@@ -18,6 +18,7 @@
#include "llvm/ADT/FoldingSet.h"
#include "llvm/IR/Attributes.h"
+#include "llvm/Support/TrailingObjects.h"
#include <string>
namespace llvm {
@@ -141,13 +142,16 @@ public:
/// \class
/// \brief This class represents a group of attributes that apply to one
/// element: function, return type, or parameter.
-class AttributeSetNode : public FoldingSetNode {
+class AttributeSetNode final
+ : public FoldingSetNode,
+ private TrailingObjects<AttributeSetNode, Attribute> {
+ friend TrailingObjects;
+
unsigned NumAttrs; ///< Number of attributes in this node.
AttributeSetNode(ArrayRef<Attribute> Attrs) : NumAttrs(Attrs.size()) {
// There's memory after the node where we can store the entries in.
- std::copy(Attrs.begin(), Attrs.end(),
- reinterpret_cast<Attribute *>(this + 1));
+ std::copy(Attrs.begin(), Attrs.end(), getTrailingObjects<Attribute>());
}
// AttributesSetNode is uniqued, these should not be publicly available.
@@ -170,7 +174,7 @@ public:
std::string getAsString(bool InAttrGrp) const;
typedef const Attribute *iterator;
- iterator begin() const { return reinterpret_cast<iterator>(this + 1); }
+ iterator begin() const { return getTrailingObjects<Attribute>(); }
iterator end() const { return begin() + NumAttrs; }
void Profile(FoldingSetNodeID &ID) const {
@@ -181,27 +185,29 @@ public:
AttrList[I].Profile(ID);
}
};
-static_assert(
- AlignOf<AttributeSetNode>::Alignment >= AlignOf<Attribute>::Alignment,
- "Alignment is insufficient for objects appended to AttributeSetNode");
//===----------------------------------------------------------------------===//
/// \class
/// \brief This class represents a set of attributes that apply to the function,
/// return type, and parameters.
-class AttributeSetImpl : public FoldingSetNode {
- friend class AttributeSet;
+typedef std::pair<unsigned, AttributeSetNode *> IndexAttrPair;
-public:
- typedef std::pair<unsigned, AttributeSetNode*> IndexAttrPair;
+class AttributeSetImpl final
+ : public FoldingSetNode,
+ private TrailingObjects<AttributeSetImpl, IndexAttrPair> {
+ friend class AttributeSet;
+ friend TrailingObjects;
private:
LLVMContext &Context;
unsigned NumAttrs; ///< Number of entries in this set.
+ // Helper fn for TrailingObjects class.
+ size_t numTrailingObjects(OverloadToken<IndexAttrPair>) { return NumAttrs; }
+
/// \brief Return a pointer to the IndexAttrPair for the specified slot.
const IndexAttrPair *getNode(unsigned Slot) const {
- return reinterpret_cast<const IndexAttrPair *>(this + 1) + Slot;
+ return getTrailingObjects<IndexAttrPair>() + Slot;
}
// AttributesSet is uniqued, these should not be publicly available.
@@ -222,8 +228,7 @@ public:
}
#endif
// There's memory after the node where we can store the entries in.
- std::copy(Attrs.begin(), Attrs.end(),
- reinterpret_cast<IndexAttrPair *>(this + 1));
+ std::copy(Attrs.begin(), Attrs.end(), getTrailingObjects<IndexAttrPair>());
}
/// \brief Get the context that created this AttributeSetImpl.
@@ -273,10 +278,6 @@ public:
void dump() const;
};
-static_assert(
- AlignOf<AttributeSetImpl>::Alignment >=
- AlignOf<AttributeSetImpl::IndexAttrPair>::Alignment,
- "Alignment is insufficient for objects appended to AttributeSetImpl");
} // end llvm namespace
Modified: llvm/trunk/lib/IR/Attributes.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Attributes.cpp?rev=244164&r1=244163&r2=244164&view=diff
==============================================================================
--- llvm/trunk/lib/IR/Attributes.cpp (original)
+++ llvm/trunk/lib/IR/Attributes.cpp Wed Aug 5 17:57:34 2015
@@ -484,8 +484,7 @@ AttributeSetNode *AttributeSetNode::get(
// new one and insert it.
if (!PA) {
// Coallocate entries after the AttributeSetNode itself.
- void *Mem = ::operator new(sizeof(AttributeSetNode) +
- sizeof(Attribute) * SortedAttrs.size());
+ void *Mem = ::operator new(totalSizeToAlloc<Attribute>(SortedAttrs.size()));
PA = new (Mem) AttributeSetNode(SortedAttrs);
pImpl->AttrsSetNodes.InsertNode(PA, InsertPoint);
}
@@ -617,9 +616,8 @@ AttributeSet::getImpl(LLVMContext &C,
// create a new one and insert it.
if (!PA) {
// Coallocate entries after the AttributeSetImpl itself.
- void *Mem = ::operator new(sizeof(AttributeSetImpl) +
- sizeof(std::pair<unsigned, AttributeSetNode *>) *
- Attrs.size());
+ void *Mem = ::operator new(
+ AttributeSetImpl::totalSizeToAlloc<IndexAttrPair>(Attrs.size()));
PA = new (Mem) AttributeSetImpl(C, Attrs);
pImpl->AttrsLists.InsertNode(PA, InsertPoint);
}
@@ -736,9 +734,8 @@ AttributeSet AttributeSet::get(LLVMConte
if (!AS) continue;
SmallVector<std::pair<unsigned, AttributeSetNode *>, 8>::iterator
ANVI = AttrNodeVec.begin(), ANVE;
- for (const AttributeSetImpl::IndexAttrPair
- *AI = AS->getNode(0),
- *AE = AS->getNode(AS->getNumAttributes());
+ for (const IndexAttrPair *AI = AS->getNode(0),
+ *AE = AS->getNode(AS->getNumAttributes());
AI != AE; ++AI) {
ANVE = AttrNodeVec.end();
while (ANVI != ANVE && ANVI->first <= AI->first)
Modified: llvm/trunk/unittests/Support/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/CMakeLists.txt?rev=244164&r1=244163&r2=244164&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/CMakeLists.txt (original)
+++ llvm/trunk/unittests/Support/CMakeLists.txt Wed Aug 5 17:57:34 2015
@@ -41,6 +41,7 @@ add_llvm_unittest(SupportTests
TargetRegistry.cpp
ThreadLocalTest.cpp
TimeValueTest.cpp
+ TrailingObjectsTest.cpp
UnicodeTest.cpp
YAMLIOTest.cpp
YAMLParserTest.cpp
Added: llvm/trunk/unittests/Support/TrailingObjectsTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/TrailingObjectsTest.cpp?rev=244164&view=auto
==============================================================================
--- llvm/trunk/unittests/Support/TrailingObjectsTest.cpp (added)
+++ llvm/trunk/unittests/Support/TrailingObjectsTest.cpp Wed Aug 5 17:57:34 2015
@@ -0,0 +1,147 @@
+//=== - llvm/unittest/Support/TrailingObjectsTest.cpp ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/TrailingObjects.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+// This class, beyond being used by the test case, a nice
+// demonstration of the intended usage of TrailingObjects, with a
+// single trailing array.
+class Class1 final : private TrailingObjects<Class1, short> {
+ friend TrailingObjects;
+
+ unsigned NumShorts;
+
+protected:
+ size_t numTrailingObjects(OverloadToken<short>) const { return NumShorts; }
+
+ Class1(int *ShortArray, unsigned NumShorts) : NumShorts(NumShorts) {
+ std::uninitialized_copy(ShortArray, ShortArray + NumShorts,
+ getTrailingObjects<short>());
+ }
+
+public:
+ static Class1 *create(int *ShortArray, unsigned NumShorts) {
+ void *Mem = ::operator new(totalSizeToAlloc<short>(NumShorts));
+ return new (Mem) Class1(ShortArray, NumShorts);
+ }
+
+ short get(unsigned Num) const { return getTrailingObjects<short>()[Num]; }
+
+ unsigned numShorts() const { return NumShorts; }
+
+ // Pull some protected members in as public, for testability.
+ using TrailingObjects::totalSizeToAlloc;
+ using TrailingObjects::additionalSizeToAlloc;
+ using TrailingObjects::getTrailingObjects;
+};
+
+// Here, there are two singular optional object types appended.
+// Note that it fails to compile without the alignment spec.
+class LLVM_ALIGNAS(8) Class2 final : private TrailingObjects<Class2, double, short> {
+ friend TrailingObjects;
+
+ bool HasShort, HasDouble;
+
+protected:
+ size_t numTrailingObjects(OverloadToken<short>) const {
+ return HasShort ? 1 : 0;
+ }
+ size_t numTrailingObjects(OverloadToken<double>) const {
+ return HasDouble ? 1 : 0;
+ }
+
+ Class2(bool HasShort, bool HasDouble)
+ : HasShort(HasShort), HasDouble(HasDouble) {}
+
+public:
+ static Class2 *create(short S = 0, double D = 0.0) {
+ bool HasShort = S != 0;
+ bool HasDouble = D != 0.0;
+
+ void *Mem =
+ ::operator new(totalSizeToAlloc<double, short>(HasDouble, HasShort));
+ Class2 *C = new (Mem) Class2(HasShort, HasDouble);
+ if (HasShort)
+ *C->getTrailingObjects<short>() = S;
+ if (HasDouble)
+ *C->getTrailingObjects<double>() = D;
+ return C;
+ }
+
+ short getShort() const {
+ if (!HasShort)
+ return 0;
+ return *getTrailingObjects<short>();
+ }
+
+ double getDouble() const {
+ if (!HasDouble)
+ return 0.0;
+ return *getTrailingObjects<double>();
+ }
+
+ // Pull some protected members in as public, for testability.
+ using TrailingObjects::totalSizeToAlloc;
+ using TrailingObjects::additionalSizeToAlloc;
+ using TrailingObjects::getTrailingObjects;
+};
+
+TEST(TrailingObjects, OneArg) {
+ int arr[] = {1, 2, 3};
+ Class1 *C = Class1::create(arr, 3);
+ EXPECT_EQ(sizeof(Class1), sizeof(unsigned));
+ EXPECT_EQ(Class1::additionalSizeToAlloc<short>(1), sizeof(short));
+ EXPECT_EQ(Class1::additionalSizeToAlloc<short>(3), sizeof(short) * 3);
+
+ EXPECT_EQ(Class1::totalSizeToAlloc<short>(1), sizeof(Class1) + sizeof(short));
+ EXPECT_EQ(Class1::totalSizeToAlloc<short>(3),
+ sizeof(Class1) + sizeof(short) * 3);
+
+ EXPECT_EQ(C->getTrailingObjects<short>(), reinterpret_cast<short *>(C + 1));
+ EXPECT_EQ(C->get(0), 1);
+ EXPECT_EQ(C->get(2), 3);
+ delete C;
+}
+
+TEST(TrailingObjects, TwoArg) {
+ Class2 *C1 = Class2::create(4);
+ Class2 *C2 = Class2::create(0, 4.2);
+
+ EXPECT_EQ(sizeof(Class2), 8u); // due to alignment
+
+ EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(1, 0)),
+ sizeof(double));
+ EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(0, 1)),
+ sizeof(short));
+ EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(3, 1)),
+ sizeof(double) * 3 + sizeof(short));
+
+ EXPECT_EQ((Class2::totalSizeToAlloc<double, short>(1, 1)),
+ sizeof(Class2) + sizeof(double) + sizeof(short));
+
+ EXPECT_EQ(C1->getDouble(), 0);
+ EXPECT_EQ(C1->getShort(), 4);
+ EXPECT_EQ(C1->getTrailingObjects<double>(),
+ reinterpret_cast<double *>(C1 + 1));
+ EXPECT_EQ(C1->getTrailingObjects<short>(), reinterpret_cast<short *>(C1 + 1));
+
+ EXPECT_EQ(C2->getDouble(), 4.2);
+ EXPECT_EQ(C2->getShort(), 0);
+ EXPECT_EQ(C2->getTrailingObjects<double>(),
+ reinterpret_cast<double *>(C2 + 1));
+ EXPECT_EQ(C2->getTrailingObjects<short>(),
+ reinterpret_cast<short *>(reinterpret_cast<double *>(C2 + 1) + 1));
+ delete C1;
+ delete C2;
+}
+}
More information about the llvm-commits
mailing list