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 @@ -6,14 +6,15 @@ // //===----------------------------------------------------------------------===// // -// This file defines the isa(), cast(), dyn_cast(), cast_or_null(), -// and dyn_cast_or_null() templates. +// This file defines the isa(), cast(), dyn_cast(), +// cast_if_present(), and dyn_cast_if_present() 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 @@ -22,44 +23,133 @@ namespace llvm { +/// There are 3 components of LLVM's casting infrastructure you may want to take +/// advantage of as the author of a new type. `isa`, `cast`, and `dyn_cast` (and +/// their _if_present) variants. Their implementation should generally be opaque +/// to most users who want to support simple casts, the most basic step needed +/// to participate in the casting infrastructure is: +/// +/// struct MyNewType { +/// static bool classof(const MyOtherType *t) { return t->isMyNewType(); } +/// }; +/// +/// From here, there are several more-advanced methods that can be used to +/// customize the way casting is performed. +/// +/// `isa` and `isa_and_present` rely on the `CastIsPossible` template. In order +/// to provide custom behavior for `isa*` you should specialize this struct like +/// so: +/// +/// template<> CastIsPossible { +/// static inline bool isPossible(const MyOtherType &ot) { +/// return ot.isMyNewType(); +/// } +/// } +/// +/// `cast` is what we call an infallible cast. This distinction allows us to +/// provide cast-only support for some types when we don't want to enable +/// fallible casting (i.e. dyn_cast). Note that cast_if_present is a fallible +/// cast! This is because if the value is not present, the failure type must be +/// provided. If you want to provide custom behavior for `cast` you should +/// specialize the `InfallibleCastTraits` struct like so: +/// +/// template<> struct InfallibleCastTraits { +/// using CastResultType = const MyNewType &; +/// static inline CastResultType doCast(const MyOtherType &ot) { +/// return (CastResultType)ot; +/// } +/// }; +/// +/// Fallible casts comprise the majority of casts performed - `cast_if_present`, +/// `dyn_cast`, `dyn_cast_if_present` are all fallible casts. The way to +/// customize the behavior for fallible casts is to specialize the struct +/// `FallibleCastTraits` like so: +/// +/// template<> struct FallibleCastTraits { +/// using CastResultType = MyOptionalContainer; +/// static inline CastResultType doCast(const MyOtherType &f) { +/// return MyNewType(f); +/// } +/// static inline CastResultType castFailed() { return CastResultType(); } +/// static inline CastResultType doCastIfPossible(const MyOtherType &f) { +/// if (!isPossible(f)) +/// return castFailed(); +/// return doCast(f); +/// } +///}; +/// +/// Sometimes you don't need the full control provided by the +/// (In/F)allibleCastTraits - you just need to say "here's how to tell if a +/// value is present or not". In that case, there's another template you can +/// specialize, `ValueIsPresent`, which is done like so: +/// +/// template struct ValueIsPresent> { +/// using UnwrappedType = T; +/// static inline bool isPresent(const MyOptionalContainer &t) { return +/// t.hasAValue(); } static inline const T &unwrapValue(const T &t) { return +/// t.getMyValue(); } +/// }; +/// +/// Other times, you need full control over how a cast is performed. In that +/// case, you should specialize the `CastInfo` struct for your type. We provide +/// a set of basic/default cast traits that can be used to implement a custom +/// `CastInfo` to reduce boilerplate, but to have full control simply implement +/// the methods you want in your CastInfo specialization. You can specialize +/// `CastInfo` like this: +/// +/// template<> struct CastInfo : +/// // Provide a checked pointer cast from bar * to foo * +/// public CheckedPointerCast { +/// // And we want to only provide an infallible cast from value ref to +/// // value ref (which we want to implement ourselves). +/// using CastResultType = foo &; +/// static inline CastResultType doCast(const bar &b) { +/// return (foo &)b; +/// } +/// }; + //===----------------------------------------------------------------------===// -// isa Support Templates +// simplify_type //===----------------------------------------------------------------------===// -// Define a template that can be specialized by smart pointers to reflect the -// fact that they are automatically dereferenced, and are not involved with the -// template selection process... the default implementation is a noop. -// -template struct simplify_type { +/// Define a template that can be specialized by smart pointers to reflect the +/// fact that they are automatically dereferenced, and are not involved with the +/// template selection process... the default implementation is a noop. +// TODO: rename this and/or replace it with other cast traits. +template struct simplify_type { using SimpleType = From; // The real type this represents... // An accessor to get the real value... static SimpleType &getSimplifiedValue(From &Val) { return Val; } }; -template struct simplify_type { +template struct simplify_type { using NonConstSimpleType = typename simplify_type::SimpleType; - using SimpleType = - typename add_const_past_pointer::type; + using SimpleType = typename add_const_past_pointer::type; using RetType = typename add_lvalue_reference_if_not_pointer::type; - static RetType getSimplifiedValue(const From& Val) { - return simplify_type::getSimplifiedValue(const_cast(Val)); + static RetType getSimplifiedValue(const From &Val) { + return simplify_type::getSimplifiedValue(const_cast(Val)); } }; +// TODO: add this namespace once everyone is switched to using the new +// interface. +// namespace detail { + +//===----------------------------------------------------------------------===// +// isa_impl +//===----------------------------------------------------------------------===// + // The core of the implementation of isa is here; To and From should be // the names of classes. This template can be specialized to customize the // implementation of isa<> without rewriting it from scratch. -template -struct isa_impl { - static inline bool doit(const From &Val) { - return To::classof(&Val); - } +template struct isa_impl { + static inline bool doit(const From &Val) { return To::classof(&Val); } }; -/// Always allow upcasts, and perform no dynamic check for them. +// Always allow upcasts, and perform no dynamic check for them. template struct isa_impl::value>> { static inline bool doit(const From &) { return true; } @@ -85,103 +175,78 @@ } }; -template struct isa_impl_cl { +template struct isa_impl_cl { static inline bool doit(const From *Val) { assert(Val && "isa<> used on a null pointer"); return isa_impl::doit(*Val); } }; -template struct isa_impl_cl { +template struct isa_impl_cl { static inline bool doit(const From *Val) { assert(Val && "isa<> used on a null pointer"); return isa_impl::doit(*Val); } }; -template struct isa_impl_cl { +template struct isa_impl_cl { static inline bool doit(const From *Val) { assert(Val && "isa<> used on a null pointer"); return isa_impl::doit(*Val); } }; -template struct isa_impl_cl { +template +struct isa_impl_cl { static inline bool doit(const From *Val) { assert(Val && "isa<> used on a null pointer"); return isa_impl::doit(*Val); } }; -template +template struct isa_impl_wrap { // When From != SimplifiedType, we can simplify the type some more by using // the simplify_type template. static bool doit(const From &Val) { return isa_impl_wrap::SimpleType>::doit( - simplify_type::getSimplifiedValue(Val)); + typename simplify_type::SimpleType>:: + doit(simplify_type::getSimplifiedValue(Val)); } }; -template +template struct isa_impl_wrap { // When From == SimpleType, we are as simple as we are going to get. static bool doit(const FromTy &Val) { - return isa_impl_cl::doit(Val); + return isa_impl_cl::doit(Val); } }; -// isa - Return true if the parameter to the template is an instance of one -// of the template type arguments. Used like this: -// -// if (isa(myVal)) { ... } -// if (isa(myVal)) { ... } -// -template LLVM_NODISCARD inline bool isa(const Y &Val) { - return isa_impl_wrap::SimpleType>::doit(Val); -} - -template -LLVM_NODISCARD inline bool isa(const Y &Val) { - return isa(Val) || isa(Val); -} - -// isa_and_nonnull - Functionally identical to isa, except that a null value -// is accepted. -// -template -LLVM_NODISCARD inline bool isa_and_nonnull(const Y &Val) { - if (!Val) - return false; - return isa(Val); -} - //===----------------------------------------------------------------------===// -// cast Support Templates +// cast_retty + cast_retty_impl //===----------------------------------------------------------------------===// -template struct cast_retty; +template struct cast_retty; // Calculate what type the 'cast' function should return, based on a requested // type of To and a source type of From. -template struct cast_retty_impl { - using ret_type = To &; // Normal case, return Ty& +template struct cast_retty_impl { + using ret_type = To &; // Normal case, return Ty& }; -template struct cast_retty_impl { +template struct cast_retty_impl { using ret_type = const To &; // Normal case, return Ty& }; -template struct cast_retty_impl { - using ret_type = To *; // Pointer arg case, return Ty* +template struct cast_retty_impl { + using ret_type = To *; // Pointer arg case, return Ty* }; -template struct cast_retty_impl { +template struct cast_retty_impl { using ret_type = const To *; // Constant pointer arg case, return const Ty* }; -template struct cast_retty_impl { +template struct cast_retty_impl { using ret_type = const To *; // Constant pointer arg case, return const Ty* }; @@ -195,187 +260,533 @@ using ret_type = std::unique_ptr; }; -template -struct cast_retty_wrap { +template struct cast_retty_wrap { // When the simplified type and the from type are not the same, use the type // simplifier to reduce the type, then reuse cast_retty_impl to get the // resultant type. using ret_type = typename cast_retty::ret_type; }; -template -struct cast_retty_wrap { +template struct cast_retty_wrap { // When the simplified type is equal to the from type, use it directly. - using ret_type = typename cast_retty_impl::ret_type; + using ret_type = typename cast_retty_impl::ret_type; }; -template -struct cast_retty { +template struct cast_retty { using ret_type = typename cast_retty_wrap< To, From, typename simplify_type::SimpleType>::ret_type; }; +//===----------------------------------------------------------------------===// +// cast_convert_val +//===----------------------------------------------------------------------===// + // Ensure the non-simple values are converted using the simplify_type template // that may be specialized by smart pointers... // -template struct cast_convert_val { +template struct cast_convert_val { // This is not a simple type, use the template to simplify it... - static typename cast_retty::ret_type doit(From &Val) { + static typename cast_retty::ret_type doit(const From &Val) { return cast_convert_val::SimpleType>::doit( - simplify_type::getSimplifiedValue(Val)); + typename simplify_type::SimpleType>:: + doit(simplify_type::getSimplifiedValue(const_cast(Val))); } }; -template struct cast_convert_val { +template struct cast_convert_val { // This _is_ a simple type, just cast it. static typename cast_retty::ret_type doit(const FromTy &Val) { - typename cast_retty::ret_type Res2 - = (typename cast_retty::ret_type)const_cast(Val); + typename cast_retty::ret_type Res2 = + (typename cast_retty::ret_type) const_cast(Val); return Res2; } }; +//===----------------------------------------------------------------------===// +// is_simple_type +//===----------------------------------------------------------------------===// + template struct is_simple_type { static const bool value = std::is_same::SimpleType>::value; }; -// cast - Return the argument parameter cast to the specified type. This -// casting operator asserts that the type is correct, so it does not return null -// on failure. It does not allow a null argument (use cast_or_null for that). -// It is typically used like this: -// -// cast(myVal)->getParent() -// -template -inline std::enable_if_t::value, - typename cast_retty::ret_type> -cast(const Y &Val) { - assert(isa(Val) && "cast() argument of incompatible type!"); - return cast_convert_val< - X, const Y, typename simplify_type::SimpleType>::doit(Val); +// } // namespace detail + +//===----------------------------------------------------------------------===// +// CastIsPossible +//===----------------------------------------------------------------------===// + +/// This struct provides a way to check if a given cast is possible. It provides +/// a static function called isPossible that is used to check if a cast can be +/// performed. It should be overridden like this: +/// +/// template<> struct CastIsPossible { +/// static inline bool isPossible(const bar &b) { +/// return bar.isFoo(); +/// } +/// }; +template +struct CastIsPossible { + static inline bool isPossible(const From &f) { + return isa_impl_wrap< + To, const From, + typename simplify_type::SimpleType>::doit(f); + } +}; + +// Needed for optional unwrapping. This could be implemented with isa_impl, but +// we want to implement things in the new method and move old implementations +// over. In fact, some of the isa_impl templates should be moved over to +// CastIsPossible. +template +struct CastIsPossible> { + static inline bool isPossible(const Optional &f) { + assert(f.hasValue() && "CastIsPossible::isPossible called on a nullopt!"); + return isa_impl_wrap< + To, const From, + typename simplify_type::SimpleType>::doit(*f); + } +}; + +/// Helper function to make it easier to call CastIsPossible::isPossible. +template inline bool isPossible(const From &f) { + return CastIsPossible::isPossible(f); } -template -inline typename cast_retty::ret_type cast(Y &Val) { - assert(isa(Val) && "cast() argument of incompatible type!"); - return cast_convert_val::SimpleType>::doit(Val); +//===----------------------------------------------------------------------===// +// InfallibleCastTraits +//===----------------------------------------------------------------------===// + +/// This struct provides the return type and method for performing an infallible +/// cast. This is a cast that must be used with cast (and not dyn_cast) because +/// it cannot fail. It should be overridden like this: +/// +/// template<> struct InfallibleCastTraits { +/// using CastResultType = foo &; +/// static inline foo &doCast(const bar &b) { +/// return (foo &)(const_cast(b)); +/// } +/// }; +template +struct InfallibleCastTraits { + using CastResultType = typename cast_retty::ret_type; + static inline CastResultType doCast(const From &f) { + return cast_convert_val< + To, From, + typename simplify_type::SimpleType>::doit(const_cast(f)); + } +}; + +//===----------------------------------------------------------------------===// +// FallibleCastTraits +//===----------------------------------------------------------------------===// + +/// This struct provides the return type and method for performing a fallible +/// cast. This is a cast that can be used with dyn_cast - for non-nullable types +/// this cast returns an Optional and for nullable types this cast returns T. +/// It should be overridden like this: +/// +/// template<> struct FallibleCastTraits { +/// using CastResultType = foo; +/// static inline CastResultType doCast(const bar &b) { +/// return foo(const_cast(b)); +/// } +/// static inline CastResultType castFailed() { return foo(); } +/// static inline CastResultType doCastIfPossible(const bar &b) { +/// if (!isPossible(b)) +/// return castFailed(); +/// return doCast(b); +/// } +/// }; +template +struct FallibleCastTraits { + using CastResultType = typename cast_retty::ret_type; + static inline CastResultType doCast(const From &f) { + return cast_convert_val< + To, From, + typename simplify_type::SimpleType>::doit(const_cast(f)); + } + + static inline CastResultType castFailed() { return CastResultType(nullptr); } + + static inline CastResultType doCastIfPossible(const From &f) { + if (!isPossible(f)) + return castFailed(); + return doCast(f); + } +}; + +/// If something is a pointer or constructible from nullptr, we consider it to +/// be "nullable". +template +constexpr bool IsNullable = std::is_pointer::value || + std::is_constructible::value; + +/// This is the non-nullable implementation for FallibleCastTraits that returns +/// Optional. +template +struct FallibleCastTraits::ret_type>>>> { + using InnerReturnType = + std::remove_reference_t::ret_type>; + static_assert( + !std::is_pointer::value, + "Optional must never be the deduced fallible cast result type."); + using CastResultType = Optional; + + static inline CastResultType doCast(const From &f) { + return cast_convert_val< + InnerReturnType, From, + typename simplify_type::SimpleType>::doit(const_cast(f)); + } + + static inline CastResultType castFailed() { return CastResultType{}; } + + static inline CastResultType doCastIfPossible(const From &f) { + if (!isPossible(f)) + return castFailed(); + return doCast(f); + } +}; + +//===----------------------------------------------------------------------===// +// ValueIsPresent +//===----------------------------------------------------------------------===// + +/// ValueIsPresent provides a way to check if a value is, well, present. For +/// pointers, this is the equivalent of checking against nullptr, for Optionals +/// this is the equivalent of checking hasValue(). It also provides a method for +/// unwrapping a value (think dereferencing a pointer). + +// Generic values can't *not* be present. +template struct ValueIsPresent { + using UnwrappedType = T; + static inline bool isPresent(const T &t) { return true; } + static inline const T &unwrapValue(const T &t) { return t; } +}; + +// Optional provides its own way to check if something is present. +template struct ValueIsPresent> { + using UnwrappedType = T; + static inline bool isPresent(const Optional &t) { return t.hasValue(); } + static inline const T &unwrapValue(const Optional &t) { + return t.getValue(); + } +}; + +// If something is "nullable" then we just compare it to nullptr to see if it +// exists. +template +struct ValueIsPresent>> { + using UnwrappedType = T; + static inline bool isPresent(const T &t) { return t != nullptr; } + static inline const T &unwrapValue(const T &t) { return t; } +}; + +// Convenience function we can use to check if a value is present. Because of +// simplify_type, we have to call it on the simplified type for now. +template inline bool isPresent(const T &t) { + return ValueIsPresent::SimpleType>::isPresent( + simplify_type::getSimplifiedValue(const_cast(t))); } -template -inline typename cast_retty::ret_type cast(Y *Val) { - assert(isa(Val) && "cast() argument of incompatible type!"); - return cast_convert_val::SimpleType>::doit(Val); +// Convenience function we can use to unwrap a value. +template +inline const typename ValueIsPresent::UnwrappedType & +unwrapValue(const T &t) { + return ValueIsPresent::unwrapValue(t); } -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 Traits +//===----------------------------------------------------------------------===// + +/// This cast trait is a composite of CastIsPossible and FallibleCastTraits. +/// It provides the standard LLVM checked pointer cast. +template +struct CheckedPointerCast : public CastIsPossible, + public FallibleCastTraits {}; + +/// This cast trait provides the standard LLVM support for infallible casts +/// (i.e.. convert one reference to another). You cannot use dyn_cast on a cast +/// that only uses this. +template +struct InfallibleValueCast : public CastIsPossible, + public InfallibleCastTraits {}; + +/// This cast trait provides fallible value casting. The default behavior is +/// that if the target type can be constructed from nullptr (i.e. nullable) then +/// castFailed() returns the target type constructed with nullptr. Otherwise, +/// the fallible cast will return an Optional with an empty optional in the +/// case of cast failure. +template +struct FallibleValueCast : public CastIsPossible, + public FallibleCastTraits {}; + +/// For the specific case of constructing a value-typed object from a +/// pointer-typed object, we implement FallibleCastTraits manually to handle +/// this. Note that `To` must be nullable to use this cast trait. +template +struct ValueFromPointerCast : public CastIsPossible { + using CastResultType = To; + + static inline To castFailed() { return To{nullptr}; } + + static inline CastResultType doCast(const From *f) { + return CastResultType(const_cast(f)); + } + + static inline CastResultType doCastIfPossible(const From *f) { + if (!isPossible(f)) + return castFailed(); + return doCast(f); + } +}; + +/// This cast trait provides std::unique_ptr casting. It has the semantics of +/// moving the contents of the input unique_ptr into the output unique_ptr +/// during the cast. It's also a good example of how to implement a move-only +/// cast. +template +struct UniquePtrCast : public CastIsPossible, + public FallibleCastTraits> { + using Self = UniquePtrCast; + using CastResultType = typename Self::CastResultType; + + static inline CastResultType doCast(std::unique_ptr &f) { + return CastResultType((typename CastResultType::element_type *)f.release()); + } + + static inline CastResultType doCast(std::unique_ptr &&f) { + return CastResultType((typename CastResultType::element_type *)f.release()); + } +}; + +//===----------------------------------------------------------------------===// +// CastInfo +//===----------------------------------------------------------------------===// + +/// This is what is actually called by cast, dyn_cast, cast_if_present, and +/// dyn_cast_if_present. For the default cases it combines the cast traits above +/// to handle a range of cast cases. For the most control over how and when +/// casting is performed, override this class with your specific types. You can +/// override it like so: +/// +/// template<> struct CastInfo : +/// // Provide a checked pointer cast from bar * to foo * +/// public CheckedPointerCast { +/// // And we want to only provide an infallible cast from value ref to +/// // value ref (which we want to implement ourselves). +/// using CastResultType = foo &; +/// static inline CastResultType doCast(const bar &b) { +/// return (foo &)b; +/// } +/// }; + +template +struct CastInfo + : public std::conditional_t, + InfallibleValueCast> {}; + +template +struct CastInfo : public CheckedPointerCast {}; + +template +struct CastInfo> : public UniquePtrCast {}; + +template +struct CastInfo> : public FallibleValueCast {}; + +/// isa - Return true if the parameter to the template is an instance of one +/// of the template type arguments. Used like this: +/// +/// if (isa(myVal)) { ... } +/// if (isa(myVal)) { ... } +template +LLVM_NODISCARD inline bool isa(const From &Val) { + return CastInfo::isPossible(Val); } -// cast_or_null - Functionally identical to cast, except that a null value is -// accepted. -// -template -LLVM_NODISCARD inline std::enable_if_t< - !is_simple_type::value, typename cast_retty::ret_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_NODISCARD inline bool isa(const From &Val) { + return isa(Val) || isa(Val); } -template -LLVM_NODISCARD inline std::enable_if_t::value, - typename cast_retty::ret_type> -cast_or_null(Y &Val) { - if (!Val) - return nullptr; - assert(isa(Val) && "cast_or_null() argument of incompatible type!"); - return cast(Val); +/// cast - Return the argument parameter cast to the specified type. This +/// casting operator asserts that the type is correct, so it does not return +/// null on failure. It does not allow a null argument (use cast_if_present for +/// that). It is typically used like this: +/// +/// cast(myVal)->getParent() + +template +LLVM_NODISCARD inline typename CastInfo::CastResultType +cast(const From &Val) { + return CastInfo::doCast(Val); } -template -LLVM_NODISCARD inline typename cast_retty::ret_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_NODISCARD inline typename CastInfo::CastResultType +cast(From &Val) { + return CastInfo::doCast(Val); } -template -inline typename cast_retty>::ret_type -cast_or_null(std::unique_ptr &&Val) { - if (!Val) - return nullptr; - return cast(std::move(Val)); +template +LLVM_NODISCARD inline typename CastInfo::CastResultType +cast(From *Val) { + return CastInfo::doCast(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 -// be used to test for a type as well as cast if successful. This should be -// used in the context of an if statement like this: -// -// if (const Instruction *I = dyn_cast(myVal)) { ... } -// +template +LLVM_NODISCARD inline + typename CastInfo>::CastResultType + cast(std::unique_ptr &&Val) { + return CastInfo>::doCast(std::move(Val)); +} -template -LLVM_NODISCARD inline std::enable_if_t< - !is_simple_type::value, typename cast_retty::ret_type> -dyn_cast(const Y &Val) { - return isa(Val) ? cast(Val) : nullptr; +/// 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. The value +/// passed in must be present, if not, use dyn_cast_if_present. This should be +/// used in the context of an if statement like this: +/// +/// if (const Instruction *I = dyn_cast(myVal)) { ... } + +template +LLVM_NODISCARD inline typename CastInfo::CastResultType +dyn_cast(const From &Val) { + return CastInfo::doCastIfPossible(Val); } -template -LLVM_NODISCARD inline typename cast_retty::ret_type dyn_cast(Y &Val) { - return isa(Val) ? cast(Val) : nullptr; +template +LLVM_NODISCARD inline typename CastInfo::CastResultType +dyn_cast(From &Val) { + return CastInfo::doCastIfPossible(Val); } -template -LLVM_NODISCARD inline typename cast_retty::ret_type dyn_cast(Y *Val) { - return isa(Val) ? cast(Val) : nullptr; +template +LLVM_NODISCARD inline typename CastInfo::CastResultType +dyn_cast(From *Val) { + return CastInfo::doCastIfPossible(Val); } -// dyn_cast_or_null - Functionally identical to dyn_cast, except that a null -// value is accepted. -// -template -LLVM_NODISCARD inline std::enable_if_t< - !is_simple_type::value, typename cast_retty::ret_type> -dyn_cast_or_null(const Y &Val) { - return (Val && isa(Val)) ? cast(Val) : nullptr; +template +LLVM_NODISCARD inline + typename CastInfo>::CastResultType + dyn_cast(std::unique_ptr &&Val) { + return CastInfo>::doCastIfPossible(std::move(Val)); } +/// isa_and_present - Functionally identical to isa, except that a null value +/// is accepted. +template +LLVM_NODISCARD inline bool isa_and_present(const Y &Val) { + if (!isPresent(Val)) + return false; + return isa(Val); +} + +template +LLVM_NODISCARD inline bool isa_and_nonnull(const Y &Val) { + return isa_and_present(Val); +} + +/// cast_if_present - Functionally identical to cast, except that a null +/// value is accepted. template -LLVM_NODISCARD inline std::enable_if_t::value, - typename cast_retty::ret_type> -dyn_cast_or_null(Y &Val) { - return (Val && isa(Val)) ? cast(Val) : nullptr; +LLVM_NODISCARD inline auto cast_if_present(const Y &Val) { + if (!isPresent(Val)) + return CastInfo::castFailed(); + assert(isa(Val) && "cast_if_present() argument of incompatible type!"); + return cast(unwrapValue(Val)); +} + +template LLVM_NODISCARD inline auto cast_if_present(Y &Val) { + if (!isPresent(Val)) + return CastInfo::castFailed(); + assert(isa(Val) && "cast_if_present() argument of incompatible type!"); + return cast(unwrapValue(Val)); +} + +template LLVM_NODISCARD inline auto cast_if_present(Y *Val) { + if (!isPresent(Val)) + return CastInfo::castFailed(); + assert(isa(Val) && "cast_if_present() argument of incompatible type!"); + return cast(unwrapValue(Val)); } template -LLVM_NODISCARD inline typename cast_retty::ret_type -dyn_cast_or_null(Y *Val) { - return (Val && isa(Val)) ? cast(Val) : nullptr; +LLVM_NODISCARD inline auto cast_if_present(std::unique_ptr &&Val) { + if (!isPresent(Val)) + return UniquePtrCast::castFailed(); + return UniquePtrCast::doCast(std::move(Val)); +} + +// Provide a forwarding from cast_or_null to cast_if_present for current +// users. This is deprecated and will be removed in a future patch, use +// cast_if_present instead. +template auto cast_or_null(const Y &Val) { + return cast_if_present(Val); +} + +template auto cast_or_null(Y &Val) { + return cast_if_present(Val); +} + +template auto cast_or_null(Y *Val) { + return cast_if_present(Val); +} + +template auto cast_or_null(std::unique_ptr &&Val) { + return cast_if_present(std::move(Val)); +} + +/// dyn_cast_if_present - Functionally identical to dyn_cast, except that a +/// null (or none in the case of optionals) value is accepted. +template auto dyn_cast_if_present(const Y &Val) { + if (!isPresent(Val)) + return CastInfo::castFailed(); + return CastInfo::doCastIfPossible(unwrapValue(Val)); +} + +template auto dyn_cast_if_present(Y &Val) { + if (!isPresent(Val)) + return CastInfo::castFailed(); + return CastInfo::doCastIfPossible(unwrapValue(Val)); +} + +template auto dyn_cast_if_present(Y *Val) { + if (!isPresent(Val)) + return CastInfo::castFailed(); + return CastInfo::doCastIfPossible(unwrapValue(Val)); +} + +// Forwards to dyn_cast_if_present to avoid breaking current users. This is +// deprecated and will be removed in a future patch, use +// cast_if_present instead. +template auto dyn_cast_or_null(const Y &Val) { + return dyn_cast_if_present(Val); +} + +template auto dyn_cast_or_null(Y &Val) { + return dyn_cast_if_present(Val); +} + +template auto dyn_cast_or_null(Y *Val) { + return dyn_cast_if_present(Val); } -// 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. +/// 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)) { +LLVM_NODISCARD inline typename CastInfo>::CastResultType +unique_dyn_cast(std::unique_ptr &Val) { if (!isa(Val)) return nullptr; return cast(std::move(Val)); @@ -386,11 +797,11 @@ return unique_dyn_cast(Val); } -// dyn_cast_or_null - Functionally identical to unique_dyn_cast, except that -// a null value is accepted. +// unique_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)) { +LLVM_NODISCARD inline typename CastInfo>::CastResultType +unique_dyn_cast_or_null(std::unique_ptr &Val) { if (!Val) return nullptr; return unique_dyn_cast(Val); 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 @@ -28,15 +28,13 @@ struct foo *caz(); struct foo *daz(); struct foo *naz(); + private: bar(const bar &); }; struct foo { + foo(const bar &) {} void ext() const; - /* static bool classof(const bar *X) { - cerr << "Classof: " << X << "\n"; - return true; - }*/ }; struct base { @@ -54,26 +52,21 @@ } }; +// Note for the future - please don't do this. isa_impl is an internal template +// for the implementation of `isa` and should not be exposed this way. +// Completely unrelated types *should* result in compiler errors if you try to +// cast between them. template struct isa_impl { static inline bool doit(const T &Val) { return false; } }; -foo *bar::baz() { - return cast(this); -} +foo *bar::baz() { return cast(this); } -foo *bar::caz() { - return cast_or_null(this); -} +foo *bar::caz() { return cast_or_null(this); } -foo *bar::daz() { - return dyn_cast(this); -} - -foo *bar::naz() { - return dyn_cast_or_null(this); -} +foo *bar::daz() { return dyn_cast(this); } +foo *bar::naz() { return dyn_cast_or_null(this); } bar *fub(); @@ -82,10 +75,54 @@ static SimpleType getSimplifiedValue(foo &Val) { return 0; } }; -} // End llvm namespace +struct T1 {}; -using namespace llvm; +struct T2 { + T2(const T1 &x) {} + static bool classof(const T1 *x) { return true; } +}; + +// Provide a pointer cast and a fallible value cast. +template <> +struct CastInfo : public CheckedPointerCast, + public FallibleValueCast {}; + +struct T3 { + T3(const T1 *x) : hasValue(x != nullptr) {} + + static bool classof(const T1 *x) { return true; } + bool hasValue = false; +}; +// T3 is convertible from a pointer to T1. +template <> struct CastInfo : public ValueFromPointerCast {}; + +struct T4 { + T4() : hasValue(false) {} + T4(const T3 &x) : hasValue(true) {} + + static bool classof(const T3 *x) { return true; } + bool hasValue = false; +}; + +template <> struct ValueIsPresent { + using UnwrappedType = T3; + static inline bool isPresent(const T3 &t) { return t.hasValue; } + static inline const T3 &unwrapValue(const T3 &t) { return t; } +}; + +template <> struct FallibleCastTraits { + using CastResultType = T4; + static inline CastResultType doCast(const T3 &t) { return T4(t); } + static inline CastResultType castFailed() { return CastResultType(); } + static inline CastResultType doCastIfPossible(const T3 &f) { + return doCast(f); + } +}; + +} // namespace llvm + +using namespace llvm; // Test the peculiar behavior of Use in simplify_type. static_assert(std::is_same::SimpleType, Value *>::value, @@ -156,7 +193,7 @@ EXPECT_NE(F12, null_foo); const foo *F13 = cast_or_null(B4); EXPECT_NE(F13, null_foo); - const foo *F14 = cast_or_null(fub()); // Shouldn't print. + const foo *F14 = cast_or_null(fub()); // Shouldn't print. EXPECT_EQ(F14, null_foo); foo *F15 = B1.caz(); EXPECT_NE(F15, null_foo); @@ -180,6 +217,8 @@ EXPECT_NE(F5, null_foo); } +// All these tests forward to dyn_cast_if_present, so they also provde an +// effective test for its use cases. TEST(CastingTest, dyn_cast_or_null) { const foo *F1 = dyn_cast_or_null(B2); EXPECT_NE(F1, null_foo); @@ -191,6 +230,43 @@ EXPECT_EQ(F4, null_foo); foo *F5 = B1.naz(); EXPECT_NE(F5, null_foo); + // dyn_cast_if_present should have exactly the same behavior as + // dyn_cast_or_null. + const foo *F6 = dyn_cast_if_present(B2); + EXPECT_EQ(F6, F2); +} + +TEST(CastingTest, dyn_cast_value_types) { + T1 t1; + Optional t2 = dyn_cast(t1); + EXPECT_TRUE(t2.hasValue()); + + T2 *t2ptr = dyn_cast(&t1); + EXPECT_TRUE(t2ptr != nullptr); + + T3 t3 = dyn_cast(&t1); + EXPECT_TRUE(t3.hasValue); +} + +TEST(CastingTest, dyn_cast_if_present) { + Optional empty{}; + Optional F1 = dyn_cast_if_present(empty); + EXPECT_FALSE(F1.hasValue()); + + T1 t1; + Optional F2 = dyn_cast_if_present(t1); + EXPECT_TRUE(F2.hasValue()); + + T1 *t1Null = nullptr; + + // T3 should have hasValue == false because t1Null is nullptr. + T3 t3 = dyn_cast_if_present(t1Null); + EXPECT_FALSE(t3.hasValue); + + // Now because of that, T4 should receive the castFailed implementation of its + // FallibleCastTraits, which default-constructs a T4, which has no value. + T4 t4 = dyn_cast_if_present(t3); + EXPECT_FALSE(t4.hasValue); } std::unique_ptr newd() { return std::make_unique(); } @@ -227,6 +303,9 @@ // Converting between unrelated types should fail. The original value should // remain unchanged and it should return nullptr. + // + // Note that this is a very contrived test - most of the time we want a cast + // like this to emit a compiler error. auto F = unique_dyn_cast(D); ASSERT_EQ(nullptr, F); ASSERT_EQ(OrigD, D.get()); @@ -246,14 +325,14 @@ } // These lines are errors... -//foo *F20 = cast(B2); // Yields const foo* -//foo &F21 = cast(B3); // Yields const foo& -//foo *F22 = cast(B4); // Yields const foo* -//foo &F23 = cast_or_null(B1); -//const foo &F24 = cast_or_null(B3); +// foo *F20 = cast(B2); // Yields const foo* +// foo &F21 = cast(B3); // Yields const foo& +// foo *F22 = cast(B4); // Yields const foo* +// foo &F23 = cast_or_null(B1); +// const foo &F24 = cast_or_null(B3); const bar *B2 = &B; -} // anonymous namespace +} // anonymous namespace bar *llvm::fub() { return nullptr; } @@ -286,16 +365,13 @@ EXPECT_NE(BP, nullptr); } - // This test verifies that the inferred upcast takes precedence over an // explicitly written one. This is important because it verifies that the // dynamic check gets optimized away. class UseInferredUpcast { public: int Dummy; - static bool classof(const UseInferredUpcast *) { - return false; - } + static bool classof(const UseInferredUpcast *) { return false; } }; TEST(CastingTest, InferredUpcastTakesPrecedence) { @@ -310,8 +386,8 @@ // Test that we reject casts of temporaries (and so the illegal cast gets used). namespace TemporaryCast { struct pod {}; -IllegalCast *testIllegalCast() { return cast(pod()); } -} +// IllegalCast *testIllegalCast() { return cast(pod()); } +} // namespace TemporaryCast namespace { namespace pointer_wrappers { @@ -328,6 +404,7 @@ class PTy { Base *B; + public: PTy(Base *B) : B(B) {} explicit operator bool() const { return get(); } @@ -339,14 +416,41 @@ namespace llvm { +template <> struct ValueIsPresent { + using UnwrappedType = pointer_wrappers::PTy; + static inline bool isPresent(const pointer_wrappers::PTy &P) { + return P.get() != nullptr; + } + static const UnwrappedType &unwrapValue(const pointer_wrappers::PTy &P) { + return P; + } +}; + +template <> struct ValueIsPresent { + using UnwrappedType = pointer_wrappers::PTy; + static inline bool isPresent(const pointer_wrappers::PTy &P) { + return P.get() != nullptr; + } + + static const UnwrappedType &unwrapValue(const pointer_wrappers::PTy &P) { + return P; + } +}; + template <> struct simplify_type { typedef pointer_wrappers::Base *SimpleType; + static bool valueIsPresent(pointer_wrappers::PTy &P) { + return P.get() != nullptr; + } static SimpleType getSimplifiedValue(pointer_wrappers::PTy &P) { return P.get(); } }; template <> struct simplify_type { typedef pointer_wrappers::Base *SimpleType; + static bool valueIsPresent(const pointer_wrappers::PTy &P) { + return P.get() != nullptr; + } static SimpleType getSimplifiedValue(const pointer_wrappers::PTy &P) { return P.get(); }