// Copyright 2023 The Dawn & Tint Authors // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef SRC_TINT_UTILS_RTTI_SWITCH_H_ #define SRC_TINT_UTILS_RTTI_SWITCH_H_ #include <tuple> #include <utility> #include "src/tint/utils/macros/defer.h" #include "src/tint/utils/memory/aligned_storage.h" #include "src/tint/utils/rtti/castable.h" #include "src/tint/utils/rtti/ignore.h" namespace tint { /// Default can be used as the default case for a Switch(), when all previous cases failed to match. /// /// Example: /// ``` /// Switch(object, /// [&](TypeA*) { /* ... */ }, /// [&](TypeB*) { /* ... */ }, /// [&](Default) { /* If not TypeA or TypeB */ }); /// ``` struct Default { … }; /// SwitchMustMatchCase is a flag that can be passed as the last argument to Switch() which will /// trigger an ICE if none of the cases matched. Cannot be used with Default. /// See TINT_ICE_ON_NO_MATCH struct SwitchMustMatchCase { … }; /// SwitchMustMatchCase is a flag that can be passed as the last argument to Switch() which will /// trigger an ICE if none of the cases matched. Cannot be used with Default. /// /// Example: /// ``` /// Switch(object, /// [&](TypeA*) { /* ... */ }, /// [&](TypeB*) { /* ... */ }, /// TINT_ICE_ON_NO_MATCH); /// ``` #define TINT_ICE_ON_NO_MATCH … } // namespace tint namespace tint::detail { /// Evaluates to the Switch case type being matched by the switch case function `FN`. /// @note does not handle the Default case /// @see Switch(). SwitchCaseType; /// Searches the list of Switch cases for a Default case, returning the index of the Default case. /// If the a Default case is not found in the tuple, then -1 is returned. template <typename TUPLE, std::size_t START_IDX = 0> constexpr int IndexOfDefaultCase() { … } /// Searches the list of Switch cases for a SwitchMustMatchCase flag, returning the index of the /// SwitchMustMatchCase case. If the a SwitchMustMatchCase case is not found in the tuple, then -1 /// is returned. template <typename TUPLE, std::size_t START_IDX = 0> constexpr int IndexOfSwitchMustMatchCase() { … } /// Resolves to T if T is not nullptr_t, otherwise resolves to Ignore. NullptrToIgnore; /// Resolves to `const TYPE` if any of `CASE_RETURN_TYPES` are const or pointer-to-const, otherwise /// resolves to TYPE. PropagateReturnConst; // No: Passthrough /// SwitchReturnTypeImpl is the implementation of SwitchReturnType template <bool IS_CASTABLE, typename REQUESTED_TYPE, typename... CASE_RETURN_TYPES> struct SwitchReturnTypeImpl; /// SwitchReturnTypeImpl specialization for non-castable case types and an explicitly specified /// return type. SwitchReturnTypeImpl<false, REQUESTED_TYPE, CASE_RETURN_TYPES...>; /// SwitchReturnTypeImpl specialization for non-castable case types and an inferred return type. SwitchReturnTypeImpl<false, ::tint::detail::Infer, CASE_RETURN_TYPES...>; /// SwitchReturnTypeImpl specialization for castable case types and an explicitly specified return /// type. SwitchReturnTypeImpl<true, REQUESTED_TYPE, CASE_RETURN_TYPES...>; /// SwitchReturnTypeImpl specialization for castable case types and an inferred return type. SwitchReturnTypeImpl<true, ::tint::detail::Infer, CASE_RETURN_TYPES...>; /// Resolves to the return type for a Switch() with the requested return type `REQUESTED_TYPE` and /// case statement return types. If `REQUESTED_TYPE` is Infer then the return type will be inferred /// from the case return types. SwitchReturnType; /// SwitchCaseReturnTypeImpl is the implementation of SwitchCaseReturnType template <typename CASE, bool is_flag> struct SwitchCaseReturnTypeImpl; /// SwitchCaseReturnTypeImpl specialization for non-flags. SwitchCaseReturnTypeImpl<CASE, false>; /// SwitchCaseReturnTypeImpl specialization for flags. SwitchCaseReturnTypeImpl<CASE, true>; /// Resolves to the return type for a Switch() case. /// If CASE is a flag like SwitchMustMatchCase, then resolves to ::tint::Ignore SwitchCaseReturnType; /// Raises an ICE error that a Switch() was passed a nullptr object and there was no default case [[noreturn]] void ICENoSwitchPassedNullptr(const char* file, unsigned int line); /// Raises an ICE error that a Switch() with a TINT_ICE_ON_NO_MATCH matched no cases. /// @param file the file holding the Switch() /// @param line the line of the TINT_ICE_ON_NO_MATCH /// @type_name the type name of the object passed to Switch() [[noreturn]] void ICENoSwitchCasesMatched(const char* file, unsigned int line, const char* type_name); } // namespace tint::detail namespace tint { /// Switch is used to dispatch one of the provided callback case handler functions based on the type /// of `object` and the parameter type of the case handlers. Switch will sequentially check the type /// of `object` against each of the switch case handler functions, and will invoke the first case /// handler function which has a parameter type that matches the object type. When a case handler is /// matched, it will be called with the single argument of `object` cast to the case handler's /// parameter type. Switch will invoke at most one case handler. Each of the case functions must /// have the signature `R(T*)` or `R(const T*)`, where `T` is the type matched by that case and `R` /// is the return type, consistent across all case handlers. /// /// An optional default case function with the signature `R(Default)` can be used as the last case. /// This default case will be called if all previous cases failed to match. /// /// The last argument may be SwitchMustMatchCase, in which case the Switch will trigger an ICE if /// none of the cases matched. SwitchMustMatchCase cannot be used with a default case. /// /// If `object` is nullptr and a default case is provided, then the default case will be called. If /// `object` is nullptr and no default case is provided, then no cases will be called. /// /// Example: /// ``` /// Switch(object, /// [&](TypeA*) { /* ... */ }, /// [&](TypeB*) { /* ... */ }); /// /// Switch(object, /// [&](TypeA*) { /* ... */ }, /// [&](TypeB*) { /* ... */ }, /// [&](Default) { /* Called if object is not TypeA or TypeB */ }); /// /// Switch(object, /// [&](TypeA*) { /* ... */ }, /// [&](TypeB*) { /* ... */ }, /// SwitchMustMatchCase); /* ICE if object is not TypeA or TypeB */ /// ``` /// /// @param object the object who's type is used to /// @param args the switch cases followed by an optional TINT_ICE_ON_NO_MATCH /// @return the value returned by the called case. If no cases matched, then the zero value for the /// consistent case type. template <typename RETURN_TYPE = ::tint::detail::Infer, typename T = CastableBase, typename... ARGS> inline auto Switch(T* object, ARGS&&... args) { … } } // namespace tint #endif // SRC_TINT_UTILS_RTTI_SWITCH_H_