From: peter klausler Date: Tue, 28 Aug 2018 22:15:18 +0000 (-0700) Subject: [flang] checkpoint once g++ can build again X-Git-Tag: llvmorg-12-init~9537^2~2181 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b1d441749cad7e8b054018b7239e086715290b06;p=platform%2Fupstream%2Fllvm.git [flang] checkpoint once g++ can build again Original-commit: flang-compiler/f18@1c09641a6daf04909561cc8d5ff7b7c223e961c3 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 caebc4e..552ddd6 100644 --- a/flang/lib/common/idioms.h +++ b/flang/lib/common/idioms.h @@ -104,7 +104,7 @@ template visitors(LAMBDAS... x)->visitors; return false; \ } \ } \ - template constexpr bool T { class_trait_ns_##T::trait_value() } + template constexpr bool T { class_trait_ns_##T::trait_value() }; // Define enum class NAME with the given enumerators, a static // function EnumToString() that maps enumerators to std::string, diff --git a/flang/lib/common/template.h b/flang/lib/common/template.h index fa2f77a..8a36fe5 100644 --- a/flang/lib/common/template.h +++ b/flang/lib/common/template.h @@ -106,8 +106,8 @@ template using CombineTuples = typename CombineTuplesHelper::type; // CombineVariants takes a list of std::variant<> instantiations and constructs -// a new instantiation that holds all of their alternatives, which probably -// should be distinct. +// a new instantiation that holds all of their alternatives, which must be +// pairwise distinct. template struct VariantToTupleHelper; template struct VariantToTupleHelper> { using type = std::tuple; @@ -115,8 +115,22 @@ template struct VariantToTupleHelper> { template using VariantToTuple = typename VariantToTupleHelper::type; +template struct AreTypesDistinctHelper { + static constexpr bool value() { + if constexpr (std::is_same_v) { + return false; + } + if constexpr (sizeof...(REST) > 0) { + return AreTypesDistinctHelper::value() && AreTypesDistinctHelper::value(); + } + return true; + } +}; +template constexpr bool AreTypesDistinct{AreTypesDistinctHelper::value()}; + template struct TupleToVariantHelper; template struct TupleToVariantHelper> { + static_assert(AreTypesDistinct || !"TupleToVariant: types are not pairwise distinct"); using type = std::variant; }; template @@ -128,6 +142,9 @@ template struct CombineVariantsHelper { template using CombineVariants = typename CombineVariantsHelper::type; +template +using SquashVariantOfVariants = OverMembers; + // Given a type function, apply it to each of the types in a tuple or variant, // and collect the results in another tuple or variant. template class, template class, typename...> diff --git a/flang/lib/evaluate/common.h b/flang/lib/evaluate/common.h index 0fe6ba0..a7afead 100644 --- a/flang/lib/evaluate/common.h +++ b/flang/lib/evaluate/common.h @@ -127,8 +127,8 @@ using HostUnsignedInt = template using CopyableIndirection = common::Indirection; // Classes that support a Fold(FoldingContext &) member function have the -// FoldableTrait set. -CLASS_TRAIT(FoldableTrait); +// IsFoldableTrait. +CLASS_TRAIT(IsFoldableTrait) struct FoldingContext { explicit FoldingContext(parser::ContextualMessages &m, Rounding round = defaultRounding, bool flush = false) diff --git a/flang/lib/evaluate/expression.cc b/flang/lib/evaluate/expression.cc index e5c57bc..debbd51 100644 --- a/flang/lib/evaluate/expression.cc +++ b/flang/lib/evaluate/expression.cc @@ -32,235 +32,128 @@ namespace Fortran::evaluate { template auto Operation::Fold(FoldingContext &context) - -> std::optional> { + -> std::optional> { auto c0{operand<0>().Fold(context)}; if constexpr (operands() == 1) { if (c0.has_value()) { - return derived().FoldScalar(context, *c0); + if (auto scalar{derived().FoldScalar(context, c0->value)}) { + return {Constant{std::move(*scalar)}}; + } } } else { auto c1{operand<1>().Fold(context)}; if (c0.has_value() && c1.has_value()) { - return derived().FoldScalar(context, *c0, *c1); + if (auto scalar{derived().FoldScalar(context, c0->value, c1->value)}) { + return {Constant{std::move(*scalar)}}; + } } } return std::nullopt; } -template -auto Expr>::Fold(FoldingContext &context) - -> std::optional> { - if (auto c{ScalarValue()}) { - return c; - } - return std::visit( - [&](auto &x) -> std::optional> { - using Ty = std::decay_t; - if constexpr (evaluate::FoldableTrait) { - if (auto c{x.Fold(context)}) { - if constexpr (std::is_same_v>) { - // Preserve parentheses around constants. - u_ = Parentheses{Expr{*c}}; - } else { - u_ = *c; - } - return c; +template +auto ExpressionBase::Fold(FoldingContext &context) + -> std::optional> { + using Const = Constant; + if constexpr (Result::isSpecificType) { + // Folding an expression of known type category and kind. + return std::visit( + [&](auto &x) -> std::optional { + using Thing = std::decay_t; + if constexpr (IsConstantTrait) { + return {x}; } - } - return std::nullopt; - }, - u_); -} - -template -auto Expr>::Fold(FoldingContext &context) - -> std::optional> { - if (auto c{ScalarValue()}) { - return c; - } - return std::visit( - [&](auto &x) -> std::optional> { - using Ty = std::decay_t; - if constexpr (evaluate::FoldableTrait) { - if (auto c{x.Fold(context)}) { - if (context.flushDenormalsToZero) { - *c = c->FlushDenormalToZero(); + if constexpr (IsFoldableTrait) { + if (auto c{x.Fold(context)}) { + static constexpr TypeCategory category{Result::category}; + if constexpr (category == TypeCategory::Real || + category == TypeCategory::Complex) { + if (context.flushDenormalsToZero) { + c->value = c->value.FlushDenormalToZero(); + } + } else if constexpr (category == TypeCategory::Logical) { + // Folding may have produced a constant of some + // dissimilar LOGICAL kind. + bool truth{c->value.IsTrue()}; + derived() = truth; + return {Const{truth}}; + } + if constexpr (std::is_same_v, Thing>) { + // Preserve parentheses around constants. + derived() = Thing{Derived{*c}}; + } else { + derived() = *c; + } + return {Const{c->value}}; } - if constexpr (std::is_same_v>) { - // Preserve parentheses around constants. - u_ = Parentheses{Expr{*c}}; - } else { - u_ = *c; + } + return std::nullopt; + }, + derived().u); + } else { + // Folding a generic expression into a generic constant. + return std::visit( + [&](auto &x) -> std::optional { + if constexpr (IsFoldableTrait>) { + if (auto c{x.Fold(context)}) { + if constexpr (ResultType::isSpecificType) { + return {Const{c->value}}; + } else { + // pmk: this is ugly + return {Const{common::MoveVariant(c->value.u)}}; + } } - return c; } - } - return std::nullopt; - }, - u_); + return std::nullopt; + }, + derived().u); + } } -template -auto Expr>::Fold(FoldingContext &context) - -> std::optional> { - if (auto c{ScalarValue()}) { - return c; - } +// FoldScalar + +template +auto Convert::FoldScalar(FoldingContext &context, + const Scalar &x) -> std::optional> { return std::visit( - [&](auto &x) -> std::optional> { - using Ty = std::decay_t; - if constexpr (evaluate::FoldableTrait) { - if (auto c{x.Fold(context)}) { - if (context.flushDenormalsToZero) { - *c = c->FlushDenormalToZero(); + [&](const auto &c) -> std::optional> { + if constexpr (Result::category == TypeCategory::Integer) { + if constexpr (Operand::category == TypeCategory::Integer) { + auto converted{Scalar::ConvertSigned(c)}; + if (converted.overflow) { + context.messages.Say( + "INTEGER to INTEGER conversion overflowed"_en_US); + } else { + return {std::move(converted.value)}; } - if constexpr (std::is_same_v>) { - // Preserve parentheses around constants. - u_ = Parentheses{Expr{*c}}; + } else if constexpr (Operand::category == TypeCategory::Real) { + auto converted{c.template ToInteger>()}; + if (converted.flags.test(RealFlag::InvalidArgument)) { + context.messages.Say( + "REAL to INTEGER conversion: invalid argument"_en_US); + } else if (converted.flags.test(RealFlag::Overflow)) { + context.messages.Say( + "REAL to INTEGER conversion overflowed"_en_US); } else { - u_ = *c; + return {std::move(converted.value)}; } - return c; } - } - return std::nullopt; - }, - u_); -} - -template -auto Expr>::Fold(FoldingContext &context) - -> std::optional> { - if (auto c{ScalarValue()}) { - return c; - } - return std::visit( - [&](auto &x) -> std::optional> { - using Ty = std::decay_t; - if constexpr (evaluate::FoldableTrait) { - if (auto c{x.Fold(context)}) { - u_ = *c; - return c; + } else if constexpr (Result::category == TypeCategory::Real) { + if constexpr (Operand::category == TypeCategory::Integer) { + auto converted{Scalar::FromInteger(c)}; + RealFlagWarnings( + context, converted.flags, "INTEGER to REAL conversion"); + return {std::move(converted.value)}; + } else if constexpr (Operand::category == TypeCategory::Real) { + auto converted{Scalar::Convert(c)}; + RealFlagWarnings( + context, converted.flags, "REAL to REAL conversion"); + return {std::move(converted.value)}; } } return std::nullopt; }, - u_); -} - -template -auto Expr>::Fold(FoldingContext &context) - -> std::optional> { - if (auto c{ScalarValue()}) { - return c; - } - return std::visit( - [&](auto &x) -> std::optional> { - using Ty = std::decay_t; - if constexpr (evaluate::FoldableTrait) { - if (auto c{x.Fold(context)}) { - Scalar result{c->IsTrue()}; - u_ = result; - return {result}; - } - } - return std::nullopt; - }, - u_); -} - -template -auto Expr>::Fold(FoldingContext &context) - -> std::optional> { - return std::visit( - [&](auto &x) -> std::optional> { - if (auto c{x.Fold(context)}) { - return {Scalar{std::move(*c)}}; - } - return std::nullopt; - }, - u.u); -} - -auto Expr::Fold(FoldingContext &context) - -> std::optional> { - return std::visit( - common::visitors{ - [](BOZLiteralConstant &) -> std::optional> { - return std::nullopt; - }, - [&](auto &x) -> std::optional> { - if (auto c{x.Fold(context)}) { - return {common::MoveVariant>(std::move(c->u))}; - } - return std::nullopt; - }}, - u); -} - -// FoldScalar - -template -auto Convert::FoldScalar(FoldingContext &context, - const Scalar &c) -> std::optional> { - if constexpr (std::is_same_v) { - return {c}; - } else if constexpr (std::is_same_v) { - using Generic = SomeKind; - if constexpr (std::is_same_v) { - return {Scalar{c}}; - } else { - return {Scalar{Generic{c}}}; - } - } else if constexpr (std::is_same_v) { - return std::visit( - [&](const auto &x) -> std::optional> { - using Ty = std::decay_t; - return Convert::FoldScalar(context, x); - }, - c.u.u); - } else if constexpr (std::is_same_v>) { - if constexpr (Result::category == Operand::category) { - return {Scalar{c}}; - } - } else if constexpr (std::is_same_v>) { - return std::visit( - [&](const auto &x) -> std::optional> { - using Ty = TypeOf>; - return Convert::FoldScalar(context, x); - }, - c.u); - } else if constexpr (Result::category == TypeCategory::Integer) { - if constexpr (Operand::category == TypeCategory::Integer) { - auto converted{Scalar::ConvertSigned(c)}; - if (converted.overflow) { - context.messages.Say("INTEGER to INTEGER conversion overflowed"_en_US); - } else { - return {std::move(converted.value)}; - } - } else if constexpr (Operand::category == TypeCategory::Real) { - auto converted{c.template ToInteger>()}; - if (converted.flags.test(RealFlag::InvalidArgument)) { - context.messages.Say( - "REAL to INTEGER conversion: invalid argument"_en_US); - } else if (converted.flags.test(RealFlag::Overflow)) { - context.messages.Say("REAL to INTEGER conversion overflowed"_en_US); - } else { - return {std::move(converted.value)}; - } - } - } else if constexpr (Result::category == TypeCategory::Real) { - if constexpr (Operand::category == TypeCategory::Integer) { - auto converted{Scalar::FromInteger(c)}; - RealFlagWarnings(context, converted.flags, "INTEGER to REAL conversion"); - return {std::move(converted.value)}; - } else if constexpr (Operand::category == TypeCategory::Real) { - auto converted{Scalar::Convert(c)}; - RealFlagWarnings(context, converted.flags, "REAL to REAL conversion"); - return {std::move(converted.value)}; - } - } - return std::nullopt; + x.u); } template @@ -388,9 +281,9 @@ auto Power::FoldScalar(FoldingContext &context, const Scalar &x, return std::nullopt; } -template -auto RealToIntPower::FoldScalar(FoldingContext &context, - const Scalar &x, const Scalar &y) +template +auto RealToIntPower::FoldScalar(FoldingContext &context, + const Scalar &x, const Scalar &y) -> std::optional> { return std::visit( [&](const auto &pow) -> std::optional> { @@ -517,6 +410,11 @@ template std::string Relational::infix() const { return "."s + EnumToString(opr) + '.'; } +std::ostream &Relational::Dump(std::ostream &o) const { + std::visit([&](const auto &rel) { rel.Dump(o); }, u); + return o; +} + template const char *LogicalOperation::infix() const { const char *result{nullptr}; switch (logicalOperator) { @@ -528,107 +426,48 @@ template const char *LogicalOperation::infix() const { return result; } -template -std::ostream &DumpExpr(std::ostream &o, const std::variant &u) { - std::visit(common::visitors{[&](const BOZLiteralConstant &x) { - o << "Z'" << x.Hexadecimal() << "'"; - }, - [&](const auto &x) { x.Dump(o); }}, - u); - return o; -} - -template -std::ostream &Expr>::Dump(std::ostream &o) const { - return DumpExpr(o, u.u); -} - -std::ostream &AnyRelational::Dump(std::ostream &o) const { - return DumpExpr(o, u); -} - -std::ostream &Expr::Dump(std::ostream &o) const { - return DumpExpr(o, u); -} - -template -std::ostream &Expr>::Dump( - std::ostream &o) const { - std::visit(common::visitors{[&](const Scalar &n) { - o << n.SignedDecimal() << '_' << KIND; - }, - [&](const CopyableIndirection &d) { d->Dump(o); }, - [&](const CopyableIndirection &d) { d->Dump(o); }, - [&](const auto &x) { x.Dump(o); }}, - u_); - return o; -} - -template -std::ostream &Expr>::Dump( - std::ostream &o) const { - std::visit(common::visitors{[&](const Scalar &n) { - o << n.DumpHexadecimal(); - }, - [&](const CopyableIndirection &d) { d->Dump(o); }, - [&](const CopyableIndirection &d) { d->Dump(o); }, - [&](const CopyableIndirection &d) { d->Dump(o); }, - [&](const auto &x) { x.Dump(o); }}, - u_); - return o; -} - -template -std::ostream &Expr>::Dump( - std::ostream &o) const { - std::visit(common::visitors{[&](const Scalar &n) { - o << n.DumpHexadecimal(); - }, - [&](const CopyableIndirection &d) { d->Dump(o); }, - [&](const CopyableIndirection &d) { d->Dump(o); }, - [&](const auto &x) { x.Dump(o); }}, - u_); - return o; -} - -template -std::ostream &Expr>::Dump( - std::ostream &o) const { - std::visit(common::visitors{[&](const Scalar &s) { - o << KIND << '_' - << parser::QuoteCharacterLiteral(s); - }, - // [&](const Parentheses &p) { p.Dump(o); }, - [&](const Concat &c) { c.Dump(o); }, - [&](const Extremum &mm) { mm.Dump(o); }, - [&](const auto &ind) { ind->Dump(o); }}, - u_); - return o; +template std::ostream &Constant::Dump(std::ostream &o) const { + if constexpr (T::category == TypeCategory::Integer) { + return o << value.SignedDecimal() << '_' << Result::kind; + } else if constexpr (T::category == TypeCategory::Real || + T::category == TypeCategory::Complex) { + return o << value.DumpHexadecimal() << '_' << Result::kind; + } else if constexpr (T::category == TypeCategory::Character) { + return o << Result::kind << '_' << parser::QuoteCharacterLiteral(value); + } else if constexpr (T::category == TypeCategory::Logical) { + if (value.IsTrue()) { + o << ".TRUE."; + } else { + o << ".FALSE."; + } + return o << '_' << Result::kind; + } else { + return value.u.Dump(o); + } } -template -std::ostream &Expr>::Dump( - std::ostream &o) const { - std::visit(common::visitors{[&](const Scalar &tf) { - o << (tf.IsTrue() ? ".TRUE." : ".FALSE.") << '_' - << KIND; - }, - [&](const CopyableIndirection &d) { d->Dump(o); }, - [&](const CopyableIndirection &d) { d->Dump(o); }, - [&](const auto &x) { x.Dump(o); }}, - u_); +template +std::ostream &ExpressionBase::Dump(std::ostream &o) const { + std::visit( + common::visitors{[&](const BOZLiteralConstant &x) { + o << "Z'" << x.Hexadecimal() << "'"; + }, + [&](const DataReference &dr) { dr.reference->Dump(o); }, + [&](const FunctionReference &fr) { fr.reference->Dump(o); }, + [&](const CopyableIndirection &s) { s->Dump(o); }, + [&](const auto &x) { x.Dump(o); }}, + derived().u); return o; } -// LEN() template Expr Expr>::LEN() const { return std::visit( - common::visitors{[](const Scalar &c) { + common::visitors{[](const Constant &c) { // std::string::size_type isn't convertible to uint64_t // on Darwin return Expr{ - static_cast(c.size())}; + static_cast(c.value.size())}; }, [](const Concat &c) { return c.template operand<0>().LEN() + @@ -638,64 +477,32 @@ Expr Expr>::LEN() const { return Expr{Extremum{ c.template operand<0>().LEN(), c.template operand<1>().LEN()}}; }, - [](const CopyableIndirection &dr) { return dr->LEN(); }, + [](const DataReference &dr) { return dr.reference->LEN(); }, [](const CopyableIndirection &ss) { return ss->LEN(); }, - [](const CopyableIndirection &fr) { - return fr->proc().LEN(); - }}, - u_); -} - -// ScalarValue - -template -auto Expr>::ScalarValue() const -> std::optional> { - return std::visit( - [](const auto &x) -> std::optional> { - if (auto c{x.ScalarValue()}) { - return {Scalar{std::move(*c)}}; - } - return std::nullopt; - }, - u.u); -} - -auto Expr::ScalarValue() const -> std::optional> { - return std::visit( - common::visitors{ - [](const BOZLiteralConstant &) -> std::optional> { - return std::nullopt; - }, - [](const auto &x) -> std::optional> { - if (auto c{x.ScalarValue()}) { - return {common::MoveVariant>(std::move(c->u))}; - } - return std::nullopt; + [](const FunctionReference &fr) { + return fr.reference->proc().LEN(); }}, u); } -// Rank - -template int Expr>::Rank() const { - return std::visit([](const auto &x) { return x.Rank(); }, u.u); -} - -int Expr::Rank() const { - // Written thus, instead of common::visitors, to dodge a bug in G++ 7.2. +template +auto ExpressionBase::ScalarValue() const + -> std::optional> { + using Const = Scalar; return std::visit( - [](const auto &x) { - if constexpr (std::is_same_v, - BOZLiteralConstant>) { - return 1; - } else { - return x.Rank(); + [](const auto &x) -> std::optional { + using Ty = std::decay_t; + if constexpr (IsConstantTrait) { + return {Const{x.value}}; } + // TODO: Also succeed for a parenthesized constant + return std::nullopt; }, - u); + derived().u); } -// Template instantiations +// Template instantiations to resolve the "extern template" declarations +// in expression.h. template class Expr>; template class Expr>; @@ -713,6 +520,16 @@ template class Expr>; template class Expr>; template class Expr>; template class Expr>; // TODO others +template class Expr>; +template class Expr>; +template class Expr>; +template class Expr>; +template class Expr; +template class Expr; +template class Expr; +template class Expr; +template class Expr; +template class Expr; template struct Relational>; template struct Relational>; @@ -730,17 +547,33 @@ template struct Relational>; template struct Relational>; template struct Relational>; template struct Relational>; // TODO others +template struct Relational; + +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase>; +template struct ExpressionBase; +template struct ExpressionBase; +template struct ExpressionBase; +template struct ExpressionBase; +template struct ExpressionBase; +template struct ExpressionBase; -template class Expr>; -template class Expr>; -template class Expr>; -template class Expr>; - -template class Expr; -template class Expr; -template class Expr; -template class Expr; -template class Expr; - -template class Expr; } // namespace Fortran::evaluate diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h index 831cc0c..54b77c8 100644 --- a/flang/lib/evaluate/expression.h +++ b/flang/lib/evaluate/expression.h @@ -18,8 +18,7 @@ // Represent Fortran expressions in a type-safe manner. // Expressions are the sole owners of their constituents; i.e., there is no // context-independent hash table or sharing of common subexpressions. -// Both deep copy and move semantics are supported for expression construction -// and manipulation in place. +// Both deep copy and move semantics are supported for expression construction. #include "common.h" #include "type.h" @@ -30,51 +29,109 @@ #include "../lib/parser/message.h" #include #include +#include #include namespace Fortran::evaluate { using common::RelationalOperator; -// Expr represents an expression whose result is the Fortran type A, -// 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. +// Expressions are represented by specializations of Expr. +// Each of these specializations wraps a single data member "u" that +// is a std::variant<> discriminated union over the representational +// types of the constants, variables, operations, and other entities that +// can be valid expressions in that context: +// - Expr> is an expression whose result is of a +// specific intrinsic type category and kind, e.g. Type +// - Expr> is a union of Expr> for each +// kind type parameter value K in that intrinsic type category +// - Expr is a union of Expr> over the five +// intrinsic type categories of Fortran. template class Expr; +// Everything that can appear in, or as, a valid Fortran expression must be +// represented with an instance of some class containing a Result typedef that +// maps to some instantiation of Type, SomeKind, +// or SomeType. template using ResultType = typename std::decay_t::Result; -// Abstract Operation<> base class. The first type parameter is a "CRTP" +// Wraps a constant value in a class to make its type clear. +CLASS_TRAIT(IsConstantTrait) +template struct Constant { + using Result = T; + using Value = Scalar; // TODO rank > 0 + using IsConstantTrait = std::true_type; + CLASS_BOILERPLATE(Constant) + template Constant(const A &x) : value{x} {} + template + Constant(std::enable_if_t, A> &&x) + : value(std::move(x)) {} + std::ostream &Dump(std::ostream &) const; + Value value; +}; + +// Wrappers around data and function references so that their resolved +// types are clear. +template struct DataReference { + using Result = T; + CopyableIndirection reference; +}; + +template struct FunctionReference { + using Result = T; + CopyableIndirection reference; +}; + +// 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 +template class Operation { + using OperandTypes = std::tuple; + static_assert(RESULT::kind > 0 || !"bad result Type"); + public: using Derived = DERIVED; using Result = RESULT; - using OperandTypes = std::tuple; - using OperandTuple = std::tuple...>; + static constexpr auto operands() { return std::tuple_size_v; } template using Operand = std::tuple_element_t; - using FoldableTrait = std::true_type; + using IsFoldableTrait = std::true_type; - static_assert(Result::kind > 0); // Operations have specific Result types + // Unary operations wrap a single Expr with a CopyableIndirection. + // Binary operations wrap a tuple of CopyableIndirections to Exprs. +private: + using Container = + std::conditional_t>>, + std::tuple>...>>; +public: CLASS_BOILERPLATE(Operation) - Operation(const Expr &... x) : operand_{OperandTuple{x...}} {} - Operation(Expr &&... x) - : operand_{OperandTuple{std::forward>(x)...}} {} - - DERIVED &derived() { return *static_cast(this); } - const DERIVED &derived() const { return *static_cast(this); } - - static constexpr auto operands() { return std::tuple_size_v; } - template Expr> &operand() { return std::get(*operand_); } + Operation(const Expr &... x) : operand_{x...} {} + Operation(Expr &&... x) + : operand_{std::forward>(x)...} {} + + Derived &derived() { return *static_cast(this); } + const Derived &derived() const { return *static_cast(this); } + + template Expr> &operand() { + if constexpr (operands() == 1) { + static_assert(J == 0); + return *operand_; + } else { + return *std::get(operand_); + } + } template const Expr> &operand() const { - return std::get(*operand_); + if constexpr (operands() == 1) { + static_assert(J == 0); + return *operand_; + } else { + return *std::get(operand_); + } } std::ostream &Dump(std::ostream &) const; - std::optional> Fold(FoldingContext &); // TODO rank > 0 + std::optional> Fold(FoldingContext &); protected: // Overridable string functions for Dump() @@ -83,27 +140,27 @@ protected: static const char *suffix() { return ")"; } private: - CopyableIndirection operand_; + Container operand_; }; // Unary operations -template -struct Convert : public Operation, TO, FROM> { - using Base = Operation, TO, FROM>; +template +struct Convert : public Operation, TO, SomeKind> { + using Result = TO; + using Operand = SomeKind; + using Base = Operation; using Base::Base; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; static std::optional> FoldScalar( FoldingContext &, const Scalar &); }; template struct Parentheses : public Operation, A, A> { + using Result = A; + using Operand = A; using Base = Operation; using Base::Base; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; static std::optional> FoldScalar( FoldingContext &, const Scalar &x) { return {x}; @@ -111,10 +168,10 @@ struct Parentheses : public Operation, A, A> { }; template struct Negate : public Operation, A, A> { + using Result = A; + using Operand = A; using Base = Operation; using Base::Base; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; static std::optional> FoldScalar( FoldingContext &, const Scalar &); static const char *prefix() { return "(-"; } @@ -124,10 +181,9 @@ template struct ComplexComponent : public Operation, Type, Type> { - using Base = Operation, - Type>; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; + using Result = Type; + using Operand = Type; + using Base = Operation; CLASS_BOILERPLATE(ComplexComponent) ComplexComponent(bool isImaginary, const Expr &x) : Base{x}, isImaginaryPart{isImaginary} {} @@ -144,10 +200,9 @@ struct ComplexComponent template struct Not : public Operation, Type, Type> { - using Base = Operation, - Type>; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; + using Result = Type; + using Operand = Result; + using Base = Operation; using Base::Base; static std::optional> FoldScalar( FoldingContext &, const Scalar &); @@ -157,9 +212,9 @@ struct Not : public Operation, Type, // Binary operations template struct Add : public Operation, A, A, A> { + using Result = A; + using Operand = A; using Base = Operation; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; using Base::Base; static std::optional> FoldScalar( FoldingContext &, const Scalar &, const Scalar &); @@ -167,9 +222,9 @@ template struct Add : public Operation, A, A, A> { }; template struct Subtract : public Operation, A, A, A> { + using Result = A; + using Operand = A; using Base = Operation; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; using Base::Base; static std::optional> FoldScalar( FoldingContext &, const Scalar &, const Scalar &); @@ -177,9 +232,9 @@ template struct Subtract : public Operation, A, A, A> { }; template struct Multiply : public Operation, A, A, A> { + using Result = A; + using Operand = A; using Base = Operation; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; using Base::Base; static std::optional> FoldScalar( FoldingContext &, const Scalar &, const Scalar &); @@ -187,9 +242,9 @@ template struct Multiply : public Operation, A, A, A> { }; template struct Divide : public Operation, A, A, A> { + using Result = A; + using Operand = A; using Base = Operation; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; using Base::Base; static std::optional> FoldScalar( FoldingContext &, const Scalar &, const Scalar &); @@ -197,31 +252,31 @@ template struct Divide : public Operation, A, A, A> { }; template struct Power : public Operation, A, A, A> { + using Result = A; + using Operand = A; using Base = Operation; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; using Base::Base; static std::optional> FoldScalar( FoldingContext &, const Scalar &, const Scalar &); static constexpr const char *infix() { return "**"; } }; -template -struct RealToIntPower : public Operation, A, A, B> { - using Base = Operation; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; - using ExponentOperand = typename Base::template Operand<1>; +template +struct RealToIntPower : public Operation, A, A, SomeInteger> { + using Base = Operation; + using Result = A; + using BaseOperand = A; + using ExponentOperand = SomeInteger; using Base::Base; static std::optional> FoldScalar(FoldingContext &, - const Scalar &, const Scalar &); + const Scalar &, const Scalar &); static constexpr const char *infix() { return "**"; } }; template struct Extremum : public Operation, A, A, A> { + using Result = A; + using Operand = A; using Base = Operation; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; CLASS_BOILERPLATE(Extremum) Extremum(const Expr &x, const Expr &y, Ordering ord = Ordering::Greater) @@ -244,10 +299,9 @@ struct ComplexConstructor : public Operation, Type, Type, Type> { - using Base = Operation, - Type, Type>; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; + using Result = Type; + using Operand = Type; + using Base = Operation; using Base::Base; static std::optional> FoldScalar( FoldingContext &, const Scalar &, const Scalar &); @@ -258,10 +312,9 @@ struct Concat : public Operation, Type, Type, Type> { - using Base = Operation, - Type, Type>; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; + using Result = Type; + using Operand = Result; + using Base = Operation; using Base::Base; static std::optional> FoldScalar( FoldingContext &, const Scalar &, const Scalar &); @@ -274,10 +327,9 @@ template struct LogicalOperation : public Operation, Type, Type, Type> { - using Base = Operation, - Type, Type>; - using typename Base::Result; - using Operand = typename Base::template Operand<0>; + using Result = Type; + using Operand = Result; + using Base = Operation; CLASS_BOILERPLATE(LogicalOperation) LogicalOperation( const Expr &x, const Expr &y, LogicalOperator opr) @@ -294,133 +346,119 @@ struct LogicalOperation // Per-category expressions -template class Expr> { +// Common Expr<> behaviors +template struct ExpressionBase { + using Result = RESULT; + using Derived = Expr; + + Derived &derived() { return *static_cast(this); } + const Derived &derived() const { return *static_cast(this); } + + int Rank() const { return 0; } // TODO + + template Derived &operator=(const A &x) { + Derived &d{derived()}; + d.u = x; + return d; + } + + template + Derived &operator=(std::enable_if_t, A> &&x) { + Derived &d{derived()}; + d.u = std::move(x); + return d; + } + + std::ostream &Dump(std::ostream &) const; + std::optional> Fold(FoldingContext &c); + std::optional> ScalarValue() const; +}; + +template +class Expr> + : public ExpressionBase> { public: using Result = Type; - using FoldableTrait = std::true_type; + using IsFoldableTrait = std::true_type; // TODO: R916 type-param-inquiry CLASS_BOILERPLATE(Expr) - Expr(const Scalar &x) : u_{x} {} - Expr(std::int64_t n) : u_{Scalar{n}} {} - Expr(std::uint64_t n) : u_{Scalar{n}} {} - Expr(int n) : u_{Scalar{n}} {} - Expr(const Expr &x) : u_{Convert{x}} {} - Expr(Expr &&x) - : u_{Convert{std::move(x)}} {} - template - Expr(const Expr> &x) - : u_{Convert{Expr{x}}} {} - template - Expr(Expr> &&x) - : u_{Convert{Expr{std::move(x)}}} {} - Expr(const Expr &x) : u_{Convert{x}} {} - Expr(Expr &&x) : u_{Convert{std::move(x)}} {} - template - Expr(const Expr> &x) - : u_{Convert{Expr{x}}} {} - template - Expr(Expr> &&x) - : u_{Convert{Expr{std::move(x)}}} {} - template Expr(const A &x) : u_{x} {} + Expr(const Scalar &x) : u{Constant{x}} {} + Expr(std::int64_t n) : u{Constant{n}} {} + Expr(std::uint64_t n) : u{Constant{n}} {} + Expr(int n) : u{Constant{n}} {} + template Expr(const A &x) : u{x} {} template - Expr(std::enable_if_t, A> &&x) : u_(std::move(x)) {} - template Expr(CopyableIndirection &&x) : u_{std::move(x)} {} - - std::optional> ScalarValue() const { - // TODO: Also succeed when parenthesized constant - return common::GetIf>(u_); - } - std::ostream &Dump(std::ostream &) const; - std::optional> Fold(FoldingContext &c); - int Rank() const { return 1; } // TODO + Expr(std::enable_if_t, A> &&x) : u(std::move(x)) {} + Expr(const DataRef &x) : u{DataReference{x}} {} + Expr(const FunctionRef &x) : u{FunctionReference{x}} {} private: - std::variant, CopyableIndirection, - CopyableIndirection, Convert, - Convert, Parentheses, Negate, + using Conversions = std::variant, + Convert>; + using Operations = std::variant, Negate, Add, Subtract, Multiply, Divide, - Power, Extremum> - u_; + Power, Extremum>; + using Others = std::variant, DataReference, + FunctionReference>; + +public: + common::CombineVariants u; }; -template class Expr> { +template +class Expr> + : public ExpressionBase> { public: using Result = Type; - using FoldableTrait = std::true_type; - - // N.B. Real->Complex and Complex->Real conversions are done with CMPLX - // and part access operations (resp.). Conversions between kinds of - // Complex are done via decomposition to Real and reconstruction. + using IsFoldableTrait = std::true_type; CLASS_BOILERPLATE(Expr) - Expr(const Scalar &x) : u_{x} {} - Expr(const Expr &x) : u_{Convert{x}} {} - Expr(Expr &&x) - : u_{Convert{std::move(x)}} {} - template - Expr(const Expr> &x) - : u_{Convert{Expr{x}}} {} - template - Expr(Expr> &&x) - : u_{Convert{Expr{std::move(x)}}} {} - Expr(const Expr &x) : u_{Convert{x}} {} - Expr(Expr &&x) : u_{Convert{std::move(x)}} {} - template - Expr(const Expr> &x) - : u_{Convert{Expr{x}}} {} - template - Expr(Expr> &&x) - : u_{Convert{Expr{std::move(x)}}} {} - template Expr(const A &x) : u_{x} {} + Expr(const Scalar &x) : u{Constant{x}} {} + template Expr(const A &x) : u{x} {} template - Expr(std::enable_if_t, A> &&x) : u_{std::move(x)} {} - template Expr(CopyableIndirection &&x) : u_{std::move(x)} {} - - std::optional> ScalarValue() const { - // TODO: parenthesized constants too - return common::GetIf>(u_); - } - std::ostream &Dump(std::ostream &) const; - std::optional> Fold(FoldingContext &c); - int Rank() const { return 1; } // TODO + Expr(std::enable_if_t, A> &&x) : u{std::move(x)} {} + Expr(const DataRef &x) : u{DataReference{x}} {} + Expr(const FunctionRef &x) : u{FunctionReference{x}} {} private: - std::variant, CopyableIndirection, - CopyableIndirection, CopyableIndirection, - Convert, Convert, - ComplexComponent, Parentheses, Negate, Add, - Subtract, Multiply, Divide, Power, - RealToIntPower, Extremum> - u_; + // N.B. Real->Complex and Complex->Real conversions are done with CMPLX + // and part access operations (resp.). Conversions between kinds of + // Complex are done via decomposition to Real and reconstruction. + using Conversions = std::variant, + Convert>; + using Operations = std::variant, Parentheses, + Negate, Add, Subtract, Multiply, + Divide, Power, RealToIntPower, Extremum>; + using Others = std::variant, DataReference, + FunctionReference>; + +public: + common::CombineVariants u; }; -template class Expr> { +template +class Expr> + : public ExpressionBase> { public: using Result = Type; - using FoldableTrait = std::true_type; + using IsFoldableTrait = std::true_type; CLASS_BOILERPLATE(Expr) - Expr(const Scalar &x) : u_{x} {} - template Expr(const A &x) : u_{x} {} + Expr(const Scalar &x) : u{Constant{x}} {} + template Expr(const A &x) : u{x} {} template - Expr(std::enable_if_t, A> &&x) : u_{std::move(x)} {} - template Expr(CopyableIndirection &&x) : u_{std::move(x)} {} - - std::optional> ScalarValue() const { - // TODO: parenthesized constants too - return common::GetIf>(u_); - } - std::ostream &Dump(std::ostream &) const; - std::optional> Fold(FoldingContext &c); - int Rank() const { return 1; } // TODO + Expr(std::enable_if_t, A> &&x) : u{std::move(x)} {} + Expr(const DataRef &x) : u{DataReference{x}} {} + Expr(const FunctionRef &x) : u{FunctionReference{x}} {} -private: - std::variant, CopyableIndirection, - CopyableIndirection, Parentheses, Negate, + using Operations = std::variant, Negate, Add, Subtract, Multiply, Divide, - Power, RealToIntPower, - ComplexConstructor> - u_; + Power, RealToIntPower, ComplexConstructor>; + using Others = std::variant, DataReference, + FunctionReference>; + +public: + common::CombineVariants u; }; extern template class Expr>; @@ -439,42 +477,38 @@ extern template class Expr>; extern template class Expr>; extern template class Expr>; -template class Expr> { +template +class Expr> + : public ExpressionBase> { public: using Result = Type; - using FoldableTrait = std::true_type; + using IsFoldableTrait = std::true_type; CLASS_BOILERPLATE(Expr) - Expr(const Scalar &x) : u_{x} {} - Expr(Scalar &&x) : u_{std::move(x)} {} - template Expr(const A &x) : u_{x} {} + Expr(const Scalar &x) : u{Constant{x}} {} + Expr(Scalar &&x) : u{Constant{std::move(x)}} {} + template Expr(const A &x) : u{x} {} template - Expr(std::enable_if_t, A> &&x) : u_{std::move(x)} {} - template Expr(CopyableIndirection &&x) : u_{std::move(x)} {} + Expr(std::enable_if_t, A> &&x) : u{std::move(x)} {} + Expr(const DataRef &x) : u{DataReference{x}} {} + Expr(const FunctionRef &x) : u{FunctionReference{x}} {} + template Expr(CopyableIndirection &&x) : u{std::move(x)} {} - std::optional> ScalarValue() const { - // TODO: parenthesized constants too - return common::GetIf>(u_); - } - std::ostream &Dump(std::ostream &) const; - std::optional> Fold(FoldingContext &c); - int Rank() const { return 1; } // TODO Expr LEN() const; -private: - std::variant, CopyableIndirection, - CopyableIndirection, CopyableIndirection, + std::variant, DataReference, + CopyableIndirection, FunctionReference, // TODO Parentheses, Concat, Extremum> - u_; + u; }; -// The Relation class template is a helper for constructing logical +// The Relational class template is a helper for constructing logical // expressions with polymorphism over the cross product of the possible // categories and kinds of comparable operands. // Fortran defines a numeric relation with distinct types or kinds as // undergoing the same operand conversions that occur with the addition // intrinsic operator first. Character relations must have the same kind. -// There are no relations between logicals. +// There are no relations between LOGICAL values. template struct Relational : public Operation, LogicalResult, A, A> { @@ -495,84 +529,57 @@ struct Relational : public Operation, LogicalResult, A, A> { RelationalOperator opr; }; -// A generic relation between two operands of the same kind in some intrinsic -// type category (except LOGICAL). -struct AnyRelational { +template<> struct Relational { using Result = LogicalResult; - template AnyRelational(const A &x) : u{x} {} + CLASS_BOILERPLATE(Relational) + template Relational(const A &x) : u(x) {} template - AnyRelational(std::enable_if_t, A> &&x) + Relational(std::enable_if_t, A> &&x) : u{std::move(x)} {} - std::optional> Fold(FoldingContext &); - std::ostream &Dump(std::ostream &) const; - + std::ostream &Dump(std::ostream &o) const; common::MapTemplate u; }; -template class Expr> { +template +class Expr> + : public ExpressionBase> { public: using Result = Type; - using FoldableTrait = std::true_type; + using IsFoldableTrait = std::true_type; CLASS_BOILERPLATE(Expr) - Expr(const Scalar &x) : u_{x} {} - Expr(bool x) : u_{Scalar{x}} {} - template - Expr(const Relational> &x) : u_{Relational>{x}} {} - template - Expr(Relational> &&x) - : u_{Relational>{std::move(x)}} {} - template Expr(const A &x) : u_(x) {} + Expr(const Scalar &x) : u{Constant{x}} {} + Expr(bool x) : u{Constant{x}} {} + template Expr(const A &x) : u(x) {} template - Expr(std::enable_if_t, A> &&x) : u_{std::move(x)} {} - template Expr(CopyableIndirection &&x) : u_{std::move(x)} {} - - std::optional> ScalarValue() const { - // TODO: parenthesized constants too - return common::GetIf>(u_); - } - std::ostream &Dump(std::ostream &) const; - std::optional> Fold(FoldingContext &c); - int Rank() const { return 1; } // TODO + Expr(std::enable_if_t, A> &&x) : u{std::move(x)} {} + Expr(const DataRef &x) : u{DataReference{x}} {} + Expr(const FunctionRef &x) : u{FunctionReference{x}} {} private: - std::variant, CopyableIndirection, - CopyableIndirection, - // TODO Parentheses, - Not, LogicalOperation, AnyRelational> - u_; + using Operations = + std::variant, LogicalOperation, Relational>; + using Others = std::variant, DataReference, + FunctionReference>; + +public: + common::CombineVariants u; }; -// Dynamically polymorphic expressions that can hold any supported kind -// of a specific intrinsic type category. -template class Expr> { +// A polymorphic expression of known intrinsic type category, but dynamic +// kind, represented as a discriminated union over Expr> +// for each supported kind K in the category. +template +class Expr> : public ExpressionBase> { public: using Result = SomeKind; - using FoldableTrait = std::true_type; - static constexpr TypeCategory category{CAT}; + using IsFoldableTrait = std::true_type; CLASS_BOILERPLATE(Expr) - 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; - - 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); - } + template Expr(const A &x) : u{x} {} + template + Expr(std::enable_if_t, A> &&x) : u{std::move(x)} {} - Variant u; + common::MapTemplate> u; }; // BOZ literal constants need to be wide enough to hold an integer or real @@ -581,12 +588,12 @@ public: // few situations. using BOZLiteralConstant = value::Integer<128>; -// A completely generic expression, polymorphic across the intrinsic type +// A completely generic expression, polymorphic across all of the intrinsic type // categories and each of their kinds. -template<> class Expr { +template<> class Expr : public ExpressionBase { public: using Result = SomeType; - using FoldableTrait = std::true_type; + using IsFoldableTrait = std::true_type; CLASS_BOILERPLATE(Expr) template Expr(const A &x) : u{x} {} @@ -599,17 +606,25 @@ public: template Expr(Expr> &&x) : u{Expr>{std::move(x)}} {} - std::optional> ScalarValue() const; - std::ostream &Dump(std::ostream &) const; - std::optional> Fold(FoldingContext &); - int Rank() const; + template + Expr &operator=(const Expr> &x) { + u = Expr>{x}; + return *this; + } - std::variant, Expr, Expr, - Expr, Expr, BOZLiteralConstant> + template + Expr &operator=(Expr> &&x) { + u = Expr>{std::move(x)}; + return *this; + } + + using Others = std::variant; + common::CombineVariants> u; }; -extern template class Expr>; // TODO others +extern template class Expr>; // TODO more extern template struct Relational>; extern template struct Relational>; extern template struct Relational>; @@ -627,6 +642,7 @@ extern template struct Relational>; extern template struct Relational>; extern template struct Relational>; // TODO // more +extern template struct Relational; extern template class Expr>; extern template class Expr>; extern template class Expr>; @@ -638,5 +654,32 @@ extern template class Expr; extern template class Expr; extern template class Expr; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase>; +extern template struct ExpressionBase; +extern template struct ExpressionBase; +extern template struct ExpressionBase; +extern template struct ExpressionBase; +extern template struct ExpressionBase; +extern template struct ExpressionBase; + } // namespace Fortran::evaluate #endif // FORTRAN_EVALUATE_EXPRESSION_H_ diff --git a/flang/lib/evaluate/tools.cc b/flang/lib/evaluate/tools.cc index 4f486de..06355e4 100644 --- a/flang/lib/evaluate/tools.cc +++ b/flang/lib/evaluate/tools.cc @@ -26,27 +26,32 @@ ConvertRealOperandsResult ConvertRealOperands( parser::ContextualMessages &messages, Expr &&x, Expr &&y) { return std::visit( - common::visitors{[&](Expr &&ix, Expr &&iy) { - // Can happen in a CMPLX() constructor. Per F'2018, - // both integer operands are converted to default REAL. - return std::optional{std::make_pair( - Expr{Expr{std::move(ix)}}, - Expr{Expr{std::move(iy)}})}; - }, - [&](Expr &&ix, Expr &&ry) { - auto rx{ConvertToTypeAndKindOf(ry, std::move(ix))}; + common::visitors{ + [&](Expr &&ix, + Expr &&iy) -> ConvertRealOperandsResult { + // Can happen in a CMPLX() constructor. Per F'2018, + // both integer operands are converted to default REAL. + return std::optional{std::make_pair( + ToCategoryExpr(ConvertToType(std::move(ix))), + ToCategoryExpr(ConvertToType(std::move(iy))))}; + }, + [&](Expr &&ix, + Expr &&ry) -> ConvertRealOperandsResult { + auto rx{ConvertTo(ry, std::move(ix))}; return std::optional{std::make_pair(std::move(rx), std::move(ry))}; }, - [&](Expr &&rx, Expr &&iy) { - auto ry{ConvertToTypeAndKindOf(rx, std::move(iy))}; + [&](Expr &&rx, + Expr &&iy) -> ConvertRealOperandsResult { + auto ry{ConvertTo(rx, std::move(iy))}; return std::optional{std::make_pair(std::move(rx), std::move(ry))}; }, - [&](Expr &&rx, Expr &&ry) { + [&](Expr &&rx, + Expr &&ry) -> ConvertRealOperandsResult { ConvertToSameKind(rx, ry); return std::optional{std::make_pair(std::move(rx), std::move(ry))}; }, - [&](const auto &, const auto &) - -> std::optional, Expr>> { + [&](auto &&, auto &&) -> ConvertRealOperandsResult { + // TODO: allow BOZ here? messages.Say("operands must be INTEGER or REAL"_err_en_US); return std::nullopt; }}, @@ -65,15 +70,6 @@ ConvertRealOperandsResult ConvertRealOperands( common::MapOptional(f, std::move(x), std::move(y))); } -Expr GenericScalarToExpr(const Scalar &x) { - return std::visit( - [](const auto &c) -> Expr { - using Ty = TypeOf; - return {Expr>{Expr{c}}}; - }, - x.u); -} - template class OPR, TypeCategory CAT> std::optional> PromoteAndCombine( Expr> &&x, Expr> &&y) { @@ -85,7 +81,7 @@ std::optional> PromoteAndCombine( return {Expr{OPR{EnsureKind(std::move(xk)), EnsureKind(std::move(yk))}}}; }, - std::move(x.u.u), std::move(y.u.u))}}; + std::move(x.u), std::move(y.u))}}; } template class OPR> @@ -106,10 +102,10 @@ std::optional> NumericOperation( [&](auto &&rxk) -> Expr { using kindEx = decltype(rxk); using resultType = ResultType; - return {kindEx{ - OPR{std::move(rxk), kindEx{std::move(iy)}}}}; + return {kindEx{OPR{std::move(rxk), + ConvertToType(std::move(iy))}}}; }, - std::move(rx.u.u))}}; + std::move(rx.u))}}; }, [](Expr &&ix, Expr &&ry) { return std::optional{Expr{std::visit( @@ -117,9 +113,10 @@ std::optional> NumericOperation( using kindEx = decltype(ryk); using resultType = ResultType; return {kindEx{ - OPR{kindEx{std::move(ix)}, std::move(ryk)}}}; + OPR{ConvertToType(std::move(ix)), + std::move(ryk)}}}; }, - std::move(ry.u.u))}}; + std::move(ry.u))}}; }, [](Expr &&zx, Expr &&zy) { return PromoteAndCombine( diff --git a/flang/lib/evaluate/tools.h b/flang/lib/evaluate/tools.h index 1581e63..147e6b3 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.u); + [](auto &xk) { return Expr>{-std::move(xk)}; }, x.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.u, y.u.u); + x.u, y.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.u, y.u.u); + x.u, y.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.u, y.u.u); + x.u, y.u); } template @@ -87,24 +87,76 @@ Expr> operator/(Expr> &&x, Expr> &&y) { [](auto &xk, auto &yk) { return Expr>{std::move(xk) / std::move(yk)}; }, - x.u.u, y.u.u); + x.u, y.u); } -// Convert the second argument expression to an expression of the same type -// and kind as that of the first. -template -Expr> ConvertToTypeAndKindOf( - const Expr> &to, Expr &&from) { +// Generalizers: these take expressions of more specific types and wrap +// them in more abstract containers. + +template +Expr> ToCategoryExpr(Expr> &&x) { + return {std::move(x)}; +} + +template Expr ToGenericExpr(A &&x) { + return {std::move(x)}; +} + +template +Expr ToGenericExpr(Expr> &&x) { + return {ToCategoryExpr(std::move(x))}; +} + +// 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). + +template +Expr ConvertToType(Expr> &&x) { + return {Convert{std::move(x)}}; +} + +template +Expr> ConvertTo( + const Expr> &, Expr> &&x) { + return ConvertToType>(std::move(x)); +} + +template +Expr> ConvertTo( + const Expr> &, Expr> &&x) { + return ConvertToType>(ToCategoryExpr(std::move(x))); +} + +template +Expr> ConvertTo( + const Expr> &to, Expr> &&from) { return std::visit( - [&](const auto &tk) -> Expr> { - using SpecificExpr = std::decay_t; - return {SpecificExpr{std::move(from)}}; + [&](const auto &toKindExpr) { + using KindExpr = std::decay_t; + return ToCategoryExpr( + ConvertToType>(std::move(from))); }, - to.u.u); + to.u); } -// Given two expressions of the same type category, convert one to the -// kind of the other in place if it has a smaller kind. +template +Expr> ConvertTo( + const Expr> &to, Expr> &&from) { + return ConvertTo(to, ToCategoryExpr(std::move(from))); +} + +template +Expr ConvertTo(const Expr &to, Expr &&from) { + return std::visit( + [&](const auto &toCatExpr) { + return ToGenericExpr(ConvertTo(toCatExpr, std::move(from))); + }, + to.u); +} + +// Given references to two expressions of the same type category, convert +// either to the kind of the other in place if it has a smaller kind. template void ConvertToSameKind(Expr> &x, Expr> &y) { std::visit( @@ -112,17 +164,18 @@ void ConvertToSameKind(Expr> &x, Expr> &y) { using xt = ResultType; using yt = ResultType; if constexpr (xt::kind < yt::kind) { - x.u = Expr{xk}; + x.u = Expr{Convert{x}}; } else if constexpr (xt::kind > yt::kind) { - y.u = Expr{yk}; + y.u = Expr{Convert{y}}; } }, - x.u.u, y.u.u); + x.u, y.u); } // Ensure that both operands of an intrinsic REAL operation (or CMPLX() // constructor) are INTEGER or REAL, then convert them as necessary to the // same kind of REAL. +// TODO pmk: need a better type that guarantees that both have same kind using ConvertRealOperandsResult = std::optional, Expr>>; ConvertRealOperandsResult ConvertRealOperands( @@ -131,26 +184,10 @@ ConvertRealOperandsResult ConvertRealOperands(parser::ContextualMessages &, std::optional> &&, std::optional> &&); template Expr> ScalarConstantToExpr(const A &x) { - static_assert(std::is_same_v>, std::decay_t> || - !"TypeOf<> is broken"); - return {x}; -} - -template -Expr> ToSomeKindExpr(Expr> &&x) { - return {std::move(x)}; -} - -Expr GenericScalarToExpr(const Scalar &); - -template -Expr ToGenericExpr(Expr> &&x) { - return Expr{Expr>{std::move(x)}}; -} - -template -Expr ToGenericExpr(Expr> &&x) { - return Expr{std::move(x)}; + using Ty = TypeOf; + static_assert( + std::is_same_v, std::decay_t> || !"TypeOf<> is broken"); + return {Constant{x}}; } // Convert, if necessary, an expression to a specific kind in the same @@ -158,8 +195,7 @@ Expr ToGenericExpr(Expr> &&x) { template Expr EnsureKind(Expr> &&x) { using ToType = TOTYPE; - using FromGenericType = SomeKind; - if (auto *p{std::get_if>(&x.u.u)}) { + if (auto *p{std::get_if>(&x.u)}) { return std::move(*p); } if constexpr (ToType::category == TypeCategory::Complex) { @@ -169,15 +205,15 @@ Expr EnsureKind(Expr> &&x) { using FromPart = typename FromType::Part; using FromGeneric = SomeKind; using ToPart = typename ToType::Part; - Convert re{Expr{ + Convert re{Expr{ Expr{ComplexComponent{false, z}}}}; - Convert im{Expr{ + Convert im{Expr{ Expr{ComplexComponent{true, z}}}}; return {std::move(re), std::move(im)}; }, - x.u.u)}; + x.u)}; } else { - return {Convert{std::move(x)}}; + return {Convert{std::move(x)}}; } } diff --git a/flang/lib/evaluate/type.h b/flang/lib/evaluate/type.h index cb585ba..b1f4fb1 100644 --- a/flang/lib/evaluate/type.h +++ b/flang/lib/evaluate/type.h @@ -26,7 +26,6 @@ #include "real.h" #include "../common/fortran.h" #include "../common/idioms.h" -#include "../common/kind-variant.h" #include "../common/template.h" #include #include @@ -37,14 +36,14 @@ namespace Fortran::evaluate { using common::TypeCategory; -// Specific intrinsic types +// Specific intrinsic types are represented by specializations of +// the class template Type. +template struct Type; -template struct Type; - -template struct TypeBase { - static constexpr TypeCategory category{C}; +template struct TypeBase { + static constexpr bool isSpecificType{true}; + static constexpr TypeCategory category{CATEGORY}; static constexpr int kind{KIND}; - static constexpr bool hasLen{false}; static std::string Dump() { return EnumToString(category) + '(' + std::to_string(kind) + ')'; } @@ -92,14 +91,10 @@ struct Type using Scalar = value::Complex; }; -template struct Type { - static constexpr TypeCategory category{TypeCategory::Character}; - static constexpr int kind{KIND}; - static constexpr bool hasLen{true}; +template +struct Type + : public TypeBase { using Scalar = std::string; - static std::string Dump() { - return EnumToString(category) + '(' + std::to_string(kind) + ')'; - } }; template @@ -113,8 +108,8 @@ struct Type template using Scalar = typename std::decay_t::Scalar; // Given a specific type, find the type of the same kind in another category. -template -using SameKind = Type::kind>; +template +using SameKind = Type::kind>; // Convenience type aliases: // Default REAL just simply has to be IEEE-754 single precision today. @@ -135,72 +130,12 @@ using DefaultCharacter = Type; using SubscriptInteger = Type; using LogicalResult = Type; -// The CategoryUnion template applies a given template to all of -// the supported kinds in a given intrinsic type category, and -// builds a KindVariant<> union over the results. This allows -// us to specify the supported kind values in just one place (here) -// with resorting to macros. -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; - -// IntrinsicTypeUnion takes a template and instantiates it over -// all five of the intrinsic type categories, using them as the -// alternatives in a KindVariant. -template class A> -struct IntrinsicTypeUnionTemplate { - template using PerCategory = CategoryUnion; - using type = common::KindVariant; -}; - -template class A> -using IntrinsicTypeUnion = typename IntrinsicTypeUnionTemplate::type; - // For an intrinsic type category CAT, CategoryTypes is an instantiation -// of std::tuple> for every supported kind K in that category. -template -using CategoryTypesTuple = std::tuple...>; +// of std::tuple> over each supported kind K in that category. +template +using CategoryTypesTuple = std::tuple...>; -template struct CategoryTypesHelper; +template struct CategoryTypesHelper; template<> struct CategoryTypesHelper { using type = CategoryTypesTuple; }; @@ -216,8 +151,8 @@ template<> struct CategoryTypesHelper { template<> struct CategoryTypesHelper { using type = CategoryTypesTuple; }; -template -using CategoryTypes = typename CategoryTypesHelper::type; +template +using CategoryTypes = typename CategoryTypesHelper::type; using NumericTypes = common::CombineTuples, CategoryTypes, CategoryTypes>; @@ -238,13 +173,14 @@ template struct TypeOfHelper { }; static constexpr int index{ common::SearchMembers}; - static_assert(index >= 0 || !"No intrinsic type found for constant type"); - using type = std::tuple_element_t; + using type = std::conditional_t= 0, + std::tuple_element_t, void>; }; template using TypeOf = typename TypeOfHelper::type; -// A variant union that can hold a scalar constant of some type in a set. +// A variant union that can hold a scalar constant of some type in a set, +// which is passed in as a tuple of Type<> specializations. template struct SomeScalar { using Types = TYPES; CLASS_BOILERPLATE(SomeScalar) @@ -280,6 +216,19 @@ template struct SomeScalar { u); } + auto IsTrue() const { + return std::visit( + [](const auto &x) -> std::optional { + if constexpr (TypeOf::category == + TypeCategory::Logical) { + return {x.IsTrue()}; + } else { + return std::nullopt; + } + }, + u); + } + template auto GetIf() const { return common::GetIf>(u); } @@ -287,14 +236,15 @@ template struct SomeScalar { common::MapTemplate u; }; -template -using SomeKindScalar = SomeScalar>; +template +using SomeKindScalar = SomeScalar>; using GenericScalar = SomeScalar; // Represents a type of any supported kind within a particular category. -template struct SomeKind { - using Scalar = SomeKindScalar; - static constexpr TypeCategory category{CAT}; +template struct SomeKind { + static constexpr bool isSpecificType{false}; + static constexpr TypeCategory category{CATEGORY}; + using Scalar = SomeKindScalar; }; using SomeInteger = SomeKind; @@ -304,7 +254,10 @@ using SomeCharacter = SomeKind; using SomeLogical = SomeKind; // Represents a completely generic intrinsic type. +using SomeCategory = + std::tuple; struct SomeType { + static constexpr bool isSpecificType{false}; using Scalar = GenericScalar; }; diff --git a/flang/lib/evaluate/variable.cc b/flang/lib/evaluate/variable.cc index 0dde964..c9547ba 100644 --- a/flang/lib/evaluate/variable.cc +++ b/flang/lib/evaluate/variable.cc @@ -127,18 +127,27 @@ Expr Substring::last() const { std::optional Substring::Fold(FoldingContext &context) { std::optional> lbValue, ubValue; + // pmk: streamline if (first_.has_value()) { - lbValue = (*first_)->Fold(context); + if (auto c{(*first_)->Fold(context)}) { + lbValue = c->value; + } } else { - lbValue = first().Fold(context); + if (auto c{first().Fold(context)}) { + lbValue = c->value; + } } if (lbValue.has_value()) { first_ = IndirectSubscriptIntegerExpr{Expr{*lbValue}}; } if (last_.has_value()) { - ubValue = (*last_)->Fold(context); + if (auto c{(*last_)->Fold(context)}) { + ubValue = c->value; + } } else { - ubValue = last().Fold(context); + if (auto c{last().Fold(context)}) { + ubValue = c->value; + } } if (ubValue.has_value()) { last_ = IndirectSubscriptIntegerExpr{Expr{*ubValue}}; diff --git a/flang/lib/evaluate/variable.h b/flang/lib/evaluate/variable.h index 9ce5bf9..d1a695b 100644 --- a/flang/lib/evaluate/variable.h +++ b/flang/lib/evaluate/variable.h @@ -171,7 +171,7 @@ private: // variants of sections instead. class Substring { public: - using FoldableTrait = std::true_type; + using IsFoldableTrait = std::true_type; CLASS_BOILERPLATE(Substring) Substring(DataRef &&, std::optional> &&, std::optional> &&); diff --git a/flang/lib/parser/parse-tree.h b/flang/lib/parser/parse-tree.h index 2580a5d..5a180b8 100644 --- a/flang/lib/parser/parse-tree.h +++ b/flang/lib/parser/parse-tree.h @@ -50,10 +50,10 @@ // although a C++ compiler wouldn't default them anyway due to the presence // of explicitly defaulted move constructors and move assignments. -CLASS_TRAIT(EmptyTrait); -CLASS_TRAIT(WrapperTrait); -CLASS_TRAIT(UnionTrait); -CLASS_TRAIT(TupleTrait); +CLASS_TRAIT(EmptyTrait) +CLASS_TRAIT(WrapperTrait) +CLASS_TRAIT(UnionTrait) +CLASS_TRAIT(TupleTrait) // Some parse tree nodes have fields in them to cache the results of a // successful semantic analysis later. Their types are forward declared diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc index ecd26bb..666adfa 100644 --- a/flang/lib/semantics/expression.cc +++ b/flang/lib/semantics/expression.cc @@ -25,6 +25,7 @@ namespace Fortran::semantics { using common::TypeCategory; using evaluate::Expr; +using evaluate::SomeKind; using evaluate::SomeType; using evaluate::Type; @@ -61,7 +62,7 @@ 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() > 1) { + if (result->Rank() > 0) { ea.context().messages.Say("must be scalar"_err_en_US); return std::nullopt; } @@ -91,29 +92,50 @@ std::optional> AnalyzeHelper( if (auto *intexpr{std::get_if>(&result->u)}) { return {std::move(*intexpr)}; } - ea.context().messages.Say("expression must be integer"_err_en_US); + ea.context().messages.Say("expression must be INTEGER"_err_en_US); } return std::nullopt; } +// pmk: document, maybe put elsewhere +template struct ConstantHelper { + using Types = evaluate::CategoryTypes; + explicit ConstantHelper(VALUE &&x) : value{std::move(x)} {} + template void SetKindTraverser(int kind) { + if constexpr (J < std::tuple_size_v) { + using Ty = std::tuple_element_t; + if (kind == Ty::kind) { + result = Expr{evaluate::Constant{std::move(value)}}; + } else { + SetKindTraverser(kind); + } + } + } + void SetKind(int kind) { SetKindTraverser<0>(kind); } + VALUE value; + std::optional>> result; +}; + 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)}; - using Ex = Expr; - std::optional result{Ex::template ForceKind(kind, std::move(value))}; - if (!result.has_value()) { + ConstantHelper helper{std::move(value)}; + helper.SetKind(kind); + if (!helper.result.has_value()) { ea.context().messages.Say("unsupported CHARACTER(KIND=%ju)"_err_en_US, static_cast(kind)); } - return result; + return std::move(helper.result); } -template MaybeExpr PackageGeneric(std::optional &&x) { - std::function(A &&)> f{ - [](A &&y) { return Expr{std::move(y)}; }}; - return common::MapOptional(f, std::move(x)); +template +MaybeExpr PackageGeneric(std::optional &&x) { + if (x.has_value()) { + return {evaluate::ToGenericExpr(std::move(*x))}; + } + return std::nullopt; } template<> @@ -132,12 +154,12 @@ MaybeExpr AnalyzeHelper( std::optional> lb, ub; if (lbTree.has_value()) { if (MaybeIntExpr lbExpr{AnalyzeHelper(ea, *lbTree)}) { - lb = Expr{std::move(*lbExpr)}; + lb = evaluate::ConvertToType(std::move(*lbExpr)); } } if (ubTree.has_value()) { if (MaybeIntExpr ubExpr{AnalyzeHelper(ea, *ubTree)}) { - ub = Expr{std::move(*ubExpr)}; + ub = evaluate::ConvertToType(std::move(*ubExpr)); } } if (!lb.has_value() || !ub.has_value()) { @@ -147,7 +169,7 @@ MaybeExpr AnalyzeHelper( evaluate::CopyableIndirection ind{std::move(substring)}; Expr chExpr{std::move(ind)}; chExpr.Fold(ea.context()); - return {Expr{Expr{std::move(chExpr)}}}; + return {evaluate::ToGenericExpr(chExpr)}; } // Common handling of parser::IntLiteralConstant and SignedIntLiteralConstant @@ -157,13 +179,13 @@ std::optional> IntLiteralConstant( auto kind{ea.Analyze(std::get>(x.t), ea.defaultIntegerKind())}; 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()) { + ConstantHelper helper{std::move(value)}; + helper.SetKind(kind); + if (!helper.result.has_value()) { ea.context().messages.Say("unsupported INTEGER(KIND=%ju)"_err_en_US, static_cast(kind)); } - return result; + return std::move(helper.result); } static std::optional> AnalyzeLiteral( @@ -214,16 +236,26 @@ std::optional> ReadRealLiteral( if (context.flushDenormalsToZero) { value = value.FlushDenormalToZero(); } - return {evaluate::ToSomeKindExpr(Expr{value})}; + return {evaluate::ToCategoryExpr(Expr{evaluate::Constant{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); + + 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); + } + } } + void SetKind(int kind) { SetKindTraverser<0>(kind); } + parser::CharBlock literal; evaluate::FoldingContext &context; std::optional> result; @@ -252,7 +284,7 @@ static std::optional> AnalyzeLiteral( } auto kind{ea.Analyze(x.kind, defaultKind)}; RealHelper helper{x.real.source, localFoldingContext}; - Expr::template AtKind(helper, kind); + helper.SetKind(kind); if (!helper.result.has_value()) { ctxMsgs.Say("unsupported REAL(KIND=%ju)"_err_en_US, static_cast(kind)); @@ -325,13 +357,13 @@ static std::optional> AnalyzeLiteral( auto kind{ea.Analyze(std::get>(x.t), ea.defaultLogicalKind())}; bool value{std::get(x.t)}; - using Ex = Expr; - std::optional result{Ex::template ForceKind(kind, std::move(value))}; - if (!result.has_value()) { + ConstantHelper helper{std::move(value)}; + helper.SetKind(kind); + if (!helper.result.has_value()) { ea.context().messages.Say("unsupported LOGICAL(KIND=%ju)"_err_en_US, static_cast(kind)); } - return result; + return std::move(helper.result); } template<> @@ -431,7 +463,7 @@ MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::Divide &x) { template<> MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::Add &x) { - // TODO pmk WIP + // TODO return std::nullopt; } @@ -565,19 +597,22 @@ ExpressionAnalyzer::KindParam ExpressionAnalyzer::Analyze( kindParam->u); } +// TODO pmk: need a way to represent a tuple of same-typed expressions, avoid CHECK here 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 &&rx, auto &&ix) -> Expr { - using realType = evaluate::ResultType; + [&](auto &&re) -> Expr { + using realType = evaluate::ResultType; + auto *im{std::get_if>(&converted->second.u)}; + CHECK(im != nullptr); constexpr int kind{realType::kind}; using zType = evaluate::Type; - return {Expr{evaluate::ComplexConstructor{ - std::move(rx), std::move(ix)}}}; + return {Expr{Expr{evaluate::ComplexConstructor{ + std::move(re), std::move(*im)}}}}; }, - std::move(converted->first.u.u), std::move(converted->second.u.u))}; + std::move(converted->first.u))}; } return std::nullopt; } diff --git a/flang/test/evaluate/logical.cc b/flang/test/evaluate/logical.cc index 5b92b79..db85079 100644 --- a/flang/test/evaluate/logical.cc +++ b/flang/test/evaluate/logical.cc @@ -19,9 +19,9 @@ template void testKind() { using Type = Fortran::evaluate::Type; + TEST(Type::isSpecificType); TEST(Type::category == Fortran::common::TypeCategory::Logical); TEST(Type::kind == KIND); - TEST(!Type::hasLen); using Value = Fortran::evaluate::Scalar; MATCH(8 * KIND, Value::bits); TEST(!Value{}.IsTrue());