xref: /llvm-project/flang/include/flang/Common/idioms.h (revision 2d3d62d77e2d011c9dbdb12732aca3070efc83ca)
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