From 79408f956d0c2b872e30b6a890dea4072da9bf18 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Fri, 31 Aug 2018 13:28:21 -0700 Subject: [PATCH] [flang] restructure work in progress in semantics/expression.cc; checkpoint before finishing operators Original-commit: flang-compiler/f18@5d991b0df38937f59aeade66fe413b2b5d4d3a4d Reviewed-on: https://github.com/flang-compiler/f18/pull/183 Tree-same-pre-rewrite: false --- flang/lib/common/template.h | 36 +- flang/lib/evaluate/tools.cc | 1 - flang/lib/evaluate/tools.h | 42 +- flang/lib/evaluate/type.h | 16 +- flang/lib/semantics/expression.cc | 802 +++++++++++++++----------------------- flang/lib/semantics/expression.h | 34 +- 6 files changed, 405 insertions(+), 526 deletions(-) diff --git a/flang/lib/common/template.h b/flang/lib/common/template.h index 8ee05b7..28f5e6d 100644 --- a/flang/lib/common/template.h +++ b/flang/lib/common/template.h @@ -29,6 +29,8 @@ 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. +// This is a compile-time operation; see SearchDynamicTypes below for a +// run-time form. template class PREDICATE, typename TUPLE> struct SearchTypeListHelper { static constexpr int value() { @@ -77,7 +79,7 @@ struct OverMembersHelper> { }; template class T, typename TorV> -using OverMembers = typename OverMembersHelper::type; +using OverMembers = typename OverMembersHelper>::type; // SearchMembers scans the types that constitute the alternatives // of a std::variant instantiation or elements of a std::tuple. @@ -243,8 +245,9 @@ std::optional> AllPresent(std::optional &&... x) { // (f(A...) -> R) -> std::optional... -> std::optional // Apply a function to optional arguments if all are present. -// If the function returns std::optional, you will probably want to -// pass it through JoinOptional to "squash" it. +// N.B. If the function returns std::optional, MapOptional will return +// std::optional> and you will probably want to +// run it through JoinOptional to "squash" it. template std::optional MapOptional( std::function &&f, std::optional &&... x) { @@ -254,5 +257,32 @@ std::optional MapOptional( return std::nullopt; } +// Given a VISITOR class of the general form +// struct VISITOR { +// using Result = ...; +// static constexpr std::size_t Types{...}; +// template static Result Test(); +// }; +// SearchDynamicTypes will traverse the indices 0 .. (Types-1) and +// invoke VISITOR::Test() until it returns a value that casts +// to a true Boolean. If no invocation of Test succeeds, it returns a +// default-constructed Result. +template +typename VISITOR::Result SearchDynamicTypesHelper(VISITOR &&visitor) { + if constexpr (J < VISITOR::Types) { + if (auto result{visitor.template Test()}) { + return result; + } + return SearchDynamicTypesHelper(std::move(visitor)); + } else { + return typename VISITOR::Result{}; + } +} + +template +typename VISITOR::Result SearchDynamicTypes(VISITOR &&visitor) { + return SearchDynamicTypesHelper<0, VISITOR>(std::move(visitor)); +} + } // namespace Fortran::common #endif // FORTRAN_COMMON_TEMPLATE_H_ diff --git a/flang/lib/evaluate/tools.cc b/flang/lib/evaluate/tools.cc index 360a4e25..181d717 100644 --- a/flang/lib/evaluate/tools.cc +++ b/flang/lib/evaluate/tools.cc @@ -97,7 +97,6 @@ template class OPR> std::optional> NumericOperation( parser::ContextualMessages &messages, Expr &&x, Expr &&y) { - return std::visit( common::visitors{[](Expr &&ix, Expr &&iy) { return Package( diff --git a/flang/lib/evaluate/tools.h b/flang/lib/evaluate/tools.h index bc430af..b1afa02 100644 --- a/flang/lib/evaluate/tools.h +++ b/flang/lib/evaluate/tools.h @@ -25,6 +25,7 @@ namespace Fortran::evaluate { // Convenience functions and operator overloadings for expression construction. + template Expr> operator-(Expr> &&x) { return {Negate>{std::move(x)}}; @@ -55,6 +56,8 @@ template Expr> operator-(Expr> &&x) { [](auto &xk) { return Expr>{-std::move(xk)}; }, x.u); } +// TODO pmk revisit these below for type safety + template Expr> operator+(Expr> &&x, Expr> &&y) { return std::visit( @@ -91,8 +94,10 @@ Expr> operator/(Expr> &&x, Expr> &&y) { x.u, y.u); } -// Generalizers: these take expressions of more specific types and wrap -// them in more abstract containers. +// Generalizing packagers: these take operations and expressions of more +// specific types and wrap them in Expr<> containers of more abstract types. +// TODO: Would these be better as conversion constructors in the classes? +// TODO: Are the lvalue argument versions still needed? template Expr> AsExpr(const A &x) { return {x}; } template Expr> AsExpr(A &&x) { @@ -109,6 +114,16 @@ Expr> AsCategoryExpr(Expr> &&x) { return {std::move(x)}; } +template +Expr> AsCategoryExpr(SomeKindScalar &&x) { + return std::visit( + [](auto &&scalar) { + using Ty = TypeOf>; + return Expr>{Expr{Constant{std::move(scalar)}}}; + }, + x.u); +} + template Expr AsGenericExpr(const A &x) { return {x}; } template Expr AsGenericExpr(A &&x) { @@ -124,6 +139,26 @@ Expr AsGenericExpr(Expr> &&x) { return {AsCategoryExpr(std::move(x))}; } +template<> inline Expr AsGenericExpr(Constant &&x) { + return std::visit( + [](auto &&scalar) { + using Ty = TypeOf>; + return Expr{Expr>{ + Expr{Constant{std::move(scalar)}}}}; + }, + x.value.u); +} + +template<> inline Expr AsGenericExpr(GenericScalar &&x) { + return std::visit( + [](auto &&scalar) { + using Ty = TypeOf>; + return Expr{Expr>{ + Expr{Constant{std::move(scalar)}}}}; + }, + x.u); +} + // Creation of conversion expressions can be done to either a known // specific intrinsic type with ConvertToType(x) or by converting // one arbitrary expression to the type of another with ConvertTo(to, from). @@ -262,7 +297,7 @@ Expr> PromoteAndCombine( } // Given two expressions of arbitrary type, try to combine them with a -// numeric operation (e.g., Add), possibly with data type conversion of +// binary numeric operation (e.g., Add), possibly with data type conversion of // one of the operands to the type of the other. template class OPR> std::optional> NumericOperation( @@ -270,6 +305,7 @@ std::optional> NumericOperation( extern template std::optional> NumericOperation( parser::ContextualMessages &, Expr &&, Expr &&); +// TODO pmk more } // namespace Fortran::evaluate #endif // FORTRAN_EVALUATE_TOOLS_H_ diff --git a/flang/lib/evaluate/type.h b/flang/lib/evaluate/type.h index 0538d77..2add276 100644 --- a/flang/lib/evaluate/type.h +++ b/flang/lib/evaluate/type.h @@ -154,12 +154,16 @@ template<> struct CategoryTypesHelper { template using CategoryTypes = typename CategoryTypesHelper::type; -using NumericTypes = common::CombineTuples, - CategoryTypes, CategoryTypes>; -using RelationalTypes = - common::CombineTuples>; -using AllIntrinsicTypes = common::CombineTuples>; +using IntegerTypes = CategoryTypes; +using RealTypes = CategoryTypes; +using ComplexTypes = CategoryTypes; +using CharacterTypes = CategoryTypes; +using LogicalTypes = CategoryTypes; + +using FloatingTypes = common::CombineTuples; +using NumericTypes = common::CombineTuples; +using RelationalTypes = common::CombineTuples; +using AllIntrinsicTypes = common::CombineTuples; // When Scalar is S, then TypeOf is T. // TypeOf is implemented by scanning all supported types for a match diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc index 82dae1e..c12c1ea 100644 --- a/flang/lib/semantics/expression.cc +++ b/flang/lib/semantics/expression.cc @@ -18,294 +18,289 @@ #include "../evaluate/common.h" #include "../evaluate/tools.h" #include +#include using namespace Fortran::parser::literals; -namespace Fortran::semantics { +namespace Fortran::evaluate { using common::TypeCategory; -using evaluate::Expr; -using evaluate::SomeKind; -using evaluate::SomeType; -using evaluate::Type; -using MaybeIntExpr = std::optional>; +using MaybeExpr = std::optional>; -// AnalyzeHelper is a local template function that keeps the API -// member function ExpressionAnalyzer::Analyze from having to be a -// many-specialized template itself. -template MaybeExpr AnalyzeHelper(ExpressionAnalyzer &, const A &); +template MaybeExpr AsMaybeExpr(std::optional &&x) { + if (x.has_value()) { + return {AsGenericExpr(AsCategoryExpr(AsExpr(std::move(*x))))}; + } + return std::nullopt; +} -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr &expr) { - return ea.Analyze(expr); +template +MaybeExpr PackageGeneric(std::optional>> &&x) { + if (x.has_value()) { + return {AsGenericExpr(AsCategoryExpr(std::move(*x)))}; + } + return std::nullopt; } -// Template wrappers are traversed with checking. -template -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const std::optional &x) { +template +MaybeExpr AsMaybeExpr(std::optional>> &&x) { if (x.has_value()) { - return AnalyzeHelper(ea, *x); - } else { - return std::nullopt; + return {AsGenericExpr(std::move(*x))}; } + return std::nullopt; } -template -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const common::Indirection &p) { - return AnalyzeHelper(ea, *p); +// This local class wraps some state and a highly overloaded member function. +struct ExprAnalyzer { + using MaybeIntExpr = std::optional>; + + ExprAnalyzer( + FoldingContext &ctx, const semantics::IntrinsicTypeDefaultKinds &dfts) + : context{ctx}, defaults{dfts} {} + + int Analyze( + const std::optional &, int defaultKind, int kanjiKind); + MaybeExpr Analyze(const parser::Expr &); + MaybeExpr Analyze(const parser::LiteralConstant &); + MaybeExpr Analyze(const parser::HollerithLiteralConstant &); + MaybeExpr Analyze(const parser::IntLiteralConstant &); + MaybeExpr Analyze(const parser::SignedIntLiteralConstant &); + MaybeExpr Analyze(const parser::RealLiteralConstant &); + MaybeExpr Analyze(const parser::SignedRealLiteralConstant &); + MaybeExpr Analyze(const parser::ComplexLiteralConstant &); + MaybeExpr Analyze(const parser::BOZLiteralConstant &); + MaybeExpr Analyze(const parser::CharLiteralConstant &); + MaybeExpr Analyze(const parser::LogicalLiteralConstant &); + MaybeExpr Analyze(const parser::Name &); + MaybeExpr Analyze(const parser::NamedConstant &); + MaybeExpr Analyze(const parser::ComplexPart &); + + MaybeExpr Analyze(const parser::Expr::Parentheses &); + MaybeExpr Analyze(const parser::Expr::ComplexConstructor &); + + std::optional> ConstructComplex(MaybeExpr &&, MaybeExpr &&); + + FoldingContext &context; + const semantics::IntrinsicTypeDefaultKinds &defaults; +}; + +// This helper template function handles the Scalar<>, Integer<>, and +// Constant<> wrappers in the parse tree. +// C++ doesn't allow template specialization in a class, so this helper +// template function must be outside ExprAnalyzer and reflect back into it. +template MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const A &x) { + return ea.Analyze(x); } template -auto AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Scalar &tree) - -> decltype(AnalyzeHelper(ea, tree.thing)) { - auto result{AnalyzeHelper(ea, tree.thing)}; - if (result.has_value()) { - if (result->Rank() > 0) { - ea.context().messages.Say("must be scalar"_err_en_US); - return std::nullopt; - } - } - return result; +MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const parser::Scalar &x) { + // TODO: check rank == 0 + return AnalyzeHelper(ea, x.thing); } template -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Constant &tree) { - MaybeExpr result{AnalyzeHelper(ea, tree.thing)}; - if (result.has_value()) { - result->Fold(ea.context()); - if (!result->ScalarValue().has_value()) { - ea.context().messages.Say("must be constant"_err_en_US); - return std::nullopt; +MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const parser::Integer &x) { + if (auto result{AnalyzeHelper(ea, x.thing)}) { + if (std::holds_alternative>(result->u)) { + return result; } + ea.context.messages.Say("expression must be INTEGER"_err_en_US); } - return result; + return std::nullopt; } template -std::optional> AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Integer &tree) { - MaybeExpr result{AnalyzeHelper(ea, tree.thing)}; - if (result.has_value()) { - if (auto *intexpr{std::get_if>(&result->u)}) { - return {std::move(*intexpr)}; +MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const parser::Constant &x) { + if (MaybeExpr result{AnalyzeHelper(ea, x.thing)}) { + if (std::optional> folded{result->Fold(ea.context)}) { + return {AsGenericExpr(std::move(*folded))}; } - ea.context().messages.Say("expression must be INTEGER"_err_en_US); + ea.context.messages.Say("expression must be constant"_err_en_US); } return std::nullopt; } -// pmk: restructure by extracting member function, document, maybe put elsewhere -template struct ConstantHelper { - using FuncResult = std::optional>>; - using Types = evaluate::CategoryTypes; - template FuncResult SetKindTraverser(int kind, VALUE &&value) { - if constexpr (J < std::tuple_size_v) { - using Ty = std::tuple_element_t; - if (kind == Ty::kind) { - return { - AsCategoryExpr(AsExpr(evaluate::Constant{std::move(value)}))}; - } else { - return SetKindTraverser(kind, std::move(value)); - } - } else { - return std::nullopt; - } - } - std::optional>> SetKind( - int kind, VALUE &&value) { - return SetKindTraverser<0>(kind, std::move(value)); - } -}; +template +MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const std::variant &u) { + return std::visit([&](const auto &x) { return AnalyzeHelper(ea, x); }, u); +} -static std::optional> AnalyzeLiteral( - ExpressionAnalyzer &ea, const parser::CharLiteralConstant &x) { - auto kind{ea.Analyze(std::get>(x.t), - ExpressionAnalyzer::KindParam{1})}; - auto value{std::get(x.t)}; - ConstantHelper helper; - auto result{helper.SetKind(kind, std::move(value))}; - if (!result.has_value()) { - ea.context().messages.Say("unsupported CHARACTER(KIND=%ju)"_err_en_US, - static_cast(kind)); - } - return result; +MaybeExpr ExprAnalyzer::Analyze(const parser::Expr &expr) { + return std::visit(common::visitors{[&](const parser::LiteralConstant &c) { + return AnalyzeHelper(*this, c); + }, + // TODO: remaining cases + [&](const auto &) { return MaybeExpr{}; }}, + expr.u); } -template MaybeExpr PackageGeneric(std::optional &&x) { - if (x.has_value()) { - return {evaluate::AsGenericExpr(std::move(*x))}; - } - return std::nullopt; +MaybeExpr ExprAnalyzer::Analyze(const parser::LiteralConstant &x) { + return std::visit([&](const auto &c) { return Analyze(c); }, x.u); } -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::CharLiteralConstantSubstring &x) { - const auto &range{std::get(x.t)}; - const std::optional &lbTree{std::get<0>(range.t)}; - const std::optional &ubTree{std::get<1>(range.t)}; - if (!lbTree.has_value() && !ubTree.has_value()) { - // "..."(:) - return PackageGeneric( - AnalyzeLiteral(ea, std::get(x.t))); - } - // TODO: ensure that any kind parameter is 1 - std::string str{std::get(x.t).GetString()}; - std::optional> lb, ub; - if (lbTree.has_value()) { - if (MaybeIntExpr lbExpr{AnalyzeHelper(ea, *lbTree)}) { - lb = evaluate::ConvertToType( - std::move(*lbExpr)); - } +int ExprAnalyzer::Analyze(const std::optional &kindParam, + int defaultKind, int kanjiKind = -1) { + if (!kindParam.has_value()) { + return defaultKind; } - if (ubTree.has_value()) { - if (MaybeIntExpr ubExpr{AnalyzeHelper(ea, *ubTree)}) { - ub = evaluate::ConvertToType( - std::move(*ubExpr)); + return std::visit( + common::visitors{[](std::uint64_t k) { return static_cast(k); }, + [&](const parser::Scalar< + parser::Integer>> &n) { + if (MaybeExpr ie{AnalyzeHelper(*this, n)}) { + if (std::optional sv{ie->ScalarValue()}) { + if (std::optional i64{sv->ToInt64()}) { + std::int64_t i64v{*i64}; + int iv = i64v; + if (iv == i64v) { + return iv; + } + } + } + } + context.messages.Say( + "KIND type parameter must be a scalar integer constant"_err_en_US); + return defaultKind; + }, + [&](parser::KindParam::Kanji) { + if (kanjiKind >= 0) { + return kanjiKind; + } + context.messages.Say("Kanji not allowed here"_err_en_US); + return defaultKind; + }}, + kindParam->u); +} + +// A helper class used with common::SearchDynamicTypes when constructing +// a literal constant with a dynamic kind in some type category. +template struct ConstantTypeVisitor { + using Result = std::optional>>; + static constexpr std::size_t Types{std::tuple_size_v>}; + + ConstantTypeVisitor(int k, const VALUE &x) : kind{k}, value{x} {} + + template Result Test() { + using Ty = std::tuple_element_t>; + if (kind == Ty::kind) { + return {AsCategoryExpr(AsExpr(Constant{std::move(value)}))}; } - } - if (!lb.has_value() || !ub.has_value()) { return std::nullopt; } - evaluate::Substring substring{std::move(str), std::move(lb), std::move(ub)}; - evaluate::CopyableIndirection ind{std::move(substring)}; - Expr chExpr{std::move(ind)}; - chExpr.Fold(ea.context()); - return {evaluate::AsGenericExpr(chExpr)}; + + int kind; + VALUE value; +}; + +MaybeExpr ExprAnalyzer::Analyze(const parser::HollerithLiteralConstant &x) { + return AsMaybeExpr(common::SearchDynamicTypes( + ConstantTypeVisitor{ + defaults.defaultCharacterKind, x.v})); } // Common handling of parser::IntLiteralConstant and SignedIntLiteralConstant template -std::optional> IntLiteralConstant( - ExpressionAnalyzer &ea, const PARSED &x) { - auto kind{ea.Analyze(std::get>(x.t), - ea.defaultIntegerKind())}; +MaybeExpr IntLiteralConstant(ExprAnalyzer &ea, const PARSED &x) { + int kind{ea.Analyze(std::get>(x.t), + ea.defaults.defaultIntegerKind)}; auto value{std::get<0>(x.t)}; // std::(u)int64_t - ConstantHelper helper; - auto result{helper.SetKind(kind, std::move(value))}; + auto result{common::SearchDynamicTypes( + ConstantTypeVisitor{ + kind, static_cast(value)})}; if (!result.has_value()) { - ea.context().messages.Say("unsupported INTEGER(KIND=%ju)"_err_en_US, - static_cast(kind)); + ea.context.messages.Say("unsupported INTEGER(KIND=%u)"_err_en_US, kind); } - return result; + return AsMaybeExpr(std::move(result)); } -static std::optional> AnalyzeLiteral( - ExpressionAnalyzer &ea, const parser::IntLiteralConstant &x) { - return IntLiteralConstant(ea, x); +MaybeExpr ExprAnalyzer::Analyze(const parser::IntLiteralConstant &x) { + return IntLiteralConstant(*this, x); } -static std::optional> AnalyzeLiteral( - ExpressionAnalyzer &ea, const parser::SignedIntLiteralConstant &x) { - return IntLiteralConstant(ea, x); +MaybeExpr ExprAnalyzer::Analyze(const parser::SignedIntLiteralConstant &x) { + return IntLiteralConstant(*this, x); } -static std::optional AnalyzeLiteral( - ExpressionAnalyzer &ea, const parser::BOZLiteralConstant &x) { - const char *p{x.v.data()}; - std::uint64_t base{16}; - switch (*p++) { - case 'b': base = 2; break; - case 'o': base = 8; break; - case 'z': break; - case 'x': break; - default: CRASH_NO_CASE; - } - CHECK(*p == '"'); - auto value{evaluate::BOZLiteralConstant::ReadUnsigned(++p, base)}; - if (*p != '"') { - ea.context().messages.Say( - "invalid digit ('%c') in BOZ literal %s"_err_en_US, *p, x.v.data()); - return std::nullopt; - } - if (value.overflow) { - ea.context().messages.Say("BOZ literal %s too large"_err_en_US, x.v.data()); - return std::nullopt; - } - return {value.value}; -} - -template -std::optional> ReadRealLiteral( - parser::CharBlock source, evaluate::FoldingContext &context) { +template +Constant ReadRealLiteral( + parser::CharBlock source, FoldingContext &context) { const char *p{source.begin()}; - using RealType = Type; - auto valWithFlags{evaluate::Scalar::Read(p, context.rounding)}; + auto valWithFlags{Scalar::Read(p, context.rounding)}; CHECK(p == source.end()); - evaluate::RealFlagWarnings( - context, valWithFlags.flags, "conversion of REAL literal"); + RealFlagWarnings(context, valWithFlags.flags, "conversion of REAL literal"); auto value{valWithFlags.value}; if (context.flushDenormalsToZero) { value = value.FlushDenormalToZero(); } - return {evaluate::AsCategoryExpr( - Expr{evaluate::Constant{value}})}; + return {value}; } -// TODO pmk: make like ConstantHelper above, clean both up -struct RealHelper { - RealHelper(parser::CharBlock lit, evaluate::FoldingContext &ctx) - : literal{lit}, context{ctx} {} - - using Types = evaluate::CategoryTypes; - template void SetKindTraverser(int kind) { - if constexpr (J < std::tuple_size_v) { - using Ty = std::tuple_element_t; - if (kind == Ty::kind) { - result = ReadRealLiteral(literal, context); - } else { - SetKindTraverser(kind); - } +// TODO: can this definition appear in the function belowe? +struct RealTypeVisitor { + using Result = std::optional>; + static constexpr std::size_t Types{std::tuple_size_v}; + + RealTypeVisitor(int k, parser::CharBlock lit, FoldingContext &ctx) + : kind{k}, literal{lit}, context{ctx} {} + + template Result Test() { + using Ty = std::tuple_element_t; + if (kind == Ty::kind) { + return {AsCategoryExpr(AsExpr(ReadRealLiteral(literal, context)))}; } + return std::nullopt; } - void SetKind(int kind) { SetKindTraverser<0>(kind); } + int kind; parser::CharBlock literal; - evaluate::FoldingContext &context; - std::optional> result; + FoldingContext &context; }; -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 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. - typename ExpressionAnalyzer::KindParam defaultKind{ea.defaultRealKind()}; +MaybeExpr ExprAnalyzer::Analyze(const parser::RealLiteralConstant &x) { + // Use a local message context around the real literal for better + // provenance on any messages. + parser::ContextualMessages ctxMsgs{x.real.source, context.messages}; + FoldingContext localFoldingContext{ctxMsgs, context}; + // If a kind parameter appears, it defines the kind of the literal and any + // letter used in an exponent part (e.g., the 'E' in "6.02214E+23") + // should agree. In the absence of an explicit kind parameter, any exponent + // letter determines the kind. Otherwise, defaults apply. + int defaultKind{defaults.defaultRealKind}; const char *end{x.real.source.end()}; + std::optional letterKind; for (const char *p{x.real.source.begin()}; p < end; ++p) { if (parser::IsLetter(*p)) { switch (*p) { - case 'e': defaultKind = 4; break; - case 'd': defaultKind = 8; break; - case 'q': defaultKind = 16; break; + case 'e': letterKind = 4; break; + case 'd': letterKind = 8; break; + case 'q': letterKind = 16; break; default: ctxMsgs.Say("unknown exponent letter '%c'"_err_en_US, *p); } break; } } - auto kind{ea.Analyze(x.kind, defaultKind)}; - RealHelper helper{x.real.source, localFoldingContext}; - helper.SetKind(kind); - if (!helper.result.has_value()) { - ctxMsgs.Say("unsupported REAL(KIND=%ju)"_err_en_US, - static_cast(kind)); + if (letterKind.has_value()) { + defaultKind = *letterKind; + } + auto kind{Analyze(x.kind, defaultKind)}; + if (letterKind.has_value() && kind != *letterKind) { + ctxMsgs.Say( + "explicit kind parameter on real constant disagrees with exponent letter"_en_US); } - return helper.result; + auto result{common::SearchDynamicTypes( + RealTypeVisitor{kind, x.real.source, context})}; + if (!result.has_value()) { + ctxMsgs.Say("unsupported REAL(KIND=%u)"_err_en_US, kind); + } + return AsMaybeExpr(std::move(result)); } -static std::optional> AnalyzeLiteral( - ExpressionAnalyzer &ea, const parser::SignedRealLiteralConstant &x) { - if (auto result{ - AnalyzeLiteral(ea, std::get(x.t))}) { +MaybeExpr ExprAnalyzer::Analyze(const parser::SignedRealLiteralConstant &x) { + if (MaybeExpr result{Analyze(std::get(x.t))}) { if (auto sign{std::get>(x.t)}) { if (sign == parser::Sign::Negative) { - return {-std::move(*result)}; + return {AsGenericExpr(-*common::GetIf>(result->u))}; } } return result; @@ -313,312 +308,143 @@ static std::optional> AnalyzeLiteral( return std::nullopt; } -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Name &n) { - CHECK(n.symbol != nullptr); - auto *details{n.symbol->detailsIf()}; - if (details == nullptr || !n.symbol->attrs().test(Attr::PARAMETER)) { - ea.context().messages.Say( - "name (%s) is not a defined constant"_err_en_US, n.ToString().data()); - return std::nullopt; - } - // TODO: enumerators, do they have the PARAMETER attribute? - return std::nullopt; // TODO parameters and enumerators -} - -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::NamedConstant &n) { - return AnalyzeHelper(ea, n.v); -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::ComplexPart &x) { - return std::visit(common::visitors{[&](const parser::NamedConstant &n) { - return AnalyzeHelper(ea, n); - }, - [&](const auto &literal) { - return PackageGeneric(AnalyzeLiteral(ea, literal)); - }}, - x.u); +MaybeExpr ExprAnalyzer::Analyze(const parser::ComplexPart &x) { + return AnalyzeHelper(*this, x.u); } // Per F'2018 R718, if both components are INTEGER, they are both converted // to default REAL and the result is default COMPLEX. Otherwise, the -// kind of the result is the kind of largest REAL component, and the other -// component is converted if necessary its type. -static std::optional> AnalyzeLiteral( - ExpressionAnalyzer &ea, const parser::ComplexLiteralConstant &z) { - const parser::ComplexPart &re{std::get<0>(z.t)}, &im{std::get<1>(z.t)}; - return ea.ConstructComplex(AnalyzeHelper(ea, re), AnalyzeHelper(ea, im)); -} - -static std::optional> AnalyzeLiteral( - ExpressionAnalyzer &ea, const parser::HollerithLiteralConstant &x) { - Expr expr{x.v}; - return {Expr{expr}}; -} - -static std::optional> AnalyzeLiteral( - ExpressionAnalyzer &ea, const parser::LogicalLiteralConstant &x) { - auto kind{ea.Analyze(std::get>(x.t), - ea.defaultLogicalKind())}; - bool value{std::get(x.t)}; - ConstantHelper helper; - auto result{helper.SetKind(kind, std::move(value))}; - if (!result.has_value()) { - ea.context().messages.Say("unsupported LOGICAL(KIND=%ju)"_err_en_US, - static_cast(kind)); +// kind of the result is the kind of most precise REAL component, and the other +// component is converted if necessary to its type. +std::optional> ExprAnalyzer::ConstructComplex( + MaybeExpr &&real, MaybeExpr &&imaginary) { + if (auto converted{ConvertRealOperands( + context.messages, std::move(real), std::move(imaginary))}) { + return {std::visit( + [](auto &&pair) -> std::optional> { + using realType = ResultType; + using zType = SameKind; + auto cmplx{ComplexConstructor{ + std::move(pair[0]), std::move(pair[1])}}; + return {AsCategoryExpr(AsExpr(std::move(cmplx)))}; + }, + std::move(*converted))}; } - return result; -} - -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::LiteralConstant &x) { - return std::visit( - [&](const auto &c) { return PackageGeneric(AnalyzeLiteral(ea, c)); }, - x.u); -} - -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::ArrayConstructor &x) { - // TODO return std::nullopt; } -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::StructureConstructor &x) { - // TODO - return std::nullopt; +MaybeExpr ExprAnalyzer::Analyze(const parser::ComplexLiteralConstant &z) { + return AsMaybeExpr( + ConstructComplex(Analyze(std::get<0>(z.t)), Analyze(std::get<1>(z.t)))); } -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::TypeParamInquiry &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::FunctionReference &x) { - // TODO - return std::nullopt; +MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::ComplexConstructor &x) { + return AsMaybeExpr( + ConstructComplex(Analyze(*std::get<0>(x.t)), Analyze(*std::get<1>(x.t)))); } -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Expr::Parentheses &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Expr::UnaryPlus &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::Negate &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::NOT &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Expr::PercentLoc &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Expr::DefinedUnary &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::Power &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Expr::Multiply &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::Divide &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::Add &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Expr::Subtract &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::Concat &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::LT &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::LE &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::EQ &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::NE &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::GE &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::GT &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::AND &x) { - // TODO - return std::nullopt; -} - -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::OR &x) { - // TODO - return std::nullopt; +MaybeExpr ExprAnalyzer::Analyze(const parser::BOZLiteralConstant &x) { + const char *p{x.v.data()}; + std::uint64_t base{16}; + switch (*p++) { + case 'b': base = 2; break; + case 'o': base = 8; break; + case 'z': break; + case 'x': break; + default: CRASH_NO_CASE; + } + CHECK(*p == '"'); + auto value{BOZLiteralConstant::ReadUnsigned(++p, base)}; + if (*p != '"') { + context.messages.Say( + "invalid digit ('%c') in BOZ literal %s"_err_en_US, *p, x.v.data()); + return std::nullopt; + } + if (value.overflow) { + context.messages.Say("BOZ literal %s too large"_err_en_US, x.v.data()); + return std::nullopt; + } + return {AsGenericExpr(value.value)}; } -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::EQV &x) { - // TODO - return std::nullopt; +MaybeExpr ExprAnalyzer::Analyze(const parser::CharLiteralConstant &x) { + int kind{Analyze(std::get>(x.t), 1)}; + auto value{std::get(x.t)}; + auto result{common::SearchDynamicTypes( + ConstantTypeVisitor{ + kind, std::move(value)})}; + if (!result.has_value()) { + context.messages.Say("unsupported CHARACTER(KIND=%u)"_err_en_US, kind); + } + return AsMaybeExpr(std::move(result)); } -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::NEQV &x) { - // TODO - return std::nullopt; +MaybeExpr ExprAnalyzer::Analyze(const parser::LogicalLiteralConstant &x) { + auto kind{Analyze(std::get>(x.t), + defaults.defaultLogicalKind)}; + bool value{std::get(x.t)}; + auto result{common::SearchDynamicTypes( + ConstantTypeVisitor{ + kind, std::move(value)})}; + if (!result.has_value()) { + context.messages.Say("unsupported LOGICAL(KIND=%u)"_err_en_US, kind); + } + return AsMaybeExpr(std::move(result)); } -template<> -MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::XOR &x) { - // TODO - return std::nullopt; +MaybeExpr ExprAnalyzer::Analyze(const parser::Name &n) { + if (n.symbol != nullptr) { + auto *details{n.symbol->detailsIf()}; + if (details == nullptr || + !n.symbol->attrs().test(semantics::Attr::PARAMETER)) { + context.messages.Say( + "name (%s) is not a defined constant"_err_en_US, n.ToString().data()); + return std::nullopt; + } + // TODO: enumerators, do they have the PARAMETER attribute? + } + return std::nullopt; // TODO parameters and enumerators } -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Expr::DefinedBinary &x) { - // TODO +MaybeExpr ExprAnalyzer::Analyze(const parser::NamedConstant &n) { + return Analyze(n.v); +} + +MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Parentheses &x) { + if (MaybeExpr operand{AnalyzeHelper(*this, *x.v)}) { + return std::visit( + common::visitors{ + [&](BOZLiteralConstant &&boz) { + return operand; // ignore parentheses around typeless + }, + [](auto &&catExpr) { + return std::visit( + [](auto &&expr) -> MaybeExpr { + using Ty = ResultType; + if constexpr (common::HasMember, + decltype(expr.u)>) { + return {AsGenericExpr( + AsExpr(Parentheses{std::move(expr)}))}; + } + // TODO: support Parentheses in all Expr specializations + return std::nullopt; + }, + std::move(catExpr.u)); + }}, + std::move(operand->u)); + } return std::nullopt; } -template<> -MaybeExpr AnalyzeHelper( - ExpressionAnalyzer &ea, const parser::Expr::ComplexConstructor &x) { - return PackageGeneric(ea.ConstructComplex( - ea.Analyze(*std::get<0>(x.t)), ea.Analyze(*std::get<1>(x.t)))); -} +// TODO: continue here with other parse tree node types -MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &x) { - return std::visit(common::visitors{[&](const parser::LiteralConstant &c) { - return AnalyzeHelper(*this, c); - }, - // TODO: remaining cases - [&](const auto &) { return MaybeExpr{}; }}, - x.u); -} +} // namespace Fortran::evaluate -ExpressionAnalyzer::KindParam ExpressionAnalyzer::Analyze( - const std::optional &kindParam, KindParam defaultKind, - KindParam kanjiKind) { - if (!kindParam.has_value()) { - return defaultKind; - } - return std::visit( - common::visitors{ - [](std::uint64_t k) { return static_cast(k); }, - [&](const parser::Scalar< - parser::Integer>> &n) { - if (MaybeIntExpr ie{AnalyzeHelper(*this, n)}) { - return *ie->ScalarValue()->ToInt64(); - } - context_.messages.Say( - "KIND type parameter must be a scalar integer constant"_err_en_US); - return defaultKind; - }, - [&](parser::KindParam::Kanji) { - if (kanjiKind >= 0) { - return kanjiKind; - } - context_.messages.Say("Kanji not allowed here"_err_en_US); - return defaultKind; - }}, - kindParam->u); -} +namespace Fortran::semantics { -std::optional> ExpressionAnalyzer::ConstructComplex( - MaybeExpr &&real, MaybeExpr &&imaginary) { - if (auto converted{evaluate::ConvertRealOperands( - context_.messages, std::move(real), std::move(imaginary))}) { - return {std::visit( - [](auto &&pair) -> std::optional> { - using realType = evaluate::ResultType; - using zType = evaluate::SameKind; - auto cmplx{evaluate::ComplexConstructor{ - std::move(pair[0]), std::move(pair[1])}}; - return {evaluate::AsCategoryExpr(evaluate::AsExpr(std::move(cmplx)))}; - }, - *converted)}; - } - return std::nullopt; +MaybeExpr AnalyzeExpr(evaluate::FoldingContext &context, + const IntrinsicTypeDefaultKinds &defaults, const parser::Expr &expr) { + return evaluate::ExprAnalyzer{context, defaults}.Analyze(expr); } } // namespace Fortran::semantics diff --git a/flang/lib/semantics/expression.h b/flang/lib/semantics/expression.h index 43fc5f4..4ed7ab7 100644 --- a/flang/lib/semantics/expression.h +++ b/flang/lib/semantics/expression.h @@ -16,6 +16,7 @@ #define FORTRAN_SEMANTICS_EXPRESSION_H_ #include "../evaluate/expression.h" +#include "../evaluate/type.h" #include "../parser/message.h" #include "../parser/parse-tree.h" #include @@ -25,32 +26,15 @@ namespace Fortran::semantics { using MaybeExpr = std::optional>; -class ExpressionAnalyzer { -public: - using KindParam = std::int64_t; - - ExpressionAnalyzer(evaluate::FoldingContext &c, KindParam dIK) - : context_{c}, defaultIntegerKind_{dIK} {} - - evaluate::FoldingContext &context() { return context_; } - KindParam defaultIntegerKind() const { return defaultIntegerKind_; } - KindParam defaultRealKind() const { return defaultRealKind_; } - KindParam defaultLogicalKind() const { return defaultLogicalKind_; } - - // Performs semantic checking on an expression. If successful, - // returns its typed expression representation. - MaybeExpr Analyze(const parser::Expr &); - KindParam Analyze(const std::optional &, - KindParam defaultKind, KindParam kanjiKind = -1 /* not allowed here */); +struct IntrinsicTypeDefaultKinds { + int defaultIntegerKind{evaluate::DefaultInteger::kind}; + int defaultRealKind{evaluate::DefaultReal::kind}; + int defaultCharacterKind{evaluate::DefaultCharacter::kind}; + int defaultLogicalKind{evaluate::DefaultLogical::kind}; +}; - std::optional> ConstructComplex( - MaybeExpr &&real, MaybeExpr &&imaginary); +MaybeExpr AnalyzeExpr(evaluate::FoldingContext &, + const IntrinsicTypeDefaultKinds &, const parser::Expr &); -private: - evaluate::FoldingContext context_; - KindParam defaultIntegerKind_{4}; - KindParam defaultRealKind_{defaultIntegerKind_}; - KindParam defaultLogicalKind_{defaultIntegerKind_}; -}; } // namespace Fortran::semantics #endif // FORTRAN_SEMANTICS_EXPRESSION_H_ -- 2.7.4