diff --git a/llvm/include/llvm/Support/Casting.h b/llvm/include/llvm/Support/Casting.h --- a/llvm/include/llvm/Support/Casting.h +++ b/llvm/include/llvm/Support/Casting.h @@ -7,13 +7,14 @@ //===----------------------------------------------------------------------===// // // This file defines the isa(), cast(), dyn_cast(), cast_or_null(), -// and dyn_cast_or_null() templates. +// dyn_cast_or_null(), and dyn_cast_or_none() templates. // //===----------------------------------------------------------------------===// #ifndef LLVM_SUPPORT_CASTING_H #define LLVM_SUPPORT_CASTING_H +#include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/type_traits.h" #include @@ -320,6 +321,25 @@ return cast(std::move(Val)); } +//===----------------------------------------------------------------------===// +// dyn_cast Support Templates +//===----------------------------------------------------------------------===// + +template struct dyn_cast_retty { +private: + using CastReturnType = typename cast_retty::ret_type; + static constexpr bool ConstructibleFromNullptr = + std::is_constructible::value || + std::is_pointer::value; + +public: + using ret_type = typename std::conditional< + ConstructibleFromNullptr, CastReturnType, + Optional>>::type; + + static ret_type castFailed() { return ret_type{}; } +}; + // 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 // be used to test for a type as well as cast if successful. This should be @@ -327,22 +347,28 @@ // // if (const Instruction *I = dyn_cast(myVal)) { ... } // +// Or, if the target type is not constructible from nullptr then it could be +// used like this: +// +// if (Optional I = dyn_cast(myVal)) { ... } +// template LLVM_NODISCARD inline std::enable_if_t< - !is_simple_type::value, typename cast_retty::ret_type> + !is_simple_type::value, typename dyn_cast_retty::ret_type> dyn_cast(const Y &Val) { - return isa(Val) ? cast(Val) : nullptr; + return isa(Val) ? cast(Val) : dyn_cast_retty::castFailed(); } template -LLVM_NODISCARD inline typename cast_retty::ret_type dyn_cast(Y &Val) { - return isa(Val) ? cast(Val) : nullptr; +LLVM_NODISCARD inline typename dyn_cast_retty::ret_type dyn_cast(Y &Val) { + return isa(Val) ? cast(Val) : dyn_cast_retty::castFailed(); } template -LLVM_NODISCARD inline typename cast_retty::ret_type dyn_cast(Y *Val) { - return isa(Val) ? cast(Val) : nullptr; +LLVM_NODISCARD inline typename dyn_cast_retty::ret_type +dyn_cast(Y *Val) { + return isa(Val) ? cast(Val) : dyn_cast_retty::castFailed(); } // dyn_cast_or_null - Functionally identical to dyn_cast, except that a null @@ -368,6 +394,51 @@ return (Val && isa(Val)) ? cast(Val) : nullptr; } +// dyn_cast_or_none - Functionally identical to dyn_cast, except that an +// empty optional value is accepted. +// + +//===----------------------------------------------------------------------===// +// dyn_cast_or_none support +//===----------------------------------------------------------------------===// + +// These three functions provide forwarding for non-optional types passed into +// dyn_cast_or_none. +// + +template +LLVM_NODISCARD inline auto dyn_cast_or_none(const Y &Val) + -> decltype(dyn_cast(Val)) { + return dyn_cast(Val); +} + +template +LLVM_NODISCARD inline auto dyn_cast_or_none(Y &Val) + -> decltype(dyn_cast(Val)) { + return dyn_cast(Val); +} + +template +LLVM_NODISCARD inline auto dyn_cast_or_none(Y *Val) + -> decltype(dyn_cast_or_null(Val)) { + return dyn_cast_or_null(Val); +} + +template +LLVM_NODISCARD inline std::enable_if_t< + !is_simple_type::value, typename dyn_cast_retty::ret_type> +dyn_cast_or_none(Optional &Val) { + return (Val.hasValue() && isa(*Val)) ? cast(*Val) + : dyn_cast_retty::castFailed(); +} + +template +LLVM_NODISCARD inline typename dyn_cast_retty::ret_type +dyn_cast_or_none(Optional &Val) { + return (Val.hasValue() && isa(*Val)) ? cast(*Val) + : dyn_cast_retty::castFailed(); +} + // 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 diff --git a/llvm/unittests/Support/Casting.cpp b/llvm/unittests/Support/Casting.cpp --- a/llvm/unittests/Support/Casting.cpp +++ b/llvm/unittests/Support/Casting.cpp @@ -19,7 +19,7 @@ struct IllegalCast; template IllegalCast *cast(...) { return nullptr; } -// set up two example classes +// set up three example classes // with conversion facility // struct bar { @@ -28,6 +28,7 @@ struct foo *caz(); struct foo *daz(); struct foo *naz(); + Optional optional(); private: bar(const bar &); }; @@ -74,6 +75,10 @@ return dyn_cast_or_null(this); } +Optional bar::optional() { + return dyn_cast(*this); +} + bar *fub(); @@ -193,6 +198,27 @@ EXPECT_NE(F5, null_foo); } +TEST(CastingTest, dyn_cast_optional) { + Optional F1 = dyn_cast(B); + EXPECT_TRUE(F1.hasValue()); + Optional F2 = dyn_cast(B1); + EXPECT_TRUE(F2.hasValue()); + Optional F3 = dyn_cast(B3); + EXPECT_TRUE(F3.hasValue()); + + Optional F4 = B1.optional(); + EXPECT_TRUE(F4.hasValue()); +} + +TEST(CastingTest, dyn_cast_or_none) { + Optional empty{}; + Optional F1 = dyn_cast_or_none(empty); + EXPECT_FALSE(F1.hasValue()); + + Optional F2 = dyn_cast_or_none(B1); + EXPECT_TRUE(F2.hasValue()); +} + std::unique_ptr newd() { return std::make_unique(); } std::unique_ptr newb() { return std::make_unique(); }