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