From ab480f45cd23c08cb9aa3f427aad072df249135f Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Wed, 12 Apr 2017 19:59:37 +0000 Subject: [PATCH] [Support] Add support for unique_ptr<> to Casting.h. Often you have a unique_ptr where T supports LLVM's casting methods, and you wish to cast it to a unique_ptr. Prior to this patch, this requires doing hacky things like: unique_ptr Casted; if (isa(Orig.get())) Casted.reset(cast(Orig.release())); This is overly verbose, and it would be nice to just be able to use unique_ptr directly with cast and dyn_cast. To this end, this patch updates cast<> to work directly with unique_ptr, so you can now write: auto Casted = cast(std::move(Orig)); Since it's possible for dyn_cast<> to fail, however, we choose to use a slightly different API here, because it's awkward to write if (auto Casted = dyn_cast(std::move(Orig))) {} when Orig may end up not having been moved at all. So the interface for dyn_cast is if (auto Casted = unique_dyn_cast(Orig)) {} Where the inclusion of `unique` in the name of the cast operator re-affirms that regardless of success of or fail of the casting, exactly one of the input value and the return value will contain a non-null result. Differential Revision: https://reviews.llvm.org/D31890 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@300098 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/Casting.h | 70 +++++++++++++++++++++++++++++++ unittests/Support/Casting.cpp | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) diff --git a/include/llvm/Support/Casting.h b/include/llvm/Support/Casting.h index a73047b2b55..3a0c5536c01 100644 --- a/include/llvm/Support/Casting.h +++ b/include/llvm/Support/Casting.h @@ -18,6 +18,7 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/type_traits.h" #include +#include namespace llvm { @@ -76,6 +77,14 @@ template struct isa_impl_cl { } }; +template +struct isa_impl_cl> { + static inline bool doit(const std::unique_ptr &Val) { + assert(Val && "isa<> used on a null pointer"); + return isa_impl_cl::doit(*Val); + } +}; + template struct isa_impl_cl { static inline bool doit(const From *Val) { assert(Val && "isa<> used on a null pointer"); @@ -161,6 +170,15 @@ template struct cast_retty_impl { typedef const To* ret_type; // Constant pointer arg case, return const Ty* }; +template +struct cast_retty_impl> { +private: + typedef typename cast_retty_impl::ret_type PointerType; + typedef typename std::remove_pointer::type ResultType; + +public: + typedef std::unique_ptr ret_type; +}; template struct cast_retty_wrap { @@ -238,6 +256,16 @@ inline typename cast_retty::ret_type cast(Y *Val) { typename simplify_type::SimpleType>::doit(Val); } +template +inline typename cast_retty>::ret_type +cast(std::unique_ptr &&Val) { + assert(isa(Val.get()) && "cast() argument of incompatible type!"); + using ret_type = typename cast_retty>::ret_type; + return ret_type( + cast_convert_val::SimpleType>::doit( + Val.release())); +} + // cast_or_null - Functionally identical to cast, except that a null value is // accepted. // @@ -271,6 +299,13 @@ cast_or_null(Y *Val) { return cast(Val); } +template +inline typename cast_retty>::ret_type +cast_or_null(std::unique_ptr &&Val) { + if (!Val) + return nullptr; + return cast(std::move(Val)); +} // dyn_cast - Return the argument parameter cast to the specified type. This // casting operator returns null if the argument is of the wrong type, so it can @@ -323,6 +358,41 @@ dyn_cast_or_null(Y *Val) { return (Val && isa(Val)) ? cast(Val) : nullptr; } +// unique_dyn_cast - Given a unique_ptr, try to return a unique_ptr, +// taking ownership of the input pointer iff isa(Val) is true. If the +// cast is successful, From refers to nullptr on exit and the casted value +// is returned. If the cast is unsuccessful, the function returns nullptr +// and From is unchanged. +template +LLVM_NODISCARD inline auto unique_dyn_cast(std::unique_ptr &Val) + -> decltype(cast(Val)) { + if (!isa(Val)) + return nullptr; + return cast(std::move(Val)); +} + +template +LLVM_NODISCARD inline auto unique_dyn_cast(std::unique_ptr &&Val) + -> decltype(cast(Val)) { + return unique_dyn_cast(Val); +} + +// dyn_cast_or_null - Functionally identical to unique_dyn_cast, except that +// a null value is accepted. +template +LLVM_NODISCARD inline auto unique_dyn_cast_or_null(std::unique_ptr &Val) + -> decltype(cast(Val)) { + if (!Val) + return nullptr; + return unique_dyn_cast(Val); +} + +template +LLVM_NODISCARD inline auto unique_dyn_cast_or_null(std::unique_ptr &&Val) + -> decltype(cast(Val)) { + return unique_dyn_cast_or_null(Val); +} + } // End llvm namespace #endif diff --git a/unittests/Support/Casting.cpp b/unittests/Support/Casting.cpp index e6c35fc21eb..9a818f6bdeb 100644 --- a/unittests/Support/Casting.cpp +++ b/unittests/Support/Casting.cpp @@ -40,6 +40,14 @@ struct foo { }*/ }; +struct base { + virtual ~base() {} +}; + +struct derived : public base { + static bool classof(const base *B) { return true; } +}; + template <> struct isa_impl { static inline bool doit(const bar &Val) { dbgs() << "Classof: " << &Val << "\n"; @@ -47,6 +55,10 @@ template <> struct isa_impl { } }; +template struct isa_impl { + static inline bool doit(const T &Val) { return false; } +}; + foo *bar::baz() { return cast(this); } @@ -123,6 +135,13 @@ TEST(CastingTest, cast) { // EXPECT_EQ(F7, null_foo); foo *F8 = B1.baz(); EXPECT_NE(F8, null_foo); + + std::unique_ptr BP(B2); + auto FP = cast(std::move(BP)); + static_assert(std::is_same, decltype(FP)>::value, + "Incorrect deduced return type!"); + EXPECT_NE(FP.get(), null_foo); + FP.release(); } TEST(CastingTest, cast_or_null) { @@ -136,6 +155,10 @@ TEST(CastingTest, cast_or_null) { EXPECT_EQ(F14, null_foo); foo *F15 = B1.caz(); EXPECT_NE(F15, null_foo); + + std::unique_ptr BP(fub()); + auto FP = cast_or_null(std::move(BP)); + EXPECT_EQ(FP.get(), null_foo); } TEST(CastingTest, dyn_cast) { @@ -165,6 +188,58 @@ TEST(CastingTest, dyn_cast_or_null) { EXPECT_NE(F5, null_foo); } +std::unique_ptr newd() { return llvm::make_unique(); } +std::unique_ptr newb() { return llvm::make_unique(); } + +TEST(CastingTest, unique_dyn_cast) { + derived *OrigD = nullptr; + auto D = llvm::make_unique(); + OrigD = D.get(); + + // Converting from D to itself is valid, it should return a new unique_ptr + // and the old one should become nullptr. + auto NewD = unique_dyn_cast(D); + ASSERT_EQ(OrigD, NewD.get()); + ASSERT_EQ(nullptr, D); + + // Converting from D to B is valid, B should have a value and D should be + // nullptr. + auto B = unique_dyn_cast(NewD); + ASSERT_EQ(OrigD, B.get()); + ASSERT_EQ(nullptr, NewD); + + // Converting from B to itself is valid, it should return a new unique_ptr + // and the old one should become nullptr. + auto NewB = unique_dyn_cast(B); + ASSERT_EQ(OrigD, NewB.get()); + ASSERT_EQ(nullptr, B); + + // Converting from B to D is valid, D should have a value and B should be + // nullptr; + D = unique_dyn_cast(NewB); + ASSERT_EQ(OrigD, D.get()); + ASSERT_EQ(nullptr, NewB); + + // Converting between unrelated types should fail. The original value should + // remain unchanged and it should return nullptr. + auto F = unique_dyn_cast(D); + ASSERT_EQ(nullptr, F); + ASSERT_EQ(OrigD, D.get()); + + // All of the above should also hold for temporaries. + auto D2 = unique_dyn_cast(newd()); + EXPECT_NE(nullptr, D2); + + auto B2 = unique_dyn_cast(newb()); + EXPECT_NE(nullptr, B2); + + auto B3 = unique_dyn_cast(newb()); + EXPECT_NE(nullptr, B3); + + auto F2 = unique_dyn_cast(newb()); + EXPECT_EQ(nullptr, F2); +} + // These lines are errors... //foo *F20 = cast(B2); // Yields const foo* //foo &F21 = cast(B3); // Yields const foo& -- 2.40.0