Casting.h: Automatically handle isa<Base>(Derived).

Additionally, all such cases are handled with no dynamic check.

All `classof()` of the form

    class Foo {
      [...]
      static bool classof(const Bar *) { return true; }
      [...]
    }

where Foo is an ancestor of Bar are no longer necessary.
Don't write them!

Note: The exact test is `is_base_of<Foo, Bar>`, which is non-strict, so
that Foo is considered an ancestor of itself.

This leads to the following rule of thumb for LLVM-style RTTI:

    The argument type of `classof()` should be a strict ancestor.

For more information about implementing LLVM-style RTTI, see
docs/HowToSetUpLLVMStyleRTTI.rst

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@165765 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Sean Silva 2012-10-11 23:30:40 +00:00
parent 8ac1995456
commit 8b8fa7b2f4
2 changed files with 63 additions and 1 deletions

View File

@ -15,6 +15,7 @@
#ifndef LLVM_SUPPORT_CASTING_H
#define LLVM_SUPPORT_CASTING_H
#include "llvm/Support/type_traits.h"
#include <cassert>
namespace llvm {
@ -44,13 +45,23 @@ template<typename From> struct simplify_type<const From> {
// The core of the implementation of isa<X> is here; To and From should be
// the names of classes. This template can be specialized to customize the
// implementation of isa<> without rewriting it from scratch.
template <typename To, typename From>
template <typename To, typename From, typename Enabler = void>
struct isa_impl {
static inline bool doit(const From &Val) {
return To::classof(&Val);
}
};
/// \brief Always allow upcasts, and perform no dynamic check for them.
template <typename To, typename From>
struct isa_impl<To, From,
typename llvm::enable_if_c<
llvm::is_base_of<To, From>::value
>::type
> {
static inline bool doit(const From &) { return true; }
};
template <typename To, typename From> struct isa_impl_cl {
static inline bool doit(const From &Val) {
return isa_impl<To, From>::doit(Val);

View File

@ -153,3 +153,54 @@ const bar *B2 = &B;
} // anonymous namespace
bar *llvm::fub() { return 0; }
namespace {
namespace inferred_upcasting {
// This test case verifies correct behavior of inferred upcasts when the
// types are statically known to be OK to upcast. This is the case when,
// for example, Derived inherits from Base, and we do `isa<Base>(Derived)`.
// Note: This test will actually fail to compile without inferred
// upcasting.
class Base {
public:
// No classof. We are testing that the upcast is inferred.
Base() {}
};
class Derived : public Base {
public:
Derived() {}
};
// Even with no explicit classof() in Base, we should still be able to cast
// Derived to its base class.
TEST(CastingTest, UpcastIsInferred) {
Derived D;
EXPECT_TRUE(isa<Base>(D));
Base *BP = dyn_cast<Base>(&D);
EXPECT_TRUE(BP != NULL);
}
// This test verifies that the inferred upcast takes precedence over an
// explicitly written one. This is important because it verifies that the
// dynamic check gets optimized away.
class UseInferredUpcast {
public:
int Dummy;
static bool classof(const UseInferredUpcast *) {
return false;
}
};
TEST(CastingTest, InferredUpcastTakesPrecedence) {
UseInferredUpcast UIU;
// Since the explicit classof() returns false, this will fail if the
// explicit one is used.
EXPECT_TRUE(isa<UseInferredUpcast>(&UIU));
}
} // end namespace inferred_upcasting
} // end anonymous namespace