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