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 "enum-class.h" 27 #include "variant.h" 28 #include "visit.h" 29 #include <array> 30 #include <functional> 31 #include <list> 32 #include <memory> 33 #include <optional> 34 #include <string> 35 #include <tuple> 36 #include <type_traits> 37 38 #if __GNUC__ == 7 39 // Avoid a deduction bug in GNU 7.x headers by forcing the answer. 40 namespace std { 41 template <typename A> 42 struct is_trivially_copy_constructible<list<A>> : false_type {}; 43 template <typename A> 44 struct is_trivially_copy_constructible<optional<list<A>>> : false_type {}; 45 } // namespace std 46 #endif 47 48 // enable "this is a std::string"s with the 's' suffix 49 using namespace std::literals::string_literals; 50 51 namespace Fortran::common { 52 53 // Helper templates for combining a list of lambdas into an anonymous 54 // struct for use with common::visit() on a std::variant<> sum type. 55 // E.g.: common::visit(visitors{ 56 // [&](const firstType &x) { ... }, 57 // [&](const secondType &x) { ... }, 58 // ... 59 // [&](const auto &catchAll) { ... }}, variantObject); 60 61 template <typename... LAMBDAS> struct visitors : LAMBDAS... { 62 using LAMBDAS::operator()...; 63 }; 64 65 template <typename... LAMBDAS> visitors(LAMBDAS... x) -> visitors<LAMBDAS...>; 66 67 // Calls std::fprintf(stderr, ...), then abort(). 68 [[noreturn]] void die(const char *, ...); 69 70 #define DIE(x) Fortran::common::die(x " at " __FILE__ "(%d)", __LINE__) 71 72 // For switch statement default: labels. 73 #define CRASH_NO_CASE DIE("no case") 74 75 // clang-format off 76 // For switch statements whose cases have return statements for 77 // all possibilities. Clang emits warnings if the default: is 78 // present, gcc emits warnings if it is absent. 79 #if __clang__ 80 #define SWITCH_COVERS_ALL_CASES 81 #else 82 #define SWITCH_COVERS_ALL_CASES default: CRASH_NO_CASE; 83 #endif 84 // clang-format on 85 86 // For cheap assertions that should be applied in production. 87 // To disable, compile with '-DCHECK=(void)' 88 #ifndef CHECK 89 #define CHECK(x) ((x) || (DIE("CHECK(" #x ") failed"), false)) 90 #endif 91 92 // Same as above, but with a custom error message. 93 #ifndef CHECK_MSG 94 #define CHECK_MSG(x, y) ((x) || (DIE("CHECK(" #x ") failed: " #y), false)) 95 #endif 96 97 // User-defined type traits that default to false: 98 // Invoke CLASS_TRAIT(traitName) to define a trait, then put 99 // using traitName = std::true_type; (or false_type) 100 // into the appropriate class definitions. You can then use 101 // typename std::enable_if_t<traitName<...>, ...> 102 // in template specialization definitions. 103 #define CLASS_TRAIT(T) \ 104 namespace class_trait_ns_##T { \ 105 template <typename A> std::true_type test(typename A::T *); \ 106 template <typename A> std::false_type test(...); \ 107 template <typename A> \ 108 constexpr bool has_trait{decltype(test<A>(nullptr))::value}; \ 109 template <typename A> constexpr bool trait_value() { \ 110 if constexpr (has_trait<A>) { \ 111 using U = typename A::T; \ 112 return U::value; \ 113 } else { \ 114 return false; \ 115 } \ 116 } \ 117 } \ 118 template <typename A> constexpr bool T{class_trait_ns_##T::trait_value<A>()}; 119 120 // Check that a pointer is non-null and dereference it 121 #define DEREF(p) Fortran::common::Deref(p, __FILE__, __LINE__) 122 123 template <typename T> constexpr T &Deref(T *p, const char *file, int line) { 124 if (!p) { 125 Fortran::common::die("nullptr dereference at %s(%d)", file, line); 126 } 127 return *p; 128 } 129 130 template <typename T> 131 constexpr T &Deref(const std::unique_ptr<T> &p, const char *file, int line) { 132 if (!p) { 133 Fortran::common::die("nullptr dereference at %s(%d)", file, line); 134 } 135 return *p; 136 } 137 138 // Given a const reference to a value, return a copy of the value. 139 template <typename A> A Clone(const A &x) { return x; } 140 141 // C++ does a weird and dangerous thing when deducing template type parameters 142 // from function arguments: lvalue references are allowed to match rvalue 143 // reference arguments. Template function declarations like 144 // template<typename A> int foo(A &&); 145 // need to be protected against this C++ language feature when functions 146 // may modify such arguments. Use these type functions to invoke SFINAE 147 // on a result type via 148 // template<typename A> common::IfNoLvalue<int, A> foo(A &&); 149 // or, for constructors, 150 // template<typename A, typename = common::NoLvalue<A>> int foo(A &&); 151 // This works with parameter packs too. 152 template <typename A, typename... B> 153 using IfNoLvalue = std::enable_if_t<(... && !std::is_lvalue_reference_v<B>), A>; 154 template <typename... RVREF> using NoLvalue = IfNoLvalue<void, RVREF...>; 155 } // namespace Fortran::common 156 #endif // FORTRAN_COMMON_IDIOMS_H_ 157