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 @@ -23,43 +24,47 @@ namespace llvm { //===----------------------------------------------------------------------===// -// 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 +90,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 +175,598 @@ 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); + } +}; + +/// Upcasting (from derived to base) and casting from a type to itself should +/// always be possible. +template +struct CastIsPossible::value>> { + static inline bool isPossible(const From &f) { return true; } +}; + +//===----------------------------------------------------------------------===// +// Cast traits +//===----------------------------------------------------------------------===// + +/// All of these cast traits are meant to be implementations for useful casts +/// that users may want to use that are outside the standard behavior. An +/// example of how to use a special cast called `CastTrait` is: +/// +/// template<> struct CastInfo : public CastTrait {}; +/// +/// Essentially, if your use case falls directly into one of the use cases +/// supported by a given cast trait, simply inherit your special CastInfo +/// directly from one of these to avoid having to reimplement the boilerplate +/// `isPossible/castFailed/doCast/doCastIfPossible`. A cast trait can also +/// provide a subset of those functions. + +/// This cast trait just provides castFailed for the specified `To` type to make +/// CastInfo specializations more declarative. In order to use this, the target +/// result type must be `To` and `To` must be constructible from `nullptr`. +template struct NullableValueCastFailed { + static To castFailed() { return To(nullptr); } +}; + +/// This cast trait just provides the default implementation of doCastIfPossible +/// to make CastInfo specializations more declarative. The `Derived` template +/// parameter *must* be provided for forwarding castFailed and doCast. +template +struct DefaultDoCastIfPossible { + static To doCastIfPossible(From f) { + if (!Derived::isPossible(f)) + return Derived::castFailed(); + return Derived::doCast(f); + } +}; + +namespace detail { +/// A helper to derive the type to use with `Self` for cast traits, when the +/// provided CRTP derived type is allowed to be void. +template +using SelfType = std::conditional_t::value, + Default, OptionalDerived>; +} // namespace detail + +/// This cast trait provides casting for the specific case of casting to a +/// value-typed object from a pointer-typed object. Note that `To` must be +/// nullable/constructible from a pointer to `From` to use this cast. +template +struct ValueFromPointerCast + : public CastIsPossible, + public NullableValueCastFailed, + public DefaultDoCastIfPossible< + To, From *, + detail::SelfType>> { + static inline To doCast(From *f) { return To(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 { + using Self = detail::SelfType>; + using CastResultType = std::unique_ptr< + std::remove_reference_t::ret_type>>; + + static inline CastResultType doCast(std::unique_ptr &&f) { + return CastResultType((typename CastResultType::element_type *)f.release()); + } + + static inline CastResultType castFailed() { return CastResultType(nullptr); } + + static inline CastResultType doCastIfPossible(std::unique_ptr &&f) { + if (!Self::isPossible(f)) + return castFailed(); + return doCast(f); + } +}; + +/// This cast trait provides Optional casting. This means that if you have a +/// value type, you can cast it to another value type and have dyn_cast return +/// an Optional. +template +struct OptionalValueCast + : public CastIsPossible, + public DefaultDoCastIfPossible< + Optional, From, + detail::SelfType>> { + static inline Optional castFailed() { return Optional{}; } + + static inline Optional doCast(const From &f) { return To(f); } +}; + +/// Provides a cast trait that strips `const` from types to make it easier to +/// implement a const-version of a non-const cast. It just removes boilerplate +/// and reduces the amount of code you as the user need to implement. You can +/// use it like this: +/// +/// template<> struct CastInfo { +/// ...verbose implementation... +/// }; +/// +/// template<> struct CastInfo : public +/// ConstStrippingForwardingCast> {}; +/// +template +struct ConstStrippingForwardingCast { + // Remove the pointer if it exists, then we can get rid of consts/volatiles. + using DecayedFrom = std::remove_cv_t>; + // Now if it's a pointer, add it back. Otherwise, we want a ref. + using NonConstFrom = std::conditional_t::value, + DecayedFrom *, DecayedFrom &>; + + static inline bool isPossible(const From &f) { + return ForwardTo::isPossible(const_cast(f)); + } + + static inline decltype(auto) castFailed() { return ForwardTo::castFailed(); } + + static inline decltype(auto) doCast(const From &f) { + return ForwardTo::doCast(const_cast(f)); + } + + static inline decltype(auto) doCastIfPossible(const From &f) { + return ForwardTo::doCastIfPossible(const_cast(f)); + } +}; + +//===----------------------------------------------------------------------===// +// CastInfo +//===----------------------------------------------------------------------===// + +/// This struct provides a method for customizing the way a cast is performed. +/// It inherits from CastIsPossible, to support the case of declaring many +/// CastIsPossible specializations without having to specialize the full +/// CastInfo. +/// +/// In order to specialize different behaviors, specify different functions in +/// your CastInfo specialization. +/// For isa<> customization, provide: +/// +/// `static bool isPossible(const From &f)` +/// +/// For cast<> customization, provide: +/// +/// `static To doCast(const From &f)` +/// +/// For dyn_cast<> and the *_if_present<> variants' customization, provide: +/// +/// `static To castFailed()` and `static To doCastIfPossible(const From &f)` +/// +/// Your specialization might look something like this: +/// +/// template<> struct CastInfo : public CastIsPossible { +/// static inline foo doCast(const bar &b) { +/// return foo(const_cast(b)); +/// } +/// static inline foo castFailed() { return foo(); } +/// static inline foo doCastIfPossible(const bar &b) { +/// if (!CastInfo::isPossible(b)) +/// return castFailed(); +/// return doCast(b); +/// } +/// }; + +// The default implementations of CastInfo don't use cast traits for now because +// we need to specify types all over the place due to the current expected +// casting behavior and the way cast_retty works. New use cases can and should +// take advantage of the cast traits whenever possible! + +template +struct CastInfo : public CastIsPossible { + using Self = CastInfo; + + using CastReturnType = typename cast_retty::ret_type; + + static inline CastReturnType doCast(From &f) { + return cast_convert_val::SimpleType>::doit(f); + } + + // This assumes that you can construct the cast return type from `nullptr`. + // This is largely to support legacy use cases - if you don't want this + // behavior you should specialize CastInfo for your use case. + // + // FIXME: fix legacy use cases to specialize CastInfo so we can remove these + // two - they don't really belong here, as it doesn't make sense to + // have a fallible reference-to-reference cast if the type isn't + // constructible from nullptr, which doesn't really happen in LLVM + // outside of MLIR (which is a separate use case in itself). + static inline CastReturnType castFailed() { return CastReturnType(nullptr); } + + static inline CastReturnType doCastIfPossible(From &f) { + if (!Self::isPossible(f)) + return castFailed(); + return doCast(f); + } +}; + +/// Provides a CastInfo partial specialization for pointers. This version *does* +/// provide castFailed and doCastIfPossible because for pointers, a fallible +/// cast is trivial - just return nullptr. +template +struct CastInfo::value>> + : public CastIsPossible { + using Self = CastInfo; + + using CastReturnType = typename cast_retty::ret_type; + + static inline CastReturnType doCast(From *f) { + // We know it is a simple type, so we can just do cast_convert_val. + return cast_convert_val::doit(f); + } + + static inline CastReturnType castFailed() { return CastReturnType(nullptr); } + + static inline CastReturnType doCastIfPossible(From *f) { + if (!Self::isPossible(f)) + return castFailed(); + return doCast(f); + } +}; + +/// This struct provides an overload for CastInfo where From has simplify_type +/// defined. This simply forwards to the appropriate CastInfo with the +/// simplified type/value, so you don't have to implement both. +template +struct CastInfo::value>> { + using Self = CastInfo; + using SimpleFrom = typename simplify_type::SimpleType; + using SimplifiedSelf = CastInfo; + + static inline bool isPossible(From &f) { + return SimplifiedSelf::isPossible( + simplify_type::getSimplifiedValue(f)); + } + + static inline decltype(auto) doCast(From &f) { + return SimplifiedSelf::doCast(simplify_type::getSimplifiedValue(f)); + } + + static inline decltype(auto) castFailed() { + return SimplifiedSelf::castFailed(); + } + + static inline decltype(auto) doCastIfPossible(From &f) { + return SimplifiedSelf::doCastIfPossible( + simplify_type::getSimplifiedValue(f)); + } +}; + +//===----------------------------------------------------------------------===// +// Pre-specialized CastInfo +//===----------------------------------------------------------------------===// + +/// Provide a CastInfo specialized for std::unique_ptr. +template +struct CastInfo> : public UniquePtrCast {}; + +/// Provide a CastInfo specialized for Optional. It's assumed that if the +/// input is Optional that the output can be Optional. If that's not +/// the case, specialize CastInfo for your use case. +template +struct CastInfo> : public OptionalValueCast {}; + +/// 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); } -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); +template +LLVM_NODISCARD inline bool isa(const From &Val) { + return isa(Val) || isa(Val); } -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); +/// 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 decltype(auto) cast(const From &Val) { + return CastInfo::doCast(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())); +template +LLVM_NODISCARD inline decltype(auto) cast(From &Val) { + return CastInfo::doCast(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 decltype(auto) cast(From *Val) { + return CastInfo::doCast(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); +template +LLVM_NODISCARD inline decltype(auto) cast(std::unique_ptr &&Val) { + return CastInfo>::doCast(std::move(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); +/// 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 decltype(auto) dyn_cast(const From &Val) { + return CastInfo::doCastIfPossible(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 decltype(auto) dyn_cast(From &Val) { + return CastInfo::doCastIfPossible(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 decltype(auto) dyn_cast(From *Val) { + return CastInfo::doCastIfPossible(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; +template +LLVM_NODISCARD inline decltype(auto) dyn_cast(std::unique_ptr &&Val) { + return CastInfo>::doCastIfPossible(std::move(Val)); } -template -LLVM_NODISCARD inline typename cast_retty::ret_type dyn_cast(Y &Val) { - return isa(Val) ? cast(Val) : nullptr; +//===----------------------------------------------------------------------===// +// ValueIsPresent +//===----------------------------------------------------------------------===// + +template +constexpr bool IsNullable = std::is_pointer::value || + std::is_constructible::value; + +/// 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 decltype(auto) unwrapValue(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 decltype(auto) unwrapValue(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 decltype(auto) unwrapValue(T &t) { return t; } +}; + +namespace detail { +// 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 -LLVM_NODISCARD inline typename cast_retty::ret_type dyn_cast(Y *Val) { - return isa(Val) ? cast(Val) : nullptr; +// Convenience function we can use to unwrap a value. +template inline decltype(auto) unwrapValue(T &t) { + return ValueIsPresent::unwrapValue(t); } +} // namespace detail -// 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; +/// 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 (!detail::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 (!detail::isPresent(Val)) + return CastInfo::castFailed(); + assert(isa(Val) && "cast_if_present() argument of incompatible type!"); + return cast(detail::unwrapValue(Val)); +} + +template LLVM_NODISCARD inline auto cast_if_present(Y &Val) { + if (!detail::isPresent(Val)) + return CastInfo::castFailed(); + assert(isa(Val) && "cast_if_present() argument of incompatible type!"); + return cast(detail::unwrapValue(Val)); +} + +template LLVM_NODISCARD inline auto cast_if_present(Y *Val) { + if (!detail::isPresent(Val)) + return CastInfo::castFailed(); + assert(isa(Val) && "cast_if_present() argument of incompatible type!"); + return cast(detail::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 (!detail::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 (!detail::isPresent(Val)) + return CastInfo::castFailed(); + return CastInfo::doCastIfPossible(detail::unwrapValue(Val)); +} + +template auto dyn_cast_if_present(Y &Val) { + if (!detail::isPresent(Val)) + return CastInfo::castFailed(); + return CastInfo::doCastIfPossible(detail::unwrapValue(Val)); +} + +template auto dyn_cast_if_present(Y *Val) { + if (!detail::isPresent(Val)) + return CastInfo::castFailed(); + return CastInfo::doCastIfPossible(detail::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 +777,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::caz() { - return cast_or_null(this); -} +foo *bar::baz() { return cast(this); } -foo *bar::daz() { - return dyn_cast(this); -} +foo *bar::caz() { return cast_or_null(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,51 @@ 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; } +}; + +template <> struct CastInfo : public OptionalValueCast {}; + +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 CastInfo { + 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 +190,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 +214,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 +227,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 +300,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 +322,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 +362,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) { @@ -307,11 +380,6 @@ } // end namespace inferred_upcasting } // end anonymous namespace -// Test that we reject casts of temporaries (and so the illegal cast gets used). -namespace TemporaryCast { -struct pod {}; -IllegalCast *testIllegalCast() { return cast(pod()); } -} namespace { namespace pointer_wrappers { @@ -328,6 +396,7 @@ class PTy { Base *B; + public: PTy(Base *B) : B(B) {} explicit operator bool() const { return get(); } @@ -339,6 +408,25 @@ namespace llvm { +template <> struct ValueIsPresent { + using UnwrappedType = pointer_wrappers::PTy; + static inline bool isPresent(const pointer_wrappers::PTy &P) { + return P.get() != nullptr; + } + static UnwrappedType &unwrapValue(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 UnwrappedType &unwrapValue(const pointer_wrappers::PTy &P) { + return const_cast(P); + } +}; + template <> struct simplify_type { typedef pointer_wrappers::Base *SimpleType; static SimpleType getSimplifiedValue(pointer_wrappers::PTy &P) {