[llvm] e823068 - [Support] Add support RTTI support for open class hierarchies.

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 13 12:53:02 PDT 2020


Author: Lang Hames
Date: 2020-04-13T12:52:44-07:00
New Revision: e823068306e98e91f726c58a2112784f5ead245b

URL: https://github.com/llvm/llvm-project/commit/e823068306e98e91f726c58a2112784f5ead245b
DIFF: https://github.com/llvm/llvm-project/commit/e823068306e98e91f726c58a2112784f5ead245b.diff

LOG: [Support] Add support RTTI support for open class hierarchies.

This patch extracts the RTTI part of llvm::ErrorInfo into its own class
(RTTIExtends) so that it can be used in other non-error hierarchies, and makes
it compatible with the existing LLVM RTTI function templates (isa, cast,
dyn_cast, dyn_cast_or_null) by adding the classof method.

Differential Revision: https://reviews.llvm.org/D39111

Added: 
    llvm/include/llvm/Support/ExtensibleRTTI.h
    llvm/unittests/Support/ExtensibleRTTITest.cpp

Modified: 
    llvm/docs/HowToSetUpLLVMStyleRTTI.rst
    llvm/lib/Support/CMakeLists.txt
    llvm/unittests/Support/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/llvm/docs/HowToSetUpLLVMStyleRTTI.rst b/llvm/docs/HowToSetUpLLVMStyleRTTI.rst
index 222ea890322e..3511466a424b 100644
--- a/llvm/docs/HowToSetUpLLVMStyleRTTI.rst
+++ b/llvm/docs/HowToSetUpLLVMStyleRTTI.rst
@@ -412,3 +412,58 @@ Rules of Thumb
 #. For each class in the hierarchy that has children, implement a
    ``classof`` that checks a range of the first child's ``Kind`` and the
    last child's ``Kind``.
+
+RTTI for Open Class Hierarchies
+===============================
+
+Sometimes it is not possible to know all types in a hierarchy ahead of time.
+For example, in the shapes hierarchy described above the authors may have
+wanted their code to work for user defined shapes too. To support use cases
+that require open hierarchies LLVM provides the ``RTTIRoot`` and
+``RTTIExtends`` utilities.
+
+The ``RTTIRoot`` class describes an interface for performing RTTI checks. The
+``RTTIExtends`` class template provides an implementation of this interface
+for classes derived from ``RTTIRoot``. ``RTTIExtends`` uses the "`Curiously
+Recurring Template Idiom`_", taking the class being defined as its first
+template argument and the parent class as the second argument. Any class that
+uses ``RTTIExtends`` must define a ``static char ID`` member, the address of
+which will be used to identify the type.
+
+This open-hierarchy RTTI support should only be used if your use case requries
+it. Otherwise the standard LLVM RTTI system should be preferred.
+
+.. _`Curiously Recurring Template Idiom`:
+https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
+
+E.g.
+
+.. code-block:: c++
+
+   class Shape : public RTTIExtends<Shape, RTTIRoot> {
+   public:
+     static char ID;
+     virtual double computeArea() = 0;
+   };
+
+   class Square : public RTTIExtends<Square, Shape> {
+     double SideLength;
+   public:
+     static char ID;
+
+     Square(double S) : SideLength(S) {}
+     double computeArea() override;
+   };
+
+   class Circle : public RTTIExtends<Circle, Shape> {
+     double Radius;
+   public:
+     static char ID;
+
+     Circle(double R) : Radius(R) {}
+     double computeArea() override;
+   };
+
+   char Shape::ID = 0;
+   char Square::ID = 0;
+   char Circle::ID = 0;

diff  --git a/llvm/include/llvm/Support/ExtensibleRTTI.h b/llvm/include/llvm/Support/ExtensibleRTTI.h
new file mode 100644
index 000000000000..6b8510ce759f
--- /dev/null
+++ b/llvm/include/llvm/Support/ExtensibleRTTI.h
@@ -0,0 +1,135 @@
+//===-- llvm/Support/ExtensibleRTTI.h - ExtensibleRTTI support --*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// \file
+//
+// Defines an extensible RTTI mechanism designed to work with Casting.h.
+//
+// Extensible RTTI 
diff ers from LLVM's primary RTTI mechanism (see
+// llvm.org/docs/HowToSetUpLLVMStyleRTTI.html) by supporting open type
+// hierarchies, where new types can be added from outside libraries without
+// needing to change existing code. LLVM's primary RTTI mechanism should be
+// preferred where possible, but where open hierarchies are needed this system
+// can be used.
+//
+// The RTTIRoot class defines methods for comparing type ids. Implementations
+// of these methods can be injected into new classes using the RTTIExtends
+// class template.
+//
+// E.g.
+//
+//   @code{.cpp}
+//   class MyBaseClass : public RTTIExtends<MyBaseClass, RTTIRoot> {
+//   public:
+//     static char ID;
+//     virtual void foo() = 0;
+//   };
+//
+//   class MyDerivedClass1 : public RTTIExtends<MyDerivedClass1, MyBaseClass> {
+//   public:
+//     static char ID;
+//     void foo() override {}
+//   };
+//
+//   class MyDerivedClass2 : public RTTIExtends<MyDerivedClass2, MyBaseClass> {
+//   public:
+//     static char ID;
+//     void foo() override {}
+//   };
+//
+//   char MyBaseClass::ID = 0;
+//   char MyDerivedClass1::ID = 0;
+//   char MyDerivedClass2:: ID = 0;
+//
+//   void fn() {
+//     std::unique_ptr<MyBaseClass> B = llvm::make_unique<MyDerivedClass1>();
+//     llvm::outs() << isa<MyBaseClass>(B) << "\n"; // Outputs "1".
+//     llvm::outs() << isa<MyDerivedClass1>(B) << "\n"; // Outputs "1".
+//     llvm::outs() << isa<MyDerivedClass2>(B) << "\n"; // Outputs "0'.
+//   }
+//
+//   @endcode
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_EXTENSIBLERTTI_H
+#define LLVM_SUPPORT_EXTENSIBLERTTI_H
+
+namespace llvm {
+
+template <typename ThisT, typename ParentT> class RTTIExtends;
+
+/// Base class for the extensible RTTI hierarchy.
+///
+/// This class defines virtual methods, dynamicClassID and isA, that enable
+/// type comparisons.
+class RTTIRoot {
+public:
+  virtual ~RTTIRoot() = default;
+
+  /// Returns the class ID for this type.
+  static const void *classID() { return &ID; }
+
+  /// Returns the class ID for the dynamic type of this RTTIRoot instance.
+  virtual const void *dynamicClassID() const = 0;
+
+  /// Returns true if this class's ID matches the given class ID.
+  virtual bool isA(const void *const ClassID) const {
+    return ClassID == classID();
+  }
+
+  /// Check whether this instance is a subclass of QueryT.
+  template <typename QueryT>
+  bool isA() const { return isA(QueryT::classID()); }
+
+private:
+  virtual void anchor();
+
+  static char ID;
+};
+
+/// Inheritance utility for extensible RTTI.
+///
+/// Supports single inheritance only: A class can only have one
+/// ExtensibleRTTI-parent (i.e. a parent for which the isa<> test will work),
+/// though it can have many non-ExtensibleRTTI parents.
+///
+/// RTTIExtents uses CRTP so the first template argument to RTTIExtends is the
+/// newly introduced type, and the *second* argument is the parent class.
+///
+/// class MyType : public RTTIExtends<MyType, RTTIRoot> {
+/// public:
+///   static char ID;
+/// };
+///
+/// class MyDerivedType : public RTTIExtends<MyDerivedType, MyType> {
+/// public:
+///   static char ID;
+/// };
+///
+template <typename ThisT, typename ParentT>
+class RTTIExtends : public ParentT {
+public:
+  // Inherit constructors from ParentT.
+  using ParentT::ParentT;
+
+  static const void *classID() { return &ThisT::ID; }
+
+  const void *dynamicClassID() const override { return &ThisT::ID; }
+
+  bool isA(const void *const ClassID) const override {
+    return ClassID == classID() || ParentT::isA(ClassID);
+  }
+
+  static bool classof(const RTTIRoot *R) { return R->isA<ThisT>(); }
+};
+
+} // end namespace llvm
+
+#endif // LLVM_SUPPORT_EXTENSIBLERTTI_H

diff  --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 897f183b660f..b2071bee4d2d 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -91,6 +91,7 @@ add_llvm_component_library(LLVMSupport
   ELFAttributes.cpp
   Error.cpp
   ErrorHandling.cpp
+  ExtensibleRTTI.cpp
   FileCheck.cpp
   FileCollector.cpp
   FileUtilities.cpp

diff  --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index b6ed410c0f7f..0c3211333836 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -32,6 +32,7 @@ add_llvm_unittest(SupportTests
   ErrnoTest.cpp
   ErrorOrTest.cpp
   ErrorTest.cpp
+  ExtensibleRTTITest.cpp
   FileCheckTest.cpp
   FileCollectorTest.cpp
   FileOutputBufferTest.cpp

diff  --git a/llvm/unittests/Support/ExtensibleRTTITest.cpp b/llvm/unittests/Support/ExtensibleRTTITest.cpp
new file mode 100644
index 000000000000..84d2a6f1ae45
--- /dev/null
+++ b/llvm/unittests/Support/ExtensibleRTTITest.cpp
@@ -0,0 +1,86 @@
+//===------ unittests/ExtensibleRTTITest.cpp - Extensible RTTI Tests ------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/ExtensibleRTTI.h"
+#include "llvm/Support/Casting.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+class MyBaseType : public RTTIExtends<MyBaseType, RTTIRoot> {
+public:
+  static char ID;
+};
+
+class MyDerivedType : public RTTIExtends<MyDerivedType, MyBaseType> {
+public:
+  static char ID;
+};
+
+class MyOtherDerivedType : public RTTIExtends<MyOtherDerivedType, MyBaseType> {
+public:
+  static char ID;
+};
+
+class MyDeeperDerivedType
+    : public RTTIExtends<MyDeeperDerivedType, MyDerivedType> {
+public:
+  static char ID;
+};
+
+char MyBaseType::ID = 0;
+char MyDerivedType::ID = 0;
+char MyOtherDerivedType::ID = 0;
+char MyDeeperDerivedType::ID = 0;
+
+TEST(ExtensibleRTTI, isa) {
+  MyBaseType B;
+  MyDerivedType D;
+  MyDeeperDerivedType DD;
+
+  EXPECT_TRUE(isa<MyBaseType>(B));
+  EXPECT_FALSE(isa<MyDerivedType>(B));
+  EXPECT_FALSE(isa<MyOtherDerivedType>(B));
+  EXPECT_FALSE(isa<MyDeeperDerivedType>(B));
+
+  EXPECT_TRUE(isa<MyBaseType>(D));
+  EXPECT_TRUE(isa<MyDerivedType>(D));
+  EXPECT_FALSE(isa<MyOtherDerivedType>(D));
+  EXPECT_FALSE(isa<MyDeeperDerivedType>(D));
+
+  EXPECT_TRUE(isa<MyBaseType>(DD));
+  EXPECT_TRUE(isa<MyDerivedType>(DD));
+  EXPECT_FALSE(isa<MyOtherDerivedType>(DD));
+  EXPECT_TRUE(isa<MyDeeperDerivedType>(DD));
+}
+
+TEST(ExtensibleRTTI, cast) {
+  MyDerivedType D;
+  MyBaseType &BD = D;
+
+  cast<MyBaseType>(D);
+  cast<MyBaseType>(BD);
+  cast<MyDerivedType>(BD);
+}
+
+TEST(ExtensibleRTTI, dyn_cast) {
+  MyBaseType B;
+  MyDerivedType D;
+  MyBaseType &BD = D;
+
+  EXPECT_EQ(dyn_cast<MyDerivedType>(&B), nullptr);
+  EXPECT_EQ(dyn_cast<MyDerivedType>(&D), &D);
+  EXPECT_EQ(dyn_cast<MyBaseType>(&BD), &BD);
+  EXPECT_EQ(dyn_cast<MyDerivedType>(&BD), &D);
+}
+
+} // namespace


        


More information about the llvm-commits mailing list