[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
This commit is contained in:
Lang Hames 2020-04-10 17:23:20 -07:00
parent ec99d6e62f
commit e823068306
5 changed files with 278 additions and 0 deletions

View File

@ -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;

View File

@ -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 differs 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

View File

@ -91,6 +91,7 @@ add_llvm_component_library(LLVMSupport
ELFAttributes.cpp
Error.cpp
ErrorHandling.cpp
ExtensibleRTTI.cpp
FileCheck.cpp
FileCollector.cpp
FileUtilities.cpp

View File

@ -32,6 +32,7 @@ add_llvm_unittest(SupportTests
ErrnoTest.cpp
ErrorOrTest.cpp
ErrorTest.cpp
ExtensibleRTTITest.cpp
FileCheckTest.cpp
FileCollectorTest.cpp
FileOutputBufferTest.cpp

View File

@ -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