[ADT] Teach alignment helpers to work correctly for abstract classes.
authorChandler Carruth <chandlerc@gmail.com>
Tue, 29 Dec 2015 09:52:41 +0000 (09:52 +0000)
committerChandler Carruth <chandlerc@gmail.com>
Tue, 29 Dec 2015 09:52:41 +0000 (09:52 +0000)
This is necessary to use them as part of pointer traits and is generally
useful. I've added unit test coverage to isolate and ensure this works
correctly.

I'll watch the build bots to try to see if any compilers can't tolerate
this bit of magic (and much credit goes to Richard Smith for coming up
with this magical production!) but give a shout if you see issues.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@256553 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Support/AlignOf.h
unittests/Support/AlignOfTest.cpp

index 07da02d063c7d863b7848c83a5e9ca93398aa9cc..5268c8d16986801b4f5f3fcf707369d28cf134b0 100644 (file)
 
 #include "llvm/Support/Compiler.h"
 #include <cstddef>
+#include <type_traits>
 
 namespace llvm {
-template <typename T>
+
+namespace detail {
+
+// For everything other than an abstract class we can calulate alignment by
+// building a class with a single character and a member of the given type.
+template <typename T, bool = std::is_abstract<T>::value>
 struct AlignmentCalcImpl {
   char x;
 #if defined(_MSC_VER)
@@ -35,6 +41,25 @@ private:
   AlignmentCalcImpl() {} // Never instantiate.
 };
 
+// Abstract base class helper, this will have the minimal alignment and size
+// for any abstract class. We don't even define its destructor because this
+// type should never be used in a way that requires it.
+struct AlignmentCalcImplBase {
+  virtual ~AlignmentCalcImplBase() = 0;
+};
+
+// When we have an abstract class type, specialize the alignment computation
+// engine to create another abstract class that derives from both an empty
+// abstract base class and the provided type. This has the same effect as the
+// above except that it handles the fact that we can't actually create a member
+// of type T.
+template <typename T>
+struct AlignmentCalcImpl<T, true> : AlignmentCalcImplBase, T {
+  virtual ~AlignmentCalcImpl() = 0;
+};
+
+} // End detail namespace.
+
 /// AlignOf - A templated class that contains an enum value representing
 ///  the alignment of the template argument.  For example,
 ///  AlignOf<int>::Alignment represents the alignment of type "int".  The
@@ -50,11 +75,13 @@ struct AlignOf {
   //   llvm::AlignOf<Y>::<anonymous>' [-Wenum-compare]
   // by using constexpr instead of enum.
   // (except on MSVC, since it doesn't support constexpr yet).
-  static constexpr unsigned Alignment =
-      static_cast<unsigned int>(sizeof(AlignmentCalcImpl<T>) - sizeof(T));
+  static constexpr unsigned Alignment = static_cast<unsigned int>(
+      sizeof(detail::AlignmentCalcImpl<T>) - sizeof(T));
 #else
-  enum { Alignment =
-         static_cast<unsigned int>(sizeof(AlignmentCalcImpl<T>) - sizeof(T)) };
+  enum {
+    Alignment = static_cast<unsigned int>(sizeof(detail::AlignmentCalcImpl<T>) -
+                                          sizeof(T))
+  };
 #endif
   enum { Alignment_GreaterEqual_2Bytes = Alignment >= 2 ? 1 : 0 };
   enum { Alignment_GreaterEqual_4Bytes = Alignment >= 4 ? 1 : 0 };
index e0859fc747f4c2f0b5340738c161ed3fbc55f0f6..be208f7d28ea8707ae7fc5882cde0c8f569e519d 100644 (file)
@@ -89,6 +89,22 @@ V6::~V6() {}
 V7::~V7() {}
 V8::~V8() {}
 
+struct Abstract1 {
+  virtual ~Abstract1() {}
+  virtual void method() = 0;
+
+  char c;
+};
+
+struct Abstract2 : Abstract1 {
+  virtual ~Abstract2() {}
+  double d;
+};
+
+struct Final final : Abstract2 {
+  void method() override {}
+};
+
 // Ensure alignment is a compile-time constant.
 char LLVM_ATTRIBUTE_UNUSED test_arr1
   [AlignOf<char>::Alignment > 0]
@@ -174,6 +190,10 @@ TEST(AlignOfTest, BasicAlignmentInvariants) {
   EXPECT_LE(alignOf<V1>(),     alignOf<V6>());
   EXPECT_LE(alignOf<V1>(),     alignOf<V7>());
   EXPECT_LE(alignOf<V1>(),     alignOf<V8>());
+
+  EXPECT_LE(alignOf<char>(), alignOf<Abstract1>());
+  EXPECT_LE(alignOf<double>(), alignOf<Abstract2>());
+  EXPECT_LE(alignOf<Abstract2>(), alignOf<Final>());
 }
 
 TEST(AlignOfTest, BasicAlignedArray) {