1 //===-- include/flang/Evaluate/type.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_EVALUATE_TYPE_H_
10 #define FORTRAN_EVALUATE_TYPE_H_
11 
12 // These definitions map Fortran's intrinsic types, characterized by byte
13 // sizes encoded in KIND type parameter values, to their value representation
14 // types in the evaluation library, which are parameterized in terms of
15 // total bit width and real precision.  Instances of the Type class template
16 // are suitable for use as template parameters to instantiate other class
17 // templates, like expressions, over the supported types and kinds.
18 
19 #include "common.h"
20 #include "complex.h"
21 #include "formatting.h"
22 #include "integer.h"
23 #include "logical.h"
24 #include "real.h"
25 #include "flang/Common/Fortran.h"
26 #include "flang/Common/idioms.h"
27 #include "flang/Common/real.h"
28 #include "flang/Common/template.h"
29 #include <cinttypes>
30 #include <optional>
31 #include <string>
32 #include <type_traits>
33 #include <variant>
34 
35 namespace Fortran::semantics {
36 class DeclTypeSpec;
37 class DerivedTypeSpec;
38 class ParamValue;
39 class Symbol;
40 bool IsDescriptor(const Symbol &);
41 } // namespace Fortran::semantics
42 
43 namespace Fortran::evaluate {
44 
45 using common::TypeCategory;
46 
47 // Specific intrinsic types are represented by specializations of
48 // this class template Type<CATEGORY, KIND>.
49 template <TypeCategory CATEGORY, int KIND = 0> class Type;
50 
51 using SubscriptInteger = Type<TypeCategory::Integer, 8>;
52 using CInteger = Type<TypeCategory::Integer, 4>;
53 using LogicalResult = Type<TypeCategory::Logical, 4>;
54 using LargestReal = Type<TypeCategory::Real, 16>;
55 
56 // A predicate that is true when a kind value is a kind that could possibly
57 // be supported for an intrinsic type category on some target instruction
58 // set architecture.
59 // TODO: specialize for the actual target architecture
IsValidKindOfIntrinsicType(TypeCategory category,std::int64_t kind)60 static constexpr bool IsValidKindOfIntrinsicType(
61     TypeCategory category, std::int64_t kind) {
62   switch (category) {
63   case TypeCategory::Integer:
64     return kind == 1 || kind == 2 || kind == 4 || kind == 8 || kind == 16;
65   case TypeCategory::Real:
66   case TypeCategory::Complex:
67     return kind == 2 || kind == 3 || kind == 4 || kind == 8 || kind == 10 ||
68         kind == 16;
69   case TypeCategory::Character:
70     return kind == 1 || kind == 2 || kind == 4;
71   case TypeCategory::Logical:
72     return kind == 1 || kind == 2 || kind == 4 || kind == 8;
73   default:
74     return false;
75   }
76 }
77 
78 // DynamicType is meant to be suitable for use as the result type for
79 // GetType() functions and member functions; consequently, it must be
80 // capable of being used in a constexpr context.  So it does *not*
81 // directly hold anything requiring a destructor, such as an arbitrary
82 // CHARACTER length type parameter expression.  Those must be derived
83 // via LEN() member functions, packaged elsewhere (e.g. as in
84 // ArrayConstructor), copied from a parameter spec in the symbol table
85 // if one is supplied, or a known integer value.
86 class DynamicType {
87 public:
DynamicType(TypeCategory cat,int k)88   constexpr DynamicType(TypeCategory cat, int k) : category_{cat}, kind_{k} {
89     CHECK(IsValidKindOfIntrinsicType(category_, kind_));
90   }
91   DynamicType(int charKind, const semantics::ParamValue &len);
DynamicType(int k,std::int64_t len)92   constexpr DynamicType(int k, std::int64_t len)
93       : category_{TypeCategory::Character}, kind_{k}, knownLength_{len} {
94     CHECK(IsValidKindOfIntrinsicType(category_, kind_));
95   }
96   explicit constexpr DynamicType(
97       const semantics::DerivedTypeSpec &dt, bool poly = false)
98       : category_{TypeCategory::Derived}, derived_{&dt} {
99     if (poly) {
100       kind_ = ClassKind;
101     }
102   }
CONSTEXPR_CONSTRUCTORS_AND_ASSIGNMENTS(DynamicType)103   CONSTEXPR_CONSTRUCTORS_AND_ASSIGNMENTS(DynamicType)
104 
105   // A rare use case used for representing the characteristics of an
106   // intrinsic function like REAL() that accepts a typeless BOZ literal
107   // argument and for typeless pointers -- things that real user Fortran can't
108   // do.
109   static constexpr DynamicType TypelessIntrinsicArgument() {
110     DynamicType result;
111     result.category_ = TypeCategory::Integer;
112     result.kind_ = TypelessKind;
113     return result;
114   }
115 
UnlimitedPolymorphic()116   static constexpr DynamicType UnlimitedPolymorphic() {
117     DynamicType result;
118     result.category_ = TypeCategory::Derived;
119     result.kind_ = ClassKind;
120     result.derived_ = nullptr;
121     return result; // CLASS(*)
122   }
123 
AssumedType()124   static constexpr DynamicType AssumedType() {
125     DynamicType result;
126     result.category_ = TypeCategory::Derived;
127     result.kind_ = AssumedTypeKind;
128     result.derived_ = nullptr;
129     return result; // TYPE(*)
130   }
131 
132   // Comparison is deep -- type parameters are compared independently.
133   bool operator==(const DynamicType &) const;
134   bool operator!=(const DynamicType &that) const { return !(*this == that); }
135 
category()136   constexpr TypeCategory category() const { return category_; }
kind()137   constexpr int kind() const {
138     CHECK(kind_ > 0);
139     return kind_;
140   }
charLengthParamValue()141   constexpr const semantics::ParamValue *charLengthParamValue() const {
142     return charLengthParamValue_;
143   }
knownLength()144   constexpr std::optional<std::int64_t> knownLength() const {
145 #if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 7
146     if (knownLength_ < 0) {
147       return std::nullopt;
148     }
149 #endif
150     return knownLength_;
151   }
152   std::optional<Expr<SubscriptInteger>> GetCharLength() const;
153 
154   std::size_t GetAlignment(const FoldingContext &) const;
155   std::optional<Expr<SubscriptInteger>> MeasureSizeInBytes(
156       FoldingContext &, bool aligned) const;
157 
158   std::string AsFortran() const;
159   std::string AsFortran(std::string &&charLenExpr) const;
160   DynamicType ResultTypeForMultiply(const DynamicType &) const;
161 
162   bool IsAssumedLengthCharacter() const;
163   bool IsNonConstantLengthCharacter() const;
164   bool IsTypelessIntrinsicArgument() const;
IsAssumedType()165   constexpr bool IsAssumedType() const { // TYPE(*)
166     return kind_ == AssumedTypeKind;
167   }
IsPolymorphic()168   constexpr bool IsPolymorphic() const { // TYPE(*) or CLASS()
169     return kind_ == ClassKind || IsAssumedType();
170   }
IsUnlimitedPolymorphic()171   constexpr bool IsUnlimitedPolymorphic() const { // TYPE(*) or CLASS(*)
172     return IsPolymorphic() && !derived_;
173   }
GetDerivedTypeSpec()174   constexpr const semantics::DerivedTypeSpec &GetDerivedTypeSpec() const {
175     return DEREF(derived_);
176   }
177 
178   bool RequiresDescriptor() const;
179   bool HasDeferredTypeParameter() const;
180 
181   // 7.3.2.3 & 15.5.2.4 type compatibility.
182   // x.IsTkCompatibleWith(y) is true if "x => y" or passing actual y to
183   // dummy argument x would be valid.  Be advised, this is not a reflexive
184   // relation.  Kind type parameters must match.
185   bool IsTkCompatibleWith(const DynamicType &) const;
186 
187   // Result will be missing when a symbol is absent or
188   // has an erroneous type, e.g., REAL(KIND=666).
189   static std::optional<DynamicType> From(const semantics::DeclTypeSpec &);
190   static std::optional<DynamicType> From(const semantics::Symbol &);
191 
From(const A & x)192   template <typename A> static std::optional<DynamicType> From(const A &x) {
193     return x.GetType();
194   }
From(const A * p)195   template <typename A> static std::optional<DynamicType> From(const A *p) {
196     if (!p) {
197       return std::nullopt;
198     } else {
199       return From(*p);
200     }
201   }
202   template <typename A>
From(const std::optional<A> & x)203   static std::optional<DynamicType> From(const std::optional<A> &x) {
204     if (x) {
205       return From(*x);
206     } else {
207       return std::nullopt;
208     }
209   }
210 
211 private:
212   // Special kind codes are used to distinguish the following Fortran types.
213   enum SpecialKind {
214     TypelessKind = -1, // BOZ actual argument to intrinsic function or pointer
215                        // argument to ASSOCIATED
216     ClassKind = -2, // CLASS(T) or CLASS(*)
217     AssumedTypeKind = -3, // TYPE(*)
218   };
219 
DynamicType()220   constexpr DynamicType() {}
221 
222   TypeCategory category_{TypeCategory::Derived}; // overridable default
223   int kind_{0};
224   const semantics::ParamValue *charLengthParamValue_{nullptr};
225 #if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 7
226   // GCC 7's optional<> lacks a constexpr operator=
227   std::int64_t knownLength_{-1};
228 #else
229   std::optional<std::int64_t> knownLength_;
230 #endif
231   const semantics::DerivedTypeSpec *derived_{nullptr}; // TYPE(T), CLASS(T)
232 };
233 
234 // Return the DerivedTypeSpec of a DynamicType if it has one.
235 const semantics::DerivedTypeSpec *GetDerivedTypeSpec(const DynamicType &);
236 const semantics::DerivedTypeSpec *GetDerivedTypeSpec(
237     const std::optional<DynamicType> &);
238 const semantics::DerivedTypeSpec *GetParentTypeSpec(
239     const semantics::DerivedTypeSpec &);
240 
241 std::string DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec &);
242 
243 template <TypeCategory CATEGORY, int KIND = 0> struct TypeBase {
244   static constexpr TypeCategory category{CATEGORY};
245   static constexpr int kind{KIND};
246   constexpr bool operator==(const TypeBase &) const { return true; }
GetTypeTypeBase247   static constexpr DynamicType GetType() { return {category, kind}; }
AsFortranTypeBase248   static std::string AsFortran() { return GetType().AsFortran(); }
249 };
250 
251 template <int KIND>
252 class Type<TypeCategory::Integer, KIND>
253     : public TypeBase<TypeCategory::Integer, KIND> {
254 public:
255   using Scalar = value::Integer<8 * KIND>;
256 };
257 
258 template <int KIND>
259 class Type<TypeCategory::Real, KIND>
260     : public TypeBase<TypeCategory::Real, KIND> {
261 public:
262   static constexpr int precision{common::PrecisionOfRealKind(KIND)};
263   static constexpr int bits{common::BitsForBinaryPrecision(precision)};
264   using Scalar = value::Real<value::Integer<bits>, precision>;
265 };
266 
267 // The KIND type parameter on COMPLEX is the kind of each of its components.
268 template <int KIND>
269 class Type<TypeCategory::Complex, KIND>
270     : public TypeBase<TypeCategory::Complex, KIND> {
271 public:
272   using Part = Type<TypeCategory::Real, KIND>;
273   using Scalar = value::Complex<typename Part::Scalar>;
274 };
275 
276 template <>
277 class Type<TypeCategory::Character, 1>
278     : public TypeBase<TypeCategory::Character, 1> {
279 public:
280   using Scalar = std::string;
281 };
282 
283 template <>
284 class Type<TypeCategory::Character, 2>
285     : public TypeBase<TypeCategory::Character, 2> {
286 public:
287   using Scalar = std::u16string;
288 };
289 
290 template <>
291 class Type<TypeCategory::Character, 4>
292     : public TypeBase<TypeCategory::Character, 4> {
293 public:
294   using Scalar = std::u32string;
295 };
296 
297 template <int KIND>
298 class Type<TypeCategory::Logical, KIND>
299     : public TypeBase<TypeCategory::Logical, KIND> {
300 public:
301   using Scalar = value::Logical<8 * KIND>;
302 };
303 
304 // Type functions
305 
306 // Given a specific type, find the type of the same kind in another category.
307 template <TypeCategory CATEGORY, typename T>
308 using SameKind = Type<CATEGORY, std::decay_t<T>::kind>;
309 
310 // Many expressions, including subscripts, CHARACTER lengths, array bounds,
311 // and effective type parameter values, are of a maximal kind of INTEGER.
312 using IndirectSubscriptIntegerExpr =
313     common::CopyableIndirection<Expr<SubscriptInteger>>;
314 
315 // For each intrinsic type category CAT, CategoryTypes<CAT> is an instantiation
316 // of std::tuple<Type<CAT, K>> that comprises every kind value K in that
317 // category that could possibly be supported on any target.
318 template <TypeCategory CATEGORY, int KIND>
319 using CategoryKindTuple =
320     std::conditional_t<IsValidKindOfIntrinsicType(CATEGORY, KIND),
321         std::tuple<Type<CATEGORY, KIND>>, std::tuple<>>;
322 
323 template <TypeCategory CATEGORY, int... KINDS>
324 using CategoryTypesHelper =
325     common::CombineTuples<CategoryKindTuple<CATEGORY, KINDS>...>;
326 
327 template <TypeCategory CATEGORY>
328 using CategoryTypes = CategoryTypesHelper<CATEGORY, 1, 2, 3, 4, 8, 10, 16, 32>;
329 
330 using IntegerTypes = CategoryTypes<TypeCategory::Integer>;
331 using RealTypes = CategoryTypes<TypeCategory::Real>;
332 using ComplexTypes = CategoryTypes<TypeCategory::Complex>;
333 using CharacterTypes = CategoryTypes<TypeCategory::Character>;
334 using LogicalTypes = CategoryTypes<TypeCategory::Logical>;
335 
336 using FloatingTypes = common::CombineTuples<RealTypes, ComplexTypes>;
337 using NumericTypes = common::CombineTuples<IntegerTypes, FloatingTypes>;
338 using RelationalTypes = common::CombineTuples<NumericTypes, CharacterTypes>;
339 using AllIntrinsicTypes = common::CombineTuples<RelationalTypes, LogicalTypes>;
340 using LengthlessIntrinsicTypes =
341     common::CombineTuples<NumericTypes, LogicalTypes>;
342 
343 // Predicates: does a type represent a specific intrinsic type?
344 template <typename T>
345 constexpr bool IsSpecificIntrinsicType{common::HasMember<T, AllIntrinsicTypes>};
346 
347 // Predicate: is a type an intrinsic type that is completely characterized
348 // by its category and kind parameter value, or might it have a derived type
349 // &/or a length type parameter?
350 template <typename T>
351 constexpr bool IsLengthlessIntrinsicType{
352     common::HasMember<T, LengthlessIntrinsicTypes>};
353 
354 // Represents a type of any supported kind within a particular category.
355 template <TypeCategory CATEGORY> struct SomeKind {
356   static constexpr TypeCategory category{CATEGORY};
357   constexpr bool operator==(const SomeKind &) const { return true; }
AsFortranSomeKind358   static std::string AsFortran() {
359     return "Some"s + common::EnumToString(category);
360   }
361 };
362 
363 using NumericCategoryTypes = std::tuple<SomeKind<TypeCategory::Integer>,
364     SomeKind<TypeCategory::Real>, SomeKind<TypeCategory::Complex>>;
365 using AllIntrinsicCategoryTypes = std::tuple<SomeKind<TypeCategory::Integer>,
366     SomeKind<TypeCategory::Real>, SomeKind<TypeCategory::Complex>,
367     SomeKind<TypeCategory::Character>, SomeKind<TypeCategory::Logical>>;
368 
369 // Represents a completely generic type (or, for Expr<SomeType>, a typeless
370 // value like a BOZ literal or NULL() pointer).
371 struct SomeType {
AsFortranSomeType372   static std::string AsFortran() { return "SomeType"s; }
373 };
374 
375 class StructureConstructor;
376 
377 // Represents any derived type, polymorphic or not, as well as CLASS(*).
378 template <> class SomeKind<TypeCategory::Derived> {
379 public:
380   static constexpr TypeCategory category{TypeCategory::Derived};
381   using Scalar = StructureConstructor;
382 
SomeKind()383   constexpr SomeKind() {} // CLASS(*)
SomeKind(const semantics::DerivedTypeSpec & dts)384   constexpr explicit SomeKind(const semantics::DerivedTypeSpec &dts)
385       : derivedTypeSpec_{&dts} {}
SomeKind(const DynamicType & dt)386   constexpr explicit SomeKind(const DynamicType &dt)
387       : SomeKind(dt.GetDerivedTypeSpec()) {}
CONSTEXPR_CONSTRUCTORS_AND_ASSIGNMENTS(SomeKind)388   CONSTEXPR_CONSTRUCTORS_AND_ASSIGNMENTS(SomeKind)
389 
390   bool IsUnlimitedPolymorphic() const { return !derivedTypeSpec_; }
GetType()391   constexpr DynamicType GetType() const {
392     if (!derivedTypeSpec_) {
393       return DynamicType::UnlimitedPolymorphic();
394     } else {
395       return DynamicType{*derivedTypeSpec_};
396     }
397   }
derivedTypeSpec()398   const semantics::DerivedTypeSpec &derivedTypeSpec() const {
399     CHECK(derivedTypeSpec_);
400     return *derivedTypeSpec_;
401   }
402   bool operator==(const SomeKind &) const;
403   std::string AsFortran() const;
404 
405 private:
406   const semantics::DerivedTypeSpec *derivedTypeSpec_{nullptr};
407 };
408 
409 using SomeInteger = SomeKind<TypeCategory::Integer>;
410 using SomeReal = SomeKind<TypeCategory::Real>;
411 using SomeComplex = SomeKind<TypeCategory::Complex>;
412 using SomeCharacter = SomeKind<TypeCategory::Character>;
413 using SomeLogical = SomeKind<TypeCategory::Logical>;
414 using SomeDerived = SomeKind<TypeCategory::Derived>;
415 using SomeCategory = std::tuple<SomeInteger, SomeReal, SomeComplex,
416     SomeCharacter, SomeLogical, SomeDerived>;
417 
418 using AllTypes =
419     common::CombineTuples<AllIntrinsicTypes, std::tuple<SomeDerived>>;
420 
421 template <typename T> using Scalar = typename std::decay_t<T>::Scalar;
422 
423 // When Scalar<T> is S, then TypeOf<S> is T.
424 // TypeOf is implemented by scanning all supported types for a match
425 // with Type<T>::Scalar.
426 template <typename CONST> struct TypeOfHelper {
427   template <typename T> struct Predicate {
valueTypeOfHelper::Predicate428     static constexpr bool value() {
429       return std::is_same_v<std::decay_t<CONST>,
430           std::decay_t<typename T::Scalar>>;
431     }
432   };
433   static constexpr int index{
434       common::SearchMembers<Predicate, AllIntrinsicTypes>};
435   using type = std::conditional_t<index >= 0,
436       std::tuple_element_t<index, AllIntrinsicTypes>, void>;
437 };
438 
439 template <typename CONST> using TypeOf = typename TypeOfHelper<CONST>::type;
440 
441 int SelectedCharKind(const std::string &, int defaultKind);
442 int SelectedIntKind(std::int64_t precision = 0);
443 int SelectedRealKind(
444     std::int64_t precision = 0, std::int64_t range = 0, std::int64_t radix = 2);
445 
446 // For generating "[extern] template class", &c. boilerplate
447 #define EXPAND_FOR_EACH_INTEGER_KIND(M, P, S) \
448   M(P, S, 1) M(P, S, 2) M(P, S, 4) M(P, S, 8) M(P, S, 16)
449 #define EXPAND_FOR_EACH_REAL_KIND(M, P, S) \
450   M(P, S, 2) M(P, S, 3) M(P, S, 4) M(P, S, 8) M(P, S, 10) M(P, S, 16)
451 #define EXPAND_FOR_EACH_COMPLEX_KIND(M, P, S) EXPAND_FOR_EACH_REAL_KIND(M, P, S)
452 #define EXPAND_FOR_EACH_CHARACTER_KIND(M, P, S) M(P, S, 1) M(P, S, 2) M(P, S, 4)
453 #define EXPAND_FOR_EACH_LOGICAL_KIND(M, P, S) \
454   M(P, S, 1) M(P, S, 2) M(P, S, 4) M(P, S, 8)
455 #define TEMPLATE_INSTANTIATION(P, S, ARG) P<ARG> S;
456 
457 #define FOR_EACH_INTEGER_KIND_HELP(PREFIX, SUFFIX, K) \
458   PREFIX<Type<TypeCategory::Integer, K>> SUFFIX;
459 #define FOR_EACH_REAL_KIND_HELP(PREFIX, SUFFIX, K) \
460   PREFIX<Type<TypeCategory::Real, K>> SUFFIX;
461 #define FOR_EACH_COMPLEX_KIND_HELP(PREFIX, SUFFIX, K) \
462   PREFIX<Type<TypeCategory::Complex, K>> SUFFIX;
463 #define FOR_EACH_CHARACTER_KIND_HELP(PREFIX, SUFFIX, K) \
464   PREFIX<Type<TypeCategory::Character, K>> SUFFIX;
465 #define FOR_EACH_LOGICAL_KIND_HELP(PREFIX, SUFFIX, K) \
466   PREFIX<Type<TypeCategory::Logical, K>> SUFFIX;
467 
468 #define FOR_EACH_INTEGER_KIND(PREFIX, SUFFIX) \
469   EXPAND_FOR_EACH_INTEGER_KIND(FOR_EACH_INTEGER_KIND_HELP, PREFIX, SUFFIX)
470 #define FOR_EACH_REAL_KIND(PREFIX, SUFFIX) \
471   EXPAND_FOR_EACH_REAL_KIND(FOR_EACH_REAL_KIND_HELP, PREFIX, SUFFIX)
472 #define FOR_EACH_COMPLEX_KIND(PREFIX, SUFFIX) \
473   EXPAND_FOR_EACH_COMPLEX_KIND(FOR_EACH_COMPLEX_KIND_HELP, PREFIX, SUFFIX)
474 #define FOR_EACH_CHARACTER_KIND(PREFIX, SUFFIX) \
475   EXPAND_FOR_EACH_CHARACTER_KIND(FOR_EACH_CHARACTER_KIND_HELP, PREFIX, SUFFIX)
476 #define FOR_EACH_LOGICAL_KIND(PREFIX, SUFFIX) \
477   EXPAND_FOR_EACH_LOGICAL_KIND(FOR_EACH_LOGICAL_KIND_HELP, PREFIX, SUFFIX)
478 
479 #define FOR_EACH_LENGTHLESS_INTRINSIC_KIND(PREFIX, SUFFIX) \
480   FOR_EACH_INTEGER_KIND(PREFIX, SUFFIX) \
481   FOR_EACH_REAL_KIND(PREFIX, SUFFIX) \
482   FOR_EACH_COMPLEX_KIND(PREFIX, SUFFIX) \
483   FOR_EACH_LOGICAL_KIND(PREFIX, SUFFIX)
484 #define FOR_EACH_INTRINSIC_KIND(PREFIX, SUFFIX) \
485   FOR_EACH_LENGTHLESS_INTRINSIC_KIND(PREFIX, SUFFIX) \
486   FOR_EACH_CHARACTER_KIND(PREFIX, SUFFIX)
487 #define FOR_EACH_SPECIFIC_TYPE(PREFIX, SUFFIX) \
488   FOR_EACH_INTRINSIC_KIND(PREFIX, SUFFIX) \
489   PREFIX<SomeDerived> SUFFIX;
490 
491 #define FOR_EACH_CATEGORY_TYPE(PREFIX, SUFFIX) \
492   PREFIX<SomeInteger> SUFFIX; \
493   PREFIX<SomeReal> SUFFIX; \
494   PREFIX<SomeComplex> SUFFIX; \
495   PREFIX<SomeCharacter> SUFFIX; \
496   PREFIX<SomeLogical> SUFFIX; \
497   PREFIX<SomeDerived> SUFFIX; \
498   PREFIX<SomeType> SUFFIX;
499 #define FOR_EACH_TYPE_AND_KIND(PREFIX, SUFFIX) \
500   FOR_EACH_INTRINSIC_KIND(PREFIX, SUFFIX) \
501   FOR_EACH_CATEGORY_TYPE(PREFIX, SUFFIX)
502 } // namespace Fortran::evaluate
503 #endif // FORTRAN_EVALUATE_TYPE_H_
504