From 5deb1bf97fbe56de1c8c9aab32f11fdac4f2a5fb Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Mon, 24 Nov 2014 03:13:02 +0000 Subject: [PATCH] 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 --- include/llvm/Support/Casting.h | 34 ++++++++++++ unittests/Support/Casting.cpp | 96 ++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/include/llvm/Support/Casting.h b/include/llvm/Support/Casting.h index beed31a4084..6ba5efa4755 100644 --- a/include/llvm/Support/Casting.h +++ b/include/llvm/Support/Casting.h @@ -242,6 +242,26 @@ inline typename cast_retty::ret_type cast(Y *Val) { // cast_or_null - Functionally identical to cast, except that a null value is // accepted. // +template +LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if< + !is_simple_type::value, typename cast_retty::ret_type>::type +cast_or_null(const Y &Val) { + if (!Val) + return nullptr; + assert(isa(Val) && "cast_or_null() argument of incompatible type!"); + return cast(Val); +} + +template +LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if< + !is_simple_type::value, typename cast_retty::ret_type>::type +cast_or_null(Y &Val) { + if (!Val) + return nullptr; + assert(isa(Val) && "cast_or_null() argument of incompatible type!"); + return cast(Val); +} + template LLVM_ATTRIBUTE_UNUSED_RESULT inline typename cast_retty::ret_type cast_or_null(Y *Val) { @@ -281,6 +301,20 @@ dyn_cast(Y *Val) { // dyn_cast_or_null - Functionally identical to dyn_cast, except that a null // value is accepted. // +template +LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if< + !is_simple_type::value, typename cast_retty::ret_type>::type +dyn_cast_or_null(const Y &Val) { + return (Val && isa(Val)) ? cast(Val) : nullptr; +} + +template +LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if< + !is_simple_type::value, typename cast_retty::ret_type>::type +dyn_cast_or_null(Y &Val) { + return (Val && isa(Val)) ? cast(Val) : nullptr; +} + template LLVM_ATTRIBUTE_UNUSED_RESULT inline typename cast_retty::ret_type dyn_cast_or_null(Y *Val) { diff --git a/unittests/Support/Casting.cpp b/unittests/Support/Casting.cpp index 88c7d19aa46..3218189eeab 100644 --- a/unittests/Support/Casting.cpp +++ b/unittests/Support/Casting.cpp @@ -232,3 +232,99 @@ namespace TemporaryCast { struct pod {}; IllegalCast *testIllegalCast() { return cast(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 { + typedef pointer_wrappers::Base *SimpleType; + static SimpleType getSimplifiedValue(pointer_wrappers::PTy &P) { + return P.get(); + } +}; +template <> struct simplify_type { + 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(MB)); + EXPECT_TRUE(!isa(CB)); + EXPECT_TRUE(isa(MD)); + EXPECT_TRUE(isa(CD)); +} + +TEST(CastingTest, smart_cast) { + EXPECT_TRUE(cast(MD) == &D); + EXPECT_TRUE(cast(CD) == &D); +} + +TEST(CastingTest, smart_cast_or_null) { + EXPECT_TRUE(cast_or_null(MN) == nullptr); + EXPECT_TRUE(cast_or_null(CN) == nullptr); + EXPECT_TRUE(cast_or_null(MD) == &D); + EXPECT_TRUE(cast_or_null(CD) == &D); +} + +TEST(CastingTest, smart_dyn_cast) { + EXPECT_TRUE(dyn_cast(MB) == nullptr); + EXPECT_TRUE(dyn_cast(CB) == nullptr); + EXPECT_TRUE(dyn_cast(MD) == &D); + EXPECT_TRUE(dyn_cast(CD) == &D); +} + +TEST(CastingTest, smart_dyn_cast_or_null) { + EXPECT_TRUE(dyn_cast_or_null(MN) == nullptr); + EXPECT_TRUE(dyn_cast_or_null(CN) == nullptr); + EXPECT_TRUE(dyn_cast_or_null(MB) == nullptr); + EXPECT_TRUE(dyn_cast_or_null(CB) == nullptr); + EXPECT_TRUE(dyn_cast_or_null(MD) == &D); + EXPECT_TRUE(dyn_cast_or_null(CD) == &D); +} + +} // end namespace pointer_wrappers +} // end namespace