[llvm] r300098 - [Support] Add support for unique_ptr<> to Casting.h.

Zachary Turner via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 12 12:59:38 PDT 2017


Author: zturner
Date: Wed Apr 12 14:59:37 2017
New Revision: 300098

URL: http://llvm.org/viewvc/llvm-project?rev=300098&view=rev
Log:
[Support] Add support for unique_ptr<> to Casting.h.

Often you have a unique_ptr<T> where T supports LLVM's
casting methods, and you wish to cast it to a unique_ptr<U>.
Prior to this patch, this requires doing hacky things like:

unique_ptr<U> Casted;
if (isa<U>(Orig.get()))
  Casted.reset(cast<U>(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<T>,
so you can now write:

auto Casted = cast<U>(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<U>(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<U>(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

Modified:
    llvm/trunk/include/llvm/Support/Casting.h
    llvm/trunk/unittests/Support/Casting.cpp

Modified: llvm/trunk/include/llvm/Support/Casting.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Casting.h?rev=300098&r1=300097&r2=300098&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/Casting.h (original)
+++ llvm/trunk/include/llvm/Support/Casting.h Wed Apr 12 14:59:37 2017
@@ -18,6 +18,7 @@
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/type_traits.h"
 #include <cassert>
+#include <memory>
 
 namespace llvm {
 
@@ -76,6 +77,14 @@ template <typename To, typename From> st
   }
 };
 
+template <typename To, typename From>
+struct isa_impl_cl<To, const std::unique_ptr<From>> {
+  static inline bool doit(const std::unique_ptr<From> &Val) {
+    assert(Val && "isa<> used on a null pointer");
+    return isa_impl_cl<To, From>::doit(*Val);
+  }
+};
+
 template <typename To, typename From> struct isa_impl_cl<To, From*> {
   static inline bool doit(const From *Val) {
     assert(Val && "isa<> used on a null pointer");
@@ -161,6 +170,15 @@ template<class To, class From> struct ca
   typedef const To* ret_type;   // Constant pointer arg case, return const Ty*
 };
 
+template <class To, class From>
+struct cast_retty_impl<To, std::unique_ptr<From>> {
+private:
+  typedef typename cast_retty_impl<To, From *>::ret_type PointerType;
+  typedef typename std::remove_pointer<PointerType>::type ResultType;
+
+public:
+  typedef std::unique_ptr<ResultType> ret_type;
+};
 
 template<class To, class From, class SimpleFrom>
 struct cast_retty_wrap {
@@ -238,6 +256,16 @@ inline typename cast_retty<X, Y *>::ret_
                           typename simplify_type<Y*>::SimpleType>::doit(Val);
 }
 
+template <class X, class Y>
+inline typename cast_retty<X, std::unique_ptr<Y>>::ret_type
+cast(std::unique_ptr<Y> &&Val) {
+  assert(isa<X>(Val.get()) && "cast<Ty>() argument of incompatible type!");
+  using ret_type = typename cast_retty<X, std::unique_ptr<Y>>::ret_type;
+  return ret_type(
+      cast_convert_val<X, Y *, typename simplify_type<Y *>::SimpleType>::doit(
+          Val.release()));
+}
+
 // cast_or_null<X> - Functionally identical to cast, except that a null value is
 // accepted.
 //
@@ -271,6 +299,13 @@ cast_or_null(Y *Val) {
   return cast<X>(Val);
 }
 
+template <class X, class Y>
+inline typename cast_retty<X, std::unique_ptr<Y>>::ret_type
+cast_or_null(std::unique_ptr<Y> &&Val) {
+  if (!Val)
+    return nullptr;
+  return cast<X>(std::move(Val));
+}
 
 // dyn_cast<X> - 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<X>(Val)) ? cast<X>(Val) : nullptr;
 }
 
+// unique_dyn_cast<X> - Given a unique_ptr<Y>, try to return a unique_ptr<X>,
+// taking ownership of the input pointer iff isa<X>(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 <class X, class Y>
+LLVM_NODISCARD inline auto unique_dyn_cast(std::unique_ptr<Y> &Val)
+    -> decltype(cast<X>(Val)) {
+  if (!isa<X>(Val))
+    return nullptr;
+  return cast<X>(std::move(Val));
+}
+
+template <class X, class Y>
+LLVM_NODISCARD inline auto unique_dyn_cast(std::unique_ptr<Y> &&Val)
+    -> decltype(cast<X>(Val)) {
+  return unique_dyn_cast<X, Y>(Val);
+}
+
+// dyn_cast_or_null<X> - Functionally identical to unique_dyn_cast, except that
+// a null value is accepted.
+template <class X, class Y>
+LLVM_NODISCARD inline auto unique_dyn_cast_or_null(std::unique_ptr<Y> &Val)
+    -> decltype(cast<X>(Val)) {
+  if (!Val)
+    return nullptr;
+  return unique_dyn_cast(Val);
+}
+
+template <class X, class Y>
+LLVM_NODISCARD inline auto unique_dyn_cast_or_null(std::unique_ptr<Y> &&Val)
+    -> decltype(cast<X>(Val)) {
+  return unique_dyn_cast_or_null<X, Y>(Val);
+}
+
 } // End llvm namespace
 
 #endif

Modified: llvm/trunk/unittests/Support/Casting.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/Casting.cpp?rev=300098&r1=300097&r2=300098&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/Casting.cpp (original)
+++ llvm/trunk/unittests/Support/Casting.cpp Wed Apr 12 14:59:37 2017
@@ -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<foo, bar> {
   static inline bool doit(const bar &Val) {
     dbgs() << "Classof: " << &Val << "\n";
@@ -47,6 +55,10 @@ template <> struct isa_impl<foo, bar> {
   }
 };
 
+template <typename T> struct isa_impl<foo, T> {
+  static inline bool doit(const T &Val) { return false; }
+};
+
 foo *bar::baz() {
     return cast<foo>(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<const bar> BP(B2);
+  auto FP = cast<foo>(std::move(BP));
+  static_assert(std::is_same<std::unique_ptr<const foo>, 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<const bar> BP(fub());
+  auto FP = cast_or_null<foo>(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<derived> newd() { return llvm::make_unique<derived>(); }
+std::unique_ptr<base> newb() { return llvm::make_unique<derived>(); }
+
+TEST(CastingTest, unique_dyn_cast) {
+  derived *OrigD = nullptr;
+  auto D = llvm::make_unique<derived>();
+  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<derived>(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<base>(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<base>(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<derived>(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<foo>(D);
+  ASSERT_EQ(nullptr, F);
+  ASSERT_EQ(OrigD, D.get());
+
+  // All of the above should also hold for temporaries.
+  auto D2 = unique_dyn_cast<derived>(newd());
+  EXPECT_NE(nullptr, D2);
+
+  auto B2 = unique_dyn_cast<derived>(newb());
+  EXPECT_NE(nullptr, B2);
+
+  auto B3 = unique_dyn_cast<base>(newb());
+  EXPECT_NE(nullptr, B3);
+
+  auto F2 = unique_dyn_cast<foo>(newb());
+  EXPECT_EQ(nullptr, F2);
+}
+
 // These lines are errors...
 //foo *F20 = cast<foo>(B2);  // Yields const foo*
 //foo &F21 = cast<foo>(B3);  // Yields const foo&




More information about the llvm-commits mailing list