Support: Add *cast_or_null<> for pointer wrappers

Fill in omission of `cast_or_null<>` and `dyn_cast_or_null<>` for types
that wrap pointers (e.g., smart pointers).

Type traits need to be slightly stricter than for `cast<>` and
`dyn_cast<>` to resolve ambiguities with simple types.

There didn't seem to be any unit tests for pointer wrappers, so I tested
`isa<>`, `cast<>`, and `dyn_cast<>` while I was in there.

This only supports pointer wrappers with a conversion to `bool` to check
for null.  If in the future it's useful to support wrappers without such
a conversion, it should be a straightforward incremental step to use the
`simplify_type` machinery for the null check.  In that case, the unit
tests should be updated to remove the `operator bool()` from the
`pointer_wrappers::PTy`.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@222644 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Duncan P. N. Exon Smith 2014-11-24 03:13:02 +00:00
parent 84d3252d1b
commit 5deb1bf97f
2 changed files with 130 additions and 0 deletions

View File

@ -242,6 +242,26 @@ inline typename cast_retty<X, Y *>::ret_type cast(Y *Val) {
// cast_or_null<X> - Functionally identical to cast, except that a null value is
// accepted.
//
template <class X, class Y>
LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if<
!is_simple_type<Y>::value, typename cast_retty<X, const Y>::ret_type>::type
cast_or_null(const Y &Val) {
if (!Val)
return nullptr;
assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
return cast<X>(Val);
}
template <class X, class Y>
LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if<
!is_simple_type<Y>::value, typename cast_retty<X, Y>::ret_type>::type
cast_or_null(Y &Val) {
if (!Val)
return nullptr;
assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
return cast<X>(Val);
}
template <class X, class Y>
LLVM_ATTRIBUTE_UNUSED_RESULT inline typename cast_retty<X, Y *>::ret_type
cast_or_null(Y *Val) {
@ -281,6 +301,20 @@ dyn_cast(Y *Val) {
// dyn_cast_or_null<X> - Functionally identical to dyn_cast, except that a null
// value is accepted.
//
template <class X, class Y>
LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if<
!is_simple_type<Y>::value, typename cast_retty<X, const Y>::ret_type>::type
dyn_cast_or_null(const Y &Val) {
return (Val && isa<X>(Val)) ? cast<X>(Val) : nullptr;
}
template <class X, class Y>
LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if<
!is_simple_type<Y>::value, typename cast_retty<X, Y>::ret_type>::type
dyn_cast_or_null(Y &Val) {
return (Val && isa<X>(Val)) ? cast<X>(Val) : nullptr;
}
template <class X, class Y>
LLVM_ATTRIBUTE_UNUSED_RESULT inline typename cast_retty<X, Y *>::ret_type
dyn_cast_or_null(Y *Val) {

View File

@ -232,3 +232,99 @@ namespace TemporaryCast {
struct pod {};
IllegalCast *testIllegalCast() { return cast<foo>(pod()); }
}
namespace {
namespace pointer_wrappers {
struct Base {
bool IsDerived;
Base(bool IsDerived = false) : IsDerived(IsDerived) {}
};
struct Derived : Base {
Derived() : Base(true) {}
static bool classof(const Base *B) { return B->IsDerived; }
};
class PTy {
Base *B;
public:
PTy(Base *B) : B(B) {}
LLVM_EXPLICIT operator bool() const { return get(); }
Base *get() const { return B; }
};
} // end namespace pointer_wrappers
} // end namespace
namespace llvm {
template <> struct simplify_type<pointer_wrappers::PTy> {
typedef pointer_wrappers::Base *SimpleType;
static SimpleType getSimplifiedValue(pointer_wrappers::PTy &P) {
return P.get();
}
};
template <> struct simplify_type<const pointer_wrappers::PTy> {
typedef pointer_wrappers::Base *SimpleType;
static SimpleType getSimplifiedValue(const pointer_wrappers::PTy &P) {
return P.get();
}
};
} // end namespace llvm
namespace {
namespace pointer_wrappers {
// Some objects.
pointer_wrappers::Base B;
pointer_wrappers::Derived D;
// Mutable "smart" pointers.
pointer_wrappers::PTy MN(nullptr);
pointer_wrappers::PTy MB(&B);
pointer_wrappers::PTy MD(&D);
// Const "smart" pointers.
const pointer_wrappers::PTy CN(nullptr);
const pointer_wrappers::PTy CB(&B);
const pointer_wrappers::PTy CD(&D);
TEST(CastingTest, smart_isa) {
EXPECT_TRUE(!isa<pointer_wrappers::Derived>(MB));
EXPECT_TRUE(!isa<pointer_wrappers::Derived>(CB));
EXPECT_TRUE(isa<pointer_wrappers::Derived>(MD));
EXPECT_TRUE(isa<pointer_wrappers::Derived>(CD));
}
TEST(CastingTest, smart_cast) {
EXPECT_TRUE(cast<pointer_wrappers::Derived>(MD) == &D);
EXPECT_TRUE(cast<pointer_wrappers::Derived>(CD) == &D);
}
TEST(CastingTest, smart_cast_or_null) {
EXPECT_TRUE(cast_or_null<pointer_wrappers::Derived>(MN) == nullptr);
EXPECT_TRUE(cast_or_null<pointer_wrappers::Derived>(CN) == nullptr);
EXPECT_TRUE(cast_or_null<pointer_wrappers::Derived>(MD) == &D);
EXPECT_TRUE(cast_or_null<pointer_wrappers::Derived>(CD) == &D);
}
TEST(CastingTest, smart_dyn_cast) {
EXPECT_TRUE(dyn_cast<pointer_wrappers::Derived>(MB) == nullptr);
EXPECT_TRUE(dyn_cast<pointer_wrappers::Derived>(CB) == nullptr);
EXPECT_TRUE(dyn_cast<pointer_wrappers::Derived>(MD) == &D);
EXPECT_TRUE(dyn_cast<pointer_wrappers::Derived>(CD) == &D);
}
TEST(CastingTest, smart_dyn_cast_or_null) {
EXPECT_TRUE(dyn_cast_or_null<pointer_wrappers::Derived>(MN) == nullptr);
EXPECT_TRUE(dyn_cast_or_null<pointer_wrappers::Derived>(CN) == nullptr);
EXPECT_TRUE(dyn_cast_or_null<pointer_wrappers::Derived>(MB) == nullptr);
EXPECT_TRUE(dyn_cast_or_null<pointer_wrappers::Derived>(CB) == nullptr);
EXPECT_TRUE(dyn_cast_or_null<pointer_wrappers::Derived>(MD) == &D);
EXPECT_TRUE(dyn_cast_or_null<pointer_wrappers::Derived>(CD) == &D);
}
} // end namespace pointer_wrappers
} // end namespace