From df1cf99600fdd9e2d20e4511121855eb43fb8c1e 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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@256553 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/AlignOf.h | 37 ++++++++++++++++++++++++++----- unittests/Support/AlignOfTest.cpp | 20 +++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/include/llvm/Support/AlignOf.h b/include/llvm/Support/AlignOf.h index 07da02d063c..5268c8d1698 100644 --- a/include/llvm/Support/AlignOf.h +++ b/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/unittests/Support/AlignOfTest.cpp b/unittests/Support/AlignOfTest.cpp index e0859fc747f..be208f7d28e 100644 --- a/unittests/Support/AlignOfTest.cpp +++ b/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) { -- 2.34.1