Skip to content

Commit

Permalink
[Support] Add support for unique_ptr<> to Casting.h.
Browse files Browse the repository at this point in the history
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

llvm-svn: 300098
  • Loading branch information
Zachary Turner committed Apr 12, 2017
1 parent 538b3f4 commit 2bb94cd
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 0 deletions.
70 changes: 70 additions & 0 deletions llvm/include/llvm/Support/Casting.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/type_traits.h"
#include <cassert>
#include <memory>

namespace llvm {

Expand Down Expand Up @@ -76,6 +77,14 @@ template <typename To, typename From> struct isa_impl_cl<To, const From> {
}
};

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");
Expand Down Expand Up @@ -161,6 +170,15 @@ template<class To, class From> struct cast_retty_impl<To, const From*const> {
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 {
Expand Down Expand Up @@ -238,6 +256,16 @@ inline typename cast_retty<X, Y *>::ret_type cast(Y *Val) {
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.
//
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
75 changes: 75 additions & 0 deletions llvm/unittests/Support/Casting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,25 @@ 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";
return true;
}
};

template <typename T> struct isa_impl<foo, T> {
static inline bool doit(const T &Val) { return false; }
};

foo *bar::baz() {
return cast<foo>(this);
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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&
Expand Down

0 comments on commit 2bb94cd

Please sign in to comment.