LLVM avoids using C++’s built in RTTI. Instead, it pervasively uses its
own hand-rolled form of RTTI which is much more efficient and flexible,
although it requires a bit more work from you as a class author.

A description of how to use LLVM-style RTTI from a client’s perspective is
given in the Programmer’s Manual. This
document, in contrast, discusses the steps you need to take as a class
hierarchy author to make LLVM-style RTTI available to your clients.

Before diving in, make sure that you are familiar with the Object Oriented
Programming concept of “is-a”.

You will usually want to keep the Kind member encapsulated and
private, but let the enum ShapeKind be public along with providing a
getKind() method. This is convenient for clients so that they can do
a switch over the enum.

A common naming convention is that these enums are “kind”s, to avoid
ambiguity with the words “type” or “class” which have overloaded meanings
in many contexts within LLVM. Sometimes there will be a natural name for
it, like “opcode”. Don’t bikeshed over this; when in doubt use Kind.

You might wonder why the Kind enum doesn’t have an entry for
Shape. The reason for this is that since Shape is abstract
(computeArea()=0;), you will never actually have non-derived
instances of exactly that class (only subclasses). See Concrete Bases
and Deeper Hierarchies for information on how to deal with
non-abstract bases. It’s worth mentioning here that unlike
dynamic_cast<>, LLVM-style RTTI can be used (and is often used) for
classes that don’t have v-tables.

Next, you need to make sure that the Kind gets initialized to the
value corresponding to the dynamic type of the class. Typically, you will
want to have it be an argument to the constructor of the base class, and
then pass in the respective XXXKind from subclass constructors.

Finally, you need to inform LLVM’s RTTI templates how to dynamically
determine the type of a class (i.e. whether the isa<>/dyn_cast<>
should succeed). The default “99.9% of use cases” way to accomplish this
is through a small static member function classof. In order to have
proper context for an explanation, we will display this code first, and
then below describe each part:

The job of classof is to dynamically determine whether an object of
a base class is in fact of a particular derived class. In order to
downcast a type Base to a type Derived, there needs to be a
classof in Derived which will accept an object of type Base.

To be concrete, consider the following code:

Shape*S=...;if(isa<Circle>(S)){/* do something ... */}

The code of the isa<> test in this code will eventually boil
down—after template instantiation and some other machinery—to a
check roughly like Circle::classof(S). For more information, see
The Contract of classof.

The argument to classof should always be an ancestor class because
the implementation has logic to allow and optimize away
upcasts/up-isa<>’s automatically. It is as though every class
Foo automatically has a classof like:

Note that this is the reason that we did not need to introduce a
classof into Shape: all relevant classes derive from Shape,
and Shape itself is abstract (has no entry in the Kind enum),
so this notional inferred classof is all we need. See Concrete
Bases and Deeper Hierarchies for more information about how to extend
this example to more general hierarchies.

Although for this small example setting up LLVM-style RTTI seems like a lot
of “boilerplate”, if your classes are doing anything interesting then this
will end up being a tiny fraction of the code.

The reason that we need to test a range like this instead of just equality
is that both SpecialSquare and OtherSpecialSquare “is-a”
Square, and so classof needs to return true for them.

This approach can be made to scale to arbitrarily deep hierarchies. The
trick is that you arrange the enum values so that they correspond to a
preorder traversal of the class hierarchy tree. With that arrangement, all
subclass tests can be done with two comparisons as shown above. If you just
list the class hierarchy like a list of bullet points, you’ll get the
ordering right:

As the comment indicates, this code contains a bug. A straightforward and
non-clever way to avoid this is to introduce an explicit SK_LastSquare
entry in the enum when adding the first subclass(es). For example, we could
rewrite the example at the beginning of Concrete Bases and Deeper
Hierarchies as:

To be more precise, let classof be inside a class C. Then the
contract for classof is “return true if the dynamic type of the
argument is-a C”. As long as your implementation fulfills this
contract, you can tweak and optimize it as much as you want.

For example, LLVM-style RTTI can work fine in the presence of
multiple-inheritance by defining an appropriate classof.
An example of this in practice is
Decl vs.
DeclContext
inside Clang.
The Decl hierarchy is done very similarly to the example setup
demonstrated in this tutorial.
The key part is how to then incorporate DeclContext: all that is needed
is in boolDeclContext::classof(constDecl*), which asks the question
“Given a Decl, how can I determine if it is-a DeclContext?”.
It answers this with a simple switch over the set of Decl “kinds”, and
returning true for ones that are known to be DeclContext’s.

The Kind enum should have one entry per concrete class, ordered
according to a preorder traversal of the inheritance tree.

The argument to classof should be a constBase*, where Base
is some ancestor in the inheritance hierarchy. The argument should
never be a derived class or the class itself: the template machinery
for isa<> already handles this case and optimizes it.

For each class in the hierarchy that has no children, implement a
classof that checks only against its Kind.

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.