1 //===-- include/flang/Common/idioms.h ---------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef FORTRAN_COMMON_IDIOMS_H_ 10 #define FORTRAN_COMMON_IDIOMS_H_ 11 12 // Defines anything that might ever be useful in more than one source file 13 // or that is too weird or too specific to the host C++ compiler to be 14 // exposed elsewhere. 15 16 #ifndef __cplusplus 17 #error this is a C++ program 18 #endif 19 #if __cplusplus < 201703L 20 #error this is a C++17 program 21 #endif 22 #if !__clang__ && defined __GNUC__ && __GNUC__ < 7 23 #error g++ >= 7.2 is required 24 #endif 25 26 #include "llvm/Support/Compiler.h" 27 #include <functional> 28 #include <list> 29 #include <memory> 30 #include <optional> 31 #include <string> 32 #include <tuple> 33 #include <type_traits> 34 #include <variant> 35 36 #if __GNUC__ == 7 37 // Avoid a deduction bug in GNU 7.x headers by forcing the answer. 38 namespace std { 39 template <typename A> 40 struct is_trivially_copy_constructible<list<A>> : false_type {}; 41 template <typename A> 42 struct is_trivially_copy_constructible<optional<list<A>>> : false_type {}; 43 } // namespace std 44 #endif 45 46 // enable "this is a std::string"s with the 's' suffix 47 using namespace std::literals::string_literals; 48 49 namespace Fortran::common { 50 51 // Helper templates for combining a list of lambdas into an anonymous 52 // struct for use with std::visit() on a std::variant<> sum type. 53 // E.g.: std::visit(visitors{ 54 // [&](const firstType &x) { ... }, 55 // [&](const secondType &x) { ... }, 56 // ... 57 // [&](const auto &catchAll) { ... }}, variantObject); 58 59 template <typename... LAMBDAS> struct visitors : LAMBDAS... { 60 using LAMBDAS::operator()...; 61 }; 62 63 template <typename... LAMBDAS> visitors(LAMBDAS... x) -> visitors<LAMBDAS...>; 64 65 // Calls std::fprintf(stderr, ...), then abort(). 66 [[noreturn]] void die(const char *, ...); 67 68 #define DIE(x) Fortran::common::die(x " at " __FILE__ "(%d)", __LINE__) 69 70 // For switch statement default: labels. 71 #define CRASH_NO_CASE DIE("no case") 72 73 // clang-format off 74 // For switch statements whose cases have return statements for 75 // all possibilities. Clang emits warnings if the default: is 76 // present, gcc emits warnings if it is absent. 77 #if __clang__ 78 #define SWITCH_COVERS_ALL_CASES 79 #else 80 #define SWITCH_COVERS_ALL_CASES default: CRASH_NO_CASE; 81 #endif 82 // clang-format on 83 84 // For cheap assertions that should be applied in production. 85 // To disable, compile with '-DCHECK=(void)' 86 #ifndef CHECK 87 #define CHECK(x) ((x) || (DIE("CHECK(" #x ") failed"), false)) 88 #endif 89 90 // User-defined type traits that default to false: 91 // Invoke CLASS_TRAIT(traitName) to define a trait, then put 92 // using traitName = std::true_type; (or false_type) 93 // into the appropriate class definitions. You can then use 94 // typename std::enable_if_t<traitName<...>, ...> 95 // in template specialization definitions. 96 #define CLASS_TRAIT(T) \ 97 namespace class_trait_ns_##T { \ 98 template <typename A> std::true_type test(typename A::T *); \ 99 template <typename A> std::false_type test(...); \ 100 template <typename A> \ 101 constexpr bool has_trait{decltype(test<A>(nullptr))::value}; \ 102 template <typename A> constexpr bool trait_value() { \ 103 if constexpr (has_trait<A>) { \ 104 using U = typename A::T; \ 105 return U::value; \ 106 } else { \ 107 return false; \ 108 } \ 109 } \ 110 } \ 111 template <typename A> constexpr bool T{class_trait_ns_##T::trait_value<A>()}; 112 113 // Define enum class NAME with the given enumerators, a static 114 // function EnumToString() that maps enumerators to std::string, 115 // and a constant NAME_enumSize that captures the number of items 116 // in the enum class. 117 118 std::string EnumIndexToString(int index, const char *names); 119 120 template <typename A> struct ListItemCount { 121 constexpr ListItemCount(std::initializer_list<A> list) : value{list.size()} {} 122 const std::size_t value; 123 }; 124 125 #define ENUM_CLASS(NAME, ...) \ 126 enum class NAME { __VA_ARGS__ }; \ 127 LLVM_ATTRIBUTE_UNUSED static constexpr std::size_t NAME##_enumSize{[] { \ 128 enum { __VA_ARGS__ }; \ 129 return Fortran::common::ListItemCount{__VA_ARGS__}.value; \ 130 }()}; \ 131 LLVM_ATTRIBUTE_UNUSED static inline std::string EnumToString(NAME e) { \ 132 return Fortran::common::EnumIndexToString( \ 133 static_cast<int>(e), #__VA_ARGS__); \ 134 } 135 136 // Check that a pointer is non-null and dereference it 137 #define DEREF(p) Fortran::common::Deref(p, __FILE__, __LINE__) 138 139 template <typename T> constexpr T &Deref(T *p, const char *file, int line) { 140 if (!p) { 141 Fortran::common::die("nullptr dereference at %s(%d)", file, line); 142 } 143 return *p; 144 } 145 146 template <typename T> 147 constexpr T &Deref(const std::unique_ptr<T> &p, const char *file, int line) { 148 if (!p) { 149 Fortran::common::die("nullptr dereference at %s(%d)", file, line); 150 } 151 return *p; 152 } 153 154 // Given a const reference to a value, return a copy of the value. 155 template <typename A> A Clone(const A &x) { return x; } 156 157 // C++ does a weird and dangerous thing when deducing template type parameters 158 // from function arguments: lvalue references are allowed to match rvalue 159 // reference arguments. Template function declarations like 160 // template<typename A> int foo(A &&); 161 // need to be protected against this C++ language feature when functions 162 // may modify such arguments. Use these type functions to invoke SFINAE 163 // on a result type via 164 // template<typename A> common::IfNoLvalue<int, A> foo(A &&); 165 // or, for constructors, 166 // template<typename A, typename = common::NoLvalue<A>> int foo(A &&); 167 // This works with parameter packs too. 168 template <typename A, typename... B> 169 using IfNoLvalue = std::enable_if_t<(... && !std::is_lvalue_reference_v<B>), A>; 170 template <typename... RVREF> using NoLvalue = IfNoLvalue<void, RVREF...>; 171 } // namespace Fortran::common 172 #endif // FORTRAN_COMMON_IDIOMS_H_ 173