Warning: This file is not a C or C++ file. It does not have highlighting.

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

Warning: This file is not a C or C++ file. It does not have highlighting.

source code of flang/include/flang/Evaluate/type.h