From 8db8e8b86f486baffb22445b5cb3f24561059585 Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Tue, 29 Dec 2015 09:52:41 +0000 Subject: [PATCH] [ADT] Teach alignment helpers to work correctly for abstract classes. 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. llvm-svn: 256553 --- llvm/include/llvm/Support/AlignOf.h | 37 ++++++++++++++++++++++---- llvm/unittests/Support/AlignOfTest.cpp | 20 ++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/llvm/include/llvm/Support/AlignOf.h b/llvm/include/llvm/Support/AlignOf.h index 07da02d063c7..5268c8d16986 100644 --- a/llvm/include/llvm/Support/AlignOf.h +++ b/llvm/include/llvm/Support/AlignOf.h @@ -17,9 +17,15 @@ #include "llvm/Support/Compiler.h" #include +#include namespace llvm { -template + +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 ::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 +struct AlignmentCalcImpl : 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::Alignment represents the alignment of type "int". The @@ -50,11 +75,13 @@ struct AlignOf { // llvm::AlignOf::' [-Wenum-compare] // by using constexpr instead of enum. // (except on MSVC, since it doesn't support constexpr yet). - static constexpr unsigned Alignment = - static_cast(sizeof(AlignmentCalcImpl) - sizeof(T)); + static constexpr unsigned Alignment = static_cast( + sizeof(detail::AlignmentCalcImpl) - sizeof(T)); #else - enum { Alignment = - static_cast(sizeof(AlignmentCalcImpl) - sizeof(T)) }; + enum { + Alignment = static_cast(sizeof(detail::AlignmentCalcImpl) - + sizeof(T)) + }; #endif enum { Alignment_GreaterEqual_2Bytes = Alignment >= 2 ? 1 : 0 }; enum { Alignment_GreaterEqual_4Bytes = Alignment >= 4 ? 1 : 0 }; diff --git a/llvm/unittests/Support/AlignOfTest.cpp b/llvm/unittests/Support/AlignOfTest.cpp index e0859fc747f4..be208f7d28ea 100644 --- a/llvm/unittests/Support/AlignOfTest.cpp +++ b/llvm/unittests/Support/AlignOfTest.cpp @@ -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::Alignment > 0] @@ -174,6 +190,10 @@ TEST(AlignOfTest, BasicAlignmentInvariants) { EXPECT_LE(alignOf(), alignOf()); EXPECT_LE(alignOf(), alignOf()); EXPECT_LE(alignOf(), alignOf()); + + EXPECT_LE(alignOf(), alignOf()); + EXPECT_LE(alignOf(), alignOf()); + EXPECT_LE(alignOf(), alignOf()); } TEST(AlignOfTest, BasicAlignedArray) {