1 // Copyright (c) 2018-2019, NVIDIA CORPORATION.  All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef FORTRAN_COMMON_IDIOMS_H_
16 #define FORTRAN_COMMON_IDIOMS_H_
17 
18 // Defines anything that might ever be useful in more than one source file
19 // or that is too weird or too specific to the host C++ compiler to be
20 // exposed elsewhere.
21 
22 #ifndef __cplusplus
23 #error this is a C++ program
24 #endif
25 #if __cplusplus < 201703L
26 #error this is a C++17 program
27 #endif
28 #if !__clang__ && defined __GNUC__ && __GNUC__ < 7
29 #error g++ >= 7.2 is required
30 #endif
31 
32 #include <functional>
33 #include <list>
34 #include <optional>
35 #include <string>
36 #include <tuple>
37 #include <type_traits>
38 #include <variant>
39 
40 #if __GNUC__ == 7
41 // Avoid a deduction bug in GNU 7.x headers by forcing the answer.
42 namespace std {
43 template<typename A>
44 struct is_trivially_copy_constructible<list<A>> : false_type {};
45 template<typename A>
46 struct is_trivially_copy_constructible<optional<list<A>>> : false_type {};
47 }
48 #endif
49 
50 // enable "this is a std::string"s with the 's' suffix
51 using namespace std::literals::string_literals;
52 
53 namespace Fortran::common {
54 
55 // Helper templates for combining a list of lambdas into an anonymous
56 // struct for use with std::visit() on a std::variant<> sum type.
57 // E.g.: std::visit(visitors{
58 //         [&](const firstType &x) { ... },
59 //         [&](const secondType &x) { ... },
60 //         ...
61 //         [&](const auto &catchAll) { ... }}, variantObject);
62 
63 template<typename... LAMBDAS> struct visitors : LAMBDAS... {
64   using LAMBDAS::operator()...;
65 };
66 
67 template<typename... LAMBDAS> visitors(LAMBDAS... x)->visitors<LAMBDAS...>;
68 
69 // Calls std::fprintf(stderr, ...), then abort().
70 [[noreturn]] void die(const char *, ...);
71 
72 #define DIE(x) Fortran::common::die(x " at " __FILE__ "(%d)", __LINE__)
73 
74 // For switch statement default: labels.
75 #define CRASH_NO_CASE DIE("no case")
76 
77 // clang-format off
78 // For switch statements whose cases have return statements for
79 // all possibilities.  Clang emits warnings if the default: is
80 // present, gcc emits warnings if it is absent.
81 #if __clang__
82 #define SWITCH_COVERS_ALL_CASES
83 #else
84 #define SWITCH_COVERS_ALL_CASES default: CRASH_NO_CASE;
85 #endif
86 // clang-format on
87 
88 // For cheap assertions that should be applied in production.
89 // To disable, compile with '-DCHECK=(void)'
90 #ifndef CHECK
91 #define CHECK(x) ((x) || (DIE("CHECK(" #x ") failed"), false))
92 #endif
93 
94 // User-defined type traits that default to false:
95 // Invoke CLASS_TRAIT(traitName) to define a trait, then put
96 //   using traitName = std::true_type;  (or false_type)
97 // into the appropriate class definitions.  You can then use
98 //   typename std::enable_if_t<traitName<...>, ...>
99 // in template specialization definitions.
100 #define CLASS_TRAIT(T) \
101   namespace class_trait_ns_##T { \
102     template<typename A> std::true_type test(typename A::T *); \
103     template<typename A> std::false_type test(...); \
104     template<typename A> \
105     constexpr bool has_trait{decltype(test<A>(nullptr))::value}; \
106     template<typename A> constexpr bool trait_value() { \
107       if constexpr (has_trait<A>) { \
108         using U = typename A::T; \
109         return U::value; \
110       } else { \
111         return false; \
112       } \
113     } \
114   } \
115   template<typename A> constexpr bool T{class_trait_ns_##T::trait_value<A>()};
116 
117 #if !defined ATTRIBUTE_UNUSED && (__clang__ || __GNUC__)
118 #define ATTRIBUTE_UNUSED __attribute__((unused))
119 #endif
120 
121 // Define enum class NAME with the given enumerators, a static
122 // function EnumToString() that maps enumerators to std::string,
123 // and a constant NAME_enumSize that captures the number of items
124 // in the enum class.
125 
126 std::string EnumIndexToString(int index, const char *names);
127 
128 template<typename A> struct ListItemCount {
129   constexpr ListItemCount(std::initializer_list<A> list) : value{list.size()} {}
130   const std::size_t value;
131 };
132 
133 #define ENUM_CLASS(NAME, ...) \
134   enum class NAME { __VA_ARGS__ }; \
135   ATTRIBUTE_UNUSED static constexpr std::size_t NAME##_enumSize{[] { \
136     enum { __VA_ARGS__ }; \
137     return Fortran::common::ListItemCount{__VA_ARGS__}.value; \
138   }()}; \
139   ATTRIBUTE_UNUSED static inline std::string EnumToString(NAME e) { \
140     return Fortran::common::EnumIndexToString( \
141         static_cast<int>(e), #__VA_ARGS__); \
142   }
143 
144 // Check that a pointer is non-null and dereference it
145 #define DEREF(p) Fortran::common::Deref(p, __FILE__, __LINE__)
146 
147 template<typename T> T &Deref(T *p, const char *file, int line) {
148   if (p == nullptr) {
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 }
172 #endif  // FORTRAN_COMMON_IDIOMS_H_
173