From: peter klausler Date: Wed, 22 Aug 2018 20:36:45 +0000 (-0700) Subject: [flang] Replace FOR_EACH_... macro cruft with safer template metaprogramming. X-Git-Tag: llvmorg-12-init~9537^2~2186 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e90480785db2b721ef2ca8f4c2235b82a3c3e73b;p=platform%2Fupstream%2Fllvm.git [flang] Replace FOR_EACH_... macro cruft with safer template metaprogramming. Original-commit: flang-compiler/f18@bc3c4279b053bf9d779bb9b341cde8bd6222260a Reviewed-on: https://github.com/flang-compiler/f18/pull/183 Tree-same-pre-rewrite: false --- diff --git a/flang/lib/common/idioms.h b/flang/lib/common/idioms.h index 1e58172..8a97c1e 100644 --- a/flang/lib/common/idioms.h +++ b/flang/lib/common/idioms.h @@ -129,17 +129,21 @@ template struct ListItemCount { static_cast(e), #__VA_ARGS__); \ } +template std::optional GetIfNonNull(const A *p) { + if (p) { + return {*p}; + } + return std::nullopt; +} + // If a variant holds a value of a particular type, return a copy in a // std::optional<>. template std::optional GetIf(const VARIANT &u) { - if (const A * x{std::get_if(&u)}) { - return {*x}; - } - return std::nullopt; + return GetIfNonNull(std::get_if(&u)); } -// Collapses a nested std::optional> +// Collapses a nested std::optional> to std::optional template std::optional JoinOptionals(std::optional> &&x) { if (x.has_value()) { @@ -168,5 +172,6 @@ std::optional MapOptional(std::function &f, } return std::nullopt; } + } // namespace Fortran::common #endif // FORTRAN_COMMON_IDIOMS_H_ diff --git a/flang/lib/common/kind-variant.h b/flang/lib/common/kind-variant.h new file mode 100644 index 0000000..95058df --- /dev/null +++ b/flang/lib/common/kind-variant.h @@ -0,0 +1,121 @@ +// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_COMMON_KIND_VARIANT_H_ +#define FORTRAN_COMMON_KIND_VARIANT_H_ + +#include "template.h" +#include +#include + +namespace Fortran::common { + +// A KindVariant instantiates a std::variant over a collection of types +// derived by applying a given template to each of a list of "kind" arguments, +// wraps that variant as the sole data member ("u"), and supplies some helpful +// member functions and member function templates to perform reverse +// mappings of both alternative indices and alternative types back to their +// kinds, invoke kind-dependent templates based on dynamic kind values, &c. +template class TYPE, KIND... KINDS> +struct KindVariant { + using Kind = KIND; + + static constexpr auto kinds{sizeof...(KINDS)}; + static constexpr Kind kindValue[kinds]{KINDS...}; + template using KindType = TYPE; + + using Variant = std::variant...>; + + CLASS_BOILERPLATE(KindVariant) + template KindVariant(const A &x) : u{x} {} + template + KindVariant(std::enable_if_t, A> &&x) + : u{std::move(x)} {} + + template KindVariant &operator=(const A &x) { + u = x; + return *this; + } + template KindVariant &operator=(A &&x) { + u = std::move(x); + return *this; + } + + static constexpr Kind IndexToKind(int index) { return kindValue[index]; } + + template + static constexpr Kind TypeToKind{ + IndexToKind(TypeIndex...>)}; + + Kind kind() const { return IndexToKind(u.index()); } + + // Accessors for alternatives as identified by kind or type. + template KindType *GetIfKind() { + if (auto *p{std::get_if>(u)}) { + return p; + } + return nullptr; + } + template const KindType *GetIfKind() const { + if (const auto *p{std::get_if>(u)}) { + return p; + } + return nullptr; + } + template std::optional> GetIf() const { + return common::GetIf>(u); + } + + // Given an instance of some class A with a member template function + // "template void action();", AtKind(A &a, Kind k) will + // invoke a.action with a *dynamic* kind value. +private: + template static void Helper(A &a, Kind k) { + static constexpr Kind K{IndexToKind(J)}; + if (k == K) { + a.template action(); + } else if constexpr (J + 1 < kinds) { + Helper(a, k); + } + } + +public: + template static void AtKind(A &a, Kind k) { Helper(a, k); } + + // When each of the alternatives of a KindVariant has a constructor that + // accepts an rvalue reference to some (same) type A, this template can be + // used to create a KindVariant instance of a forced kind. +private: + template struct SetResult { + explicit SetResult(A &&x) : value{std::move(x)} {} + template void action() { + CHECK(!result.has_value()); + result = KindVariant{KindType{std::move(value)}}; + } + std::optional result; + A value; + }; + +public: + template + static std::optional ForceKind(Kind k, A &&x) { + SetResult setter{std::move(x)}; + AtKind(setter, k); + return std::move(setter.result); + } + + Variant u; +}; +} // namespace Fortran::common +#endif // FORTRAN_COMMON_KIND_VARIANT_H_ diff --git a/flang/lib/common/template.h b/flang/lib/common/template.h new file mode 100644 index 0000000..cd26419 --- /dev/null +++ b/flang/lib/common/template.h @@ -0,0 +1,77 @@ +// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_COMMON_TEMPLATE_H_ +#define FORTRAN_COMMON_TEMPLATE_H_ + +// Template metaprogramming utilities + +namespace Fortran::common { + +// SearchTypeList scans a list of types. The zero-based +// index of the first type T in the list for which PREDICATE::value() is +// true is returned, or -1 if the predicate is false for every type in the list. +template class PREDICATE, typename A, + typename... REST> +struct SearchTypeListTemplate { + static constexpr int value() { + if constexpr (PREDICATE::value()) { + return N; + } else if constexpr (sizeof...(REST) == 0) { + return -1; + } else { + return SearchTypeListTemplate::value(); + } + } +}; + +template class PREDICATE, typename... TYPES> +constexpr int SearchTypeList{ + SearchTypeListTemplate<0, PREDICATE, TYPES...>::value()}; + +// TypeIndex scans a list of types for simple type equality. +// The zero-based index of A in the list is returned, or -1 if A is not present. +template struct MatchType { + template struct Match { + static constexpr bool value() { + return std::is_same_v, std::decay_t>; + } + }; +}; + +template +constexpr int TypeIndex{SearchTypeList::template Match, TYPES...>}; + +// SearchVariantType scans the types that constitute the alternatives +// of a std::variant instantiation. The zero-based index of the first type T +// among the alternatives for which PREDICATE::value() is true is returned, +// or -1 if the predicate is false for every alternative of the union. + +// N.B. It *is* possible to extract the types of the alternatives of a +// std::variant discriminated union instantiation and reuse them as a +// template parameter pack in another template instantiation. The trick is +// to match the std::variant type with a partial specialization. +template class PREDICATE, typename V> +struct SearchVariantTypeTemplate; +template class PREDICATE, typename... Ts> +struct SearchVariantTypeTemplate> { + static constexpr int index{SearchTypeList}; +}; + +template class PREDICATE, typename VARIANT> +constexpr int SearchVariantType{ + SearchVariantTypeTemplate::index}; + +} // namespace Fortran::common +#endif // FORTRAN_COMMON_TEMPLATE_H_ diff --git a/flang/lib/evaluate/expression.cc b/flang/lib/evaluate/expression.cc index 2e493ec..a4e3261 100644 --- a/flang/lib/evaluate/expression.cc +++ b/flang/lib/evaluate/expression.cc @@ -179,7 +179,7 @@ auto Expr>::Fold(FoldingContext &context) } return std::nullopt; }, - u); + u.u); } auto Expr::Fold(FoldingContext &context) @@ -218,7 +218,7 @@ auto Convert::FoldScalar(FoldingContext &context, using Ty = std::decay_t; return Convert::FoldScalar(context, x); }, - c.u); + c.u.u); } else if constexpr (std::is_same_v>) { if constexpr (Result::category == Operand::category) { return {Scalar{c}}; @@ -226,10 +226,10 @@ auto Convert::FoldScalar(FoldingContext &context, } else if constexpr (std::is_same_v>) { return std::visit( [&](const auto &x) -> std::optional> { - using Ty = ScalarValueType>; + using Ty = TypeOf>; return Convert::FoldScalar(context, x); }, - c.u); + c.u.u); } else if constexpr (Result::category == TypeCategory::Integer) { if constexpr (Operand::category == TypeCategory::Integer) { auto converted{Scalar::ConvertSigned(c)}; @@ -398,7 +398,7 @@ auto RealToIntPower::FoldScalar(FoldingContext &context, RealFlagWarnings(context, power.flags, "raising to INTEGER power"); return {std::move(power.value)}; }, - y.u); + y.u.u); } template @@ -540,12 +540,12 @@ std::ostream &DumpExpr(std::ostream &o, const std::variant &u) { template std::ostream &Expr>::Dump(std::ostream &o) const { - return DumpExpr(o, u); + return DumpExpr(o, u.u); } template std::ostream &Relational>::Dump(std::ostream &o) const { - return DumpExpr(o, u); + return DumpExpr(o, u.u); } std::ostream &Expr::Dump(std::ostream &o) const { @@ -658,7 +658,7 @@ auto Expr>::ScalarValue() const -> std::optional> { } return std::nullopt; }, - u); + u.u); } auto Expr::ScalarValue() const -> std::optional> { @@ -679,7 +679,7 @@ auto Expr::ScalarValue() const -> std::optional> { // Rank template int Expr>::Rank() const { - return std::visit([](const auto &x) { return x.Rank(); }, u); + return std::visit([](const auto &x) { return x.Rank(); }, u.u); } int Expr::Rank() const { diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h index e00f5ff..aa66ce2 100644 --- a/flang/lib/evaluate/expression.h +++ b/flang/lib/evaluate/expression.h @@ -37,13 +37,16 @@ namespace Fortran::evaluate { using common::RelationalOperator; // Expr represents an expression whose result is the Fortran type A, -// which can be specific, SomeKind for a type category C, or +// which can be a specific Type, or SomeKind for a type category C, or // Expr for a wholly generic expression. Instances of Expr<> // wrap discriminated unions. template class Expr; template using ResultType = typename std::decay_t::Result; +// Abstract Operation<> base class. The first type parameter is a "CRTP" +// reference to the specific operation class; e.g., Add is defined with +// struct Add : public Operation. template class Operation { public: @@ -495,18 +498,21 @@ struct Relational : public Operation, LogicalResult, A, A> { // A generic relation between two operands of the same kind in some intrinsic // type category (except LOGICAL). template struct Relational> { + static constexpr TypeCategory category{CAT}; using Result = LogicalResult; using Operand = SomeKind; - template using KindRelational = Relational>; + template using KindRelational = Relational>; CLASS_BOILERPLATE(Relational) - template Relational(const KindRelational &x) : u{x} {} - template Relational(KindRelational &&x) : u{std::move(x)} {} + template + Relational(const KindRelational &x) : u{x} {} + template + Relational(KindRelational &&x) : u{std::move(x)} {} std::optional> Fold(FoldingContext &); std::ostream &Dump(std::ostream &) const; - KindsVariant u; + CategoryUnion u; }; template class Expr> { @@ -552,17 +558,31 @@ template class Expr> { public: using Result = SomeKind; using FoldableTrait = std::true_type; + static constexpr TypeCategory category{CAT}; CLASS_BOILERPLATE(Expr) - template using KindExpr = Expr>; - template Expr(const KindExpr &x) : u{x} {} - template Expr(KindExpr &&x) : u{std::move(x)} {} + template using KindExpr = Expr>; + using Variant = CategoryUnion; + Expr(Variant &&x) : u{std::move(x)} {} + template Expr(const KindExpr &x) : u{x} {} + template Expr(KindExpr &&x) : u{std::move(x)} {} std::optional> ScalarValue() const; std::ostream &Dump(std::ostream &) const; std::optional> Fold(FoldingContext &); int Rank() const; - KindsVariant u; + template static std::optional ForceKind(int kind, A &&x) { + if (std::optional result{ + Variant::template ForceKind(kind, std::move(x))}) { + return {Expr{std::move(*result)}}; + } + return std::nullopt; + } + template static void AtKind(A &x, int kind) { + Variant::template AtKind(x, kind); + } + + Variant u; }; // BOZ literal constants need to be wide enough to hold an integer or real diff --git a/flang/lib/evaluate/tools.h b/flang/lib/evaluate/tools.h index f1bc983..36aba3c 100644 --- a/flang/lib/evaluate/tools.h +++ b/flang/lib/evaluate/tools.h @@ -51,7 +51,7 @@ Expr> operator/(Expr> &&x, Expr> &&y) { template Expr> operator-(Expr> &&x) { return std::visit( - [](auto &xk) { return Expr>{-std::move(xk)}; }, x.u); + [](auto &xk) { return Expr>{-std::move(xk)}; }, x.u.u); } template @@ -60,7 +60,7 @@ Expr> operator+(Expr> &&x, Expr> &&y) { [](auto &xk, auto &yk) { return Expr>{std::move(xk) + std::move(yk)}; }, - x.u, y.u); + x.u.u, y.u.u); } template @@ -69,7 +69,7 @@ Expr> operator-(Expr> &&x, Expr> &&y) { [](auto &xk, auto &yk) { return Expr>{std::move(xk) - std::move(yk)}; }, - x.u, y.u); + x.u.u, y.u.u); } template @@ -78,7 +78,7 @@ Expr> operator*(Expr> &&x, Expr> &&y) { [](auto &xk, auto &yk) { return Expr>{std::move(xk) * std::move(yk)}; }, - x.u, y.u); + x.u.u, y.u.u); } template @@ -87,7 +87,7 @@ Expr> operator/(Expr> &&x, Expr> &&y) { [](auto &xk, auto &yk) { return Expr>{std::move(xk) / std::move(yk)}; }, - x.u, y.u); + x.u.u, y.u.u); } // Convert the second argument expression to an expression of the same type @@ -100,7 +100,7 @@ Expr> ConvertToTypeAndKindOf( using SpecificExpr = std::decay_t; return {SpecificExpr{std::move(from)}}; }, - to.u); + to.u.u); } // Ensure that both operands of an intrinsic REAL operation or CMPLX() @@ -122,23 +122,25 @@ void ConvertToSameKind(Expr> &x, Expr> &y) { y.u = Expr{yk}; } }, - x.u, y.u); + x.u.u, y.u.u); } -template Expr> ScalarConstantToExpr(const A &x) { +template Expr> ScalarConstantToExpr(const A &x) { + static_assert(std::is_same_v>, std::decay_t> || + !"TypeOf<> is broken"); return {x}; } template Expr> ToSomeKindExpr(const Expr> &x) { - return Expr>{x}; + return {x}; } template Expr> SomeKindScalarToExpr(const SomeKindScalar &x) { return std::visit( [](const auto &c) { return ToSomeKindExpr(ScalarConstantToExpr(c)); }, - x.u); + x.u.u); } Expr GenericScalarToExpr(const Scalar &); diff --git a/flang/lib/evaluate/type.h b/flang/lib/evaluate/type.h index ec01f51..eba3f55 100644 --- a/flang/lib/evaluate/type.h +++ b/flang/lib/evaluate/type.h @@ -26,6 +26,8 @@ #include "real.h" #include "../common/fortran.h" #include "../common/idioms.h" +#include "../common/kind-variant.h" +#include "../common/template.h" #include #include #include @@ -37,6 +39,8 @@ using common::TypeCategory; // Specific intrinsic types +template struct Type; + template struct TypeBase { static constexpr TypeCategory category{C}; static constexpr int kind{KIND}; @@ -46,8 +50,6 @@ template struct TypeBase { } }; -template struct Type; - template struct Type : public TypeBase { @@ -107,6 +109,7 @@ struct Type }; // Type functions + template using Scalar = typename std::decay_t::Scalar; template @@ -131,66 +134,89 @@ using DefaultCharacter = Type; using SubscriptInteger = Type; using LogicalResult = Type; -// These macros invoke other macros on each of the supported kinds of -// a given category. -// TODO larger CHARACTER kinds, incl. Kanji -#define COMMA , -#define FOR_EACH_INTEGER_KIND(M, SEP) M(1) SEP M(2) SEP M(4) SEP M(8) SEP M(16) -#define FOR_EACH_REAL_KIND(M, SEP) M(2) SEP M(4) SEP M(8) SEP M(10) SEP M(16) -#define FOR_EACH_COMPLEX_KIND(M, SEP) M(2) SEP M(4) SEP M(8) SEP M(10) SEP M(16) -#define FOR_EACH_CHARACTER_KIND(M, SEP) M(1) -#define FOR_EACH_LOGICAL_KIND(M, SEP) M(1) SEP M(2) SEP M(4) SEP M(8) - -#define FOR_EACH_CATEGORY(M) \ - M(Integer, INTEGER) \ - M(Real, REAL) M(Complex, COMPLEX) M(Character, CHARACTER) M(Logical, LOGICAL) - -// These macros and template create instances of std::variant<> that can contain -// applications of some class template to all of the supported kinds of -// a category of intrinsic type. -template class T> struct VariantOverKinds; -#define TKIND(K) T -#define MAKE(Cat, CAT) \ - template class T> \ - struct VariantOverKinds { \ - using type = std::variant; \ +template class TYPE> +struct CategoryUnionTemplate; + +template class TYPE> +struct CategoryUnionTemplate { + static constexpr auto category{TypeCategory::Integer}; + template using PerKind = TYPE; + using type = common::KindVariant; +}; + +template class TYPE> +struct CategoryUnionTemplate { + static constexpr auto category{TypeCategory::Real}; + template using PerKind = TYPE; + using type = common::KindVariant; +}; + +template class TYPE> +struct CategoryUnionTemplate { + static constexpr auto category{TypeCategory::Complex}; + template using PerKind = TYPE; + using type = common::KindVariant; +}; + +template class TYPE> +struct CategoryUnionTemplate { + static constexpr auto category{TypeCategory::Character}; + template using PerKind = TYPE; + using type = common::KindVariant; // TODO: add kinds 2 & 4; +}; + +template class TYPE> +struct CategoryUnionTemplate { + static constexpr auto category{TypeCategory::Logical}; + template using PerKind = TYPE; + using type = common::KindVariant; +}; + +template class TYPE> +using CategoryUnion = typename CategoryUnionTemplate::type; + +template class A> +struct IntrinsicTypeUnionTemplate { + template using PerCategory = CategoryUnion; + using type = common::KindVariant; +}; + +template class A> +using IntrinsicTypeUnion = typename IntrinsicTypeUnionTemplate::type; + +// When Scalar is S, then TypeOf is T. +template struct TypeOfTemplate { + template + struct InnerPredicate { // A is a specific Type + static constexpr bool value() { + return std::is_same_v, + std::decay_t>; + } }; -FOR_EACH_CATEGORY(MAKE) -#undef MAKE -#undef TKIND - -template class T> -using KindsVariant = typename VariantOverKinds::type; - -// Map scalar value types back to their Fortran types. -// For every type T = Type, TypeOfScalarValue> == T. -// E.g., TypeOfScalarValue> is Type. -template struct GetTypeOfScalarValue; -#define TOSV(cat, kind) \ - template<> \ - struct GetTypeOfScalarValue>> { \ - using type = Type; \ + template + struct OuterPredicate { // A is a CategoryUnion + static constexpr bool value() { + return common::SearchVariantType >= + 0; + } }; -#define M(k) TOSV(Integer, k) -FOR_EACH_INTEGER_KIND(M, ) -#undef M -#define M(k) TOSV(Real, k) -FOR_EACH_REAL_KIND(M, ) -#undef M -#define M(k) TOSV(Complex, k) -FOR_EACH_COMPLEX_KIND(M, ) -#undef M -#define M(k) TOSV(Character, k) -FOR_EACH_CHARACTER_KIND(M, ) -#undef M -#define M(k) TOSV(Logical, k) -FOR_EACH_LOGICAL_KIND(M, ) -#undef M -#undef TOSV - -template -using ScalarValueType = - typename GetTypeOfScalarValue>::type; + using BareTypes = IntrinsicTypeUnion; + static constexpr int CatIndex{ + common::SearchVariantType}; + static_assert( + CatIndex >= 0 || !"no category found for type of scalar constant"); + static constexpr TypeCategory category{BareTypes::IndexToKind(CatIndex)}; + using CatType = BareTypes::template KindType; + static constexpr int KindIndex{ + common::SearchVariantType}; + static_assert(KindIndex >= 0 || !"search over category failed when repeated"); + static constexpr int kind{CatType::IndexToKind(KindIndex)}; + using type = Type; +}; + +template using TypeOf = typename TypeOfTemplate::type; // Holds a scalar value of any kind within a particular intrinsic type // category. @@ -206,17 +232,17 @@ template struct SomeKindScalar { std::optional ToInt64() const { if constexpr (category == TypeCategory::Integer) { return std::visit( - [](const auto &x) { return std::make_optional(x.ToInt64()); }, u); + [](const auto &x) { return std::make_optional(x.ToInt64()); }, u.u); } return std::nullopt; } std::optional ToString() const { - return common::GetIf(u); + return common::GetIf(u.u); } - template using KindScalar = Scalar>; - KindsVariant u; + template using KindScalar = Scalar>; + CategoryUnion u; }; // Holds a scalar constant of any intrinsic category and size. diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc index 722d488..1f97135 100644 --- a/flang/lib/semantics/expression.cc +++ b/flang/lib/semantics/expression.cc @@ -100,20 +100,14 @@ static std::optional> AnalyzeLiteral( ExpressionAnalyzer &ea, const parser::CharLiteralConstant &x) { auto kind{ea.Analyze(std::get>(x.t), ExpressionAnalyzer::KindParam{1})}; - switch (kind) { -#define CASE(k) \ - case k: { \ - using Ty = Type; \ - return { \ - Expr{Expr{std::get(x.t)}}}; \ - } - FOR_EACH_CHARACTER_KIND(CASE, ) -#undef CASE - default: - ea.context().messages.Say("unimplemented CHARACTER kind (%ju)"_err_en_US, + auto value{std::get(x.t)}; + using Ex = Expr; + std::optional result{Ex::template ForceKind(kind, std::move(value))}; + if (!result.has_value()) { + ea.context().messages.Say("unsupported CHARACTER(KIND=%ju)"_err_en_US, static_cast(kind)); - return std::nullopt; } + return result; } template MaybeExpr PackageGeneric(std::optional &&x) { @@ -162,20 +156,14 @@ std::optional> IntLiteralConstant( ExpressionAnalyzer &ea, const PARSED &x) { auto kind{ea.Analyze(std::get>(x.t), ea.defaultIntegerKind())}; - auto value{std::get<0>(x.t)}; // std::[u]int64_t - switch (kind) { -#define CASE(k) \ - case k: { \ - using Ty = Type; \ - return {evaluate::ToSomeKindExpr(Expr{value})}; \ - } - FOR_EACH_INTEGER_KIND(CASE, ) -#undef CASE - default: - ea.context().messages.Say("unimplemented INTEGER kind (%ju)"_err_en_US, + auto value{std::get<0>(x.t)}; // std::(u)int64_t + using Ex = Expr; + std::optional result{Ex::template ForceKind(kind, std::move(value))}; + if (!result.has_value()) { + ea.context().messages.Say("unsupported INTEGER(KIND=%ju)"_err_en_US, static_cast(kind)); - return std::nullopt; } + return result; } static std::optional> AnalyzeLiteral( @@ -229,11 +217,23 @@ std::optional> ReadRealLiteral( return {evaluate::ToSomeKindExpr(Expr{value})}; } +struct RealHelper { + RealHelper(parser::CharBlock lit, evaluate::FoldingContext &ctx) + : literal{lit}, context{ctx} {} + template void action() { + CHECK(!result.has_value()); + result = ReadRealLiteral(literal, context); + } + parser::CharBlock literal; + evaluate::FoldingContext &context; + std::optional> result; +}; + static std::optional> AnalyzeLiteral( ExpressionAnalyzer &ea, const parser::RealLiteralConstant &x) { // Use a local message context around the real literal. parser::ContextualMessages ctxMsgs{x.real.source, ea.context().messages}; - evaluate::FoldingContext foldingContext{ctxMsgs, ea.context()}; + evaluate::FoldingContext localFoldingContext{ctxMsgs, ea.context()}; // If a kind parameter appears, it takes precedence. In the absence of // an explicit kind parameter, the exponent letter (e.g., 'e'/'d') // determines the kind. @@ -251,16 +251,13 @@ static std::optional> AnalyzeLiteral( } } auto kind{ea.Analyze(x.kind, defaultKind)}; - switch (kind) { -#define CASE(k) \ - case k: return ReadRealLiteral(x.real.source, foldingContext); - FOR_EACH_REAL_KIND(CASE, ) -#undef CASE - default: - ctxMsgs.Say("unimplemented REAL kind (%ju)"_err_en_US, + RealHelper helper{x.real.source, localFoldingContext}; + Expr::template AtKind(helper, kind); + if (!helper.result.has_value()) { + ctxMsgs.Say("unsupported REAL(KIND=%ju)"_err_en_US, static_cast(kind)); - return std::nullopt; } + return helper.result; } static std::optional> AnalyzeLiteral( @@ -328,19 +325,13 @@ static std::optional> AnalyzeLiteral( auto kind{ea.Analyze(std::get>(x.t), ea.defaultLogicalKind())}; bool value{std::get(x.t)}; - switch (kind) { -#define CASE(k) \ - case k: { \ - using Ty = Type; \ - return {Expr{Expr{value}}}; \ - } - FOR_EACH_LOGICAL_KIND(CASE, ) -#undef CASE - default: - ea.context().messages.Say("unimplemented LOGICAL kind (%ju)"_err_en_US, + using Ex = Expr; + std::optional result{Ex::template ForceKind(kind, std::move(value))}; + if (!result.has_value()) { + ea.context().messages.Say("unsupported LOGICAL(KIND=%ju)"_err_en_US, static_cast(kind)); - return std::nullopt; } + return result; } template<> @@ -594,7 +585,7 @@ std::optional> ExpressionAnalyzer::ConstructComplex( return {Expr{evaluate::ComplexConstructor{ std::move(rx), std::move(ix)}}}; }, - std::move(joined->first.u), std::move(joined->second.u))}; + std::move(joined->first.u.u), std::move(joined->second.u.u))}; } return std::nullopt; }