[flang] LOGICAL operations
authorpeter klausler <pklausler@nvidia.com>
Fri, 7 Sep 2018 22:25:10 +0000 (15:25 -0700)
committerpeter klausler <pklausler@nvidia.com>
Wed, 12 Sep 2018 23:29:17 +0000 (16:29 -0700)
Original-commit: flang-compiler/f18@71a1de4c599c89f2b93afffdc40b8b6d10066dbd
Reviewed-on: https://github.com/flang-compiler/f18/pull/183
Tree-same-pre-rewrite: false

flang/lib/evaluate/expression.cc
flang/lib/evaluate/expression.h
flang/lib/evaluate/tools.cc
flang/lib/evaluate/tools.h
flang/lib/semantics/expression.cc
flang/lib/semantics/symbol.cc

index 03776c6..4bf3043 100644 (file)
@@ -360,11 +360,6 @@ auto Relational<A>::FoldScalar(FoldingContext &c, const Scalar<Operand> &a,
     case Relation::Unordered: return std::nullopt;
     }
   }
-  if constexpr (A::category == TypeCategory::Complex) {
-    bool eqOk{opr == RelationalOperator::LE || opr == RelationalOperator::EQ ||
-        opr == RelationalOperator::GE};
-    return {eqOk == a.Equals(b)};
-  }
   if constexpr (A::category == TypeCategory::Character) {
     switch (Compare(a, b)) {
     case Ordering::Less:
@@ -582,11 +577,6 @@ template struct Relational<Type<TypeCategory::Real, 4>>;
 template struct Relational<Type<TypeCategory::Real, 8>>;
 template struct Relational<Type<TypeCategory::Real, 10>>;
 template struct Relational<Type<TypeCategory::Real, 16>>;
-template struct Relational<Type<TypeCategory::Complex, 2>>;
-template struct Relational<Type<TypeCategory::Complex, 4>>;
-template struct Relational<Type<TypeCategory::Complex, 8>>;
-template struct Relational<Type<TypeCategory::Complex, 10>>;
-template struct Relational<Type<TypeCategory::Complex, 16>>;
 template struct Relational<Type<TypeCategory::Character, 1>>;  // TODO others
 template struct Relational<SomeType>;
 
index 017b603..1b80d52 100644 (file)
@@ -36,17 +36,18 @@ namespace Fortran::evaluate {
 
 using common::RelationalOperator;
 
-// Expressions are represented by specializations of Expr.
+// Expressions are represented by specializations of the class template Expr.
 // Each of these specializations wraps a single data member "u" that
-// is a std::variant<> discriminated union over the representational
+// is a std::variant<> discriminated union over all of the representational
 // types for the constants, variables, operations, and other entities that
 // can be valid expressions in that context:
-// - Expr<Type<CATEGORY, KIND>> is an expression whose result is of a
+// - Expr<Type<CATEGORY, KIND>> represents an expression whose result is of a
 //   specific intrinsic type category and kind, e.g. Type<TypeCategory::Real, 4>
 // - Expr<SomeKind<CATEGORY>> is a union of Expr<Type<CATEGORY, K>> for each
-//   kind type parameter value K in that intrinsic type category
+//   kind type parameter value K in that intrinsic type category.  It represents
+//   an expression with known category and any kind.
 // - Expr<SomeType> is a union of Expr<SomeKind<CATEGORY>> over the five
-//   intrinsic type categories of Fortran.
+//   intrinsic type categories of Fortran.  It represents any valid expression.
 template<typename A> class Expr;
 
 // Everything that can appear in, or as, a valid Fortran expression must be
@@ -55,7 +56,7 @@ template<typename A> class Expr;
 // or SomeType.
 template<typename A> using ResultType = typename std::decay_t<A>::Result;
 
-// Wraps a constant value in a class to make its type clear.
+// Wraps a constant value in a class with its resolved type.
 template<typename T> struct Constant {
   using Result = T;
   using Value = Scalar<Result>;  // TODO rank > 0
@@ -378,9 +379,9 @@ struct LogicalOperation
   using Base = Operation<LogicalOperation, Result, Operand, Operand>;
   CLASS_BOILERPLATE(LogicalOperation)
   LogicalOperation(
-      const Expr<Operand> &x, const Expr<Operand> &y, LogicalOperator opr)
+      LogicalOperator opr, const Expr<Operand> &x, const Expr<Operand> &y)
     : Base{x, y}, logicalOperator{opr} {}
-  LogicalOperation(Expr<Operand> &&x, Expr<Operand> &&y, LogicalOperator opr)
+  LogicalOperation(LogicalOperator opr, Expr<Operand> &&x, Expr<Operand> &&y)
     : Base{std::move(x), std::move(y)}, logicalOperator{opr} {}
 
   std::optional<Scalar<Result>> FoldScalar(
@@ -390,7 +391,7 @@ struct LogicalOperation
   LogicalOperator logicalOperator;
 };
 
-// Per-category expressions
+// Per-category expression representations
 
 // Common Expr<> behaviors
 template<typename RESULT> struct ExpressionBase {
@@ -497,11 +498,11 @@ public:
   Expr(const DataRef &x) : u{DataReference<Result>{x}} {}
   Expr(const FunctionRef &x) : u{FunctionReference<Result>{x}} {}
 
-  // TODO pmk: Remove Negate, Add, Subtract in favor of component-wise
-  // operations.
-  using Operations = std::variant<Parentheses<Result>, Negate<Result>,
-      Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
-      Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
+  // Note that many COMPLEX operations are represented as REAL operations
+  // over their components (viz., conversions, negation, add, and subtract).
+  using Operations =
+      std::variant<Parentheses<Result>, Multiply<Result>, Divide<Result>,
+          Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
   using Others = std::variant<Constant<Result>, DataReference<Result>,
       FunctionReference<Result>>;
 
@@ -556,8 +557,8 @@ extern template class Expr<Type<TypeCategory::Character, 1>>;  // TODO more
 // 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.
+// first undergoing the same operand conversions that occur with the intrinsic
+// addition operator.  Character relations must have the same kind.
 // There are no relations between LOGICAL values.
 
 template<typename A>
@@ -579,7 +580,12 @@ struct Relational : public Operation<Relational<A>, LogicalResult, A, A> {
   RelationalOperator opr;
 };
 
-template<> struct Relational<SomeType> {
+template<> class Relational<SomeType> {
+  // COMPLEX data is compared piecewise.
+  using DirectlyComparableTypes =
+      common::CombineTuples<IntegerTypes, RealTypes, CharacterTypes>;
+
+public:
   using Result = LogicalResult;
   CLASS_BOILERPLATE(Relational)
   template<typename A> Relational(const A &x) : u(x) {}
@@ -587,7 +593,7 @@ template<> struct Relational<SomeType> {
   Relational(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
     : u{std::move(x)} {}
   std::ostream &Dump(std::ostream &o) const;
-  common::MapTemplate<Relational, RelationalTypes> u;
+  common::MapTemplate<Relational, DirectlyComparableTypes> u;
 };
 
 extern template struct Relational<Type<TypeCategory::Integer, 1>>;
@@ -600,11 +606,6 @@ extern template struct Relational<Type<TypeCategory::Real, 4>>;
 extern template struct Relational<Type<TypeCategory::Real, 8>>;
 extern template struct Relational<Type<TypeCategory::Real, 10>>;
 extern template struct Relational<Type<TypeCategory::Real, 16>>;
-extern template struct Relational<Type<TypeCategory::Complex, 2>>;
-extern template struct Relational<Type<TypeCategory::Complex, 4>>;
-extern template struct Relational<Type<TypeCategory::Complex, 8>>;
-extern template struct Relational<Type<TypeCategory::Complex, 10>>;
-extern template struct Relational<Type<TypeCategory::Complex, 16>>;
 extern template struct Relational<Type<TypeCategory::Character, 1>>;  // TODO
                                                                       // more
 extern template struct Relational<SomeType>;
@@ -625,8 +626,8 @@ public:
   Expr(const FunctionRef &x) : u{FunctionReference<Result>{x}} {}
 
 private:
-  using Operations =
-      std::variant<Not<KIND>, LogicalOperation<KIND>, Relational<SomeType>>;
+  using Operations = std::variant<Convert<Result, TypeCategory::Logical>,
+      Not<KIND>, LogicalOperation<KIND>, Relational<SomeType>>;
   using Others = std::variant<Constant<Result>, DataReference<Result>,
       FunctionReference<Result>>;
 
index f70b299..a89c27c 100644 (file)
@@ -22,6 +22,7 @@ using namespace Fortran::parser::literals;
 
 namespace Fortran::evaluate {
 
+// Conversions of complex component expressions to REAL.
 ConvertRealOperandsResult ConvertRealOperands(
     parser::ContextualMessages &messages, Expr<SomeType> &&x,
     Expr<SomeType> &&y) {
@@ -49,8 +50,29 @@ ConvertRealOperandsResult ConvertRealOperands(
             return {AsSameKindExprs<TypeCategory::Real>(
                 std::move(rx), std::move(ry))};
           },
+          [&](Expr<SomeInteger> &&ix,
+              BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
+            return {AsSameKindExprs<TypeCategory::Real>(
+                ConvertToType<DefaultReal>(std::move(ix)),
+                ConvertToType<DefaultReal>(std::move(by)))};
+          },
+          [&](BOZLiteralConstant &&bx,
+              Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
+            return {AsSameKindExprs<TypeCategory::Real>(
+                ConvertToType<DefaultReal>(std::move(bx)),
+                ConvertToType<DefaultReal>(std::move(iy)))};
+          },
+          [&](Expr<SomeReal> &&rx,
+              BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
+            return {AsSameKindExprs<TypeCategory::Real>(
+                std::move(rx), ConvertTo(rx, std::move(by)))};
+          },
+          [&](BOZLiteralConstant &&bx,
+              Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
+            return {AsSameKindExprs<TypeCategory::Real>(
+                ConvertTo(ry, std::move(bx)), std::move(ry))};
+          },
           [&](auto &&, auto &&) -> ConvertRealOperandsResult {
-            // TODO: allow BOZ here?
             messages.Say("operands must be INTEGER or REAL"_err_en_US);
             return std::nullopt;
           }},
@@ -73,6 +95,26 @@ std::optional<Expr<SomeType>> Package(
   return NoExpr();
 }
 
+// Mixed REAL+INTEGER operations.  REAL**INTEGER is a special case that
+// does not require conversion of the exponent expression.
+template<template<typename> class OPR>
+std::optional<Expr<SomeType>> MixedRealLeft(
+    Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
+  return Package(std::visit(
+      [&](auto &&rxk) -> Expr<SomeReal> {
+        using resultType = ResultType<decltype(rxk)>;
+        if constexpr (std::is_same_v<OPR<resultType>, Power<resultType>>) {
+          return AsCategoryExpr(AsExpr(
+              RealToIntPower<resultType>{std::move(rxk), std::move(iy)}));
+        }
+        // G++ 8.1.0 emits bogus warnings about missing return statements if
+        // this statement is wrapped in an "else", as it should be.
+        return AsCategoryExpr(AsExpr(OPR<resultType>{
+            std::move(rxk), ConvertToType<resultType>(std::move(iy))}));
+      },
+      std::move(rx.u)));
+}
+
 std::optional<Expr<SomeComplex>> ConstructComplex(
     parser::ContextualMessages &messages, Expr<SomeType> &&real,
     Expr<SomeType> &&imaginary) {
@@ -106,7 +148,7 @@ Expr<SomeReal> GetComplexPart(const Expr<SomeComplex> &z, bool isImaginary) {
       z.u);
 }
 
-// Handle mixed COMPLEX+REAL (or INTEGER) operations in a smarter way
+// Handle mixed COMPLEX+REAL (or INTEGER) operations in a better way
 // than just converting the second operand to COMPLEX and performing the
 // corresponding COMPLEX+COMPLEX operation.
 template<template<typename> class OPR, TypeCategory RCAT>
@@ -138,8 +180,19 @@ std::optional<Expr<SomeType>> MixedComplexLeft(
       return Package(ConstructComplex(messages, std::move(std::get<0>(*parts)),
           std::move(std::get<1>(*parts))));
     }
+  } else if constexpr (RCAT == TypeCategory::Integer &&
+      std::is_same_v<OPR<DefaultReal>, Power<DefaultReal>>) {
+    // COMPLEX**INTEGER is a special case that doesn't convert the exponent.
+    static_assert(RCAT == TypeCategory::Integer);
+    return Package(std::visit(
+        [&](auto &&zxk) {
+          using Ty = ResultType<decltype(zxk)>;
+          return AsCategoryExpr(
+              AsExpr(RealToIntPower<Ty>{std::move(zxk), std::move(iry)}));
+        },
+        std::move(zx.u)));
   } else {
-    // (a,b) ? x -> (a,b) ? (x,0)
+    // (a,b) ** x -> (a,b) ** (x,0)
     Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))};
     return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
   }
@@ -150,7 +203,7 @@ std::optional<Expr<SomeType>> MixedComplexLeft(
 //  x + (a,b) -> (x+a, b)
 //  x - (a,b) -> (x-a, -b)
 //  x * (a,b) -> (x*a, x*b)
-//  x / (a,b) -> (x,0) / (a,b)
+//  x / (a,b) -> (x,0) / (a,b)   (and **)
 template<template<typename> class OPR, TypeCategory LCAT>
 std::optional<Expr<SomeType>> MixedComplexRight(
     parser::ContextualMessages &messages, Expr<SomeKind<LCAT>> &&irx,
@@ -171,7 +224,7 @@ std::optional<Expr<SomeType>> MixedComplexRight(
           messages, std::move(*rr), AsGenericExpr(-std::move(zi))));
     }
   } else {
-    // x / (a,b) -> (x,0) / (a,b)    and any other operators that make it here
+    // x / (a,b) -> (x,0) / (a,b)
     Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
     return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
   }
@@ -179,9 +232,8 @@ std::optional<Expr<SomeType>> MixedComplexRight(
 }
 
 // N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
-// the operands to a dyadic operation, it assumes the type and kind of the
-// other operand.
-// TODO pmk: add Power, RealToIntPower, &c.
+// the operands to a dyadic operation where one is permitted, it assumes the
+// type and kind of the other operand.
 template<template<typename> class OPR>
 std::optional<Expr<SomeType>> NumericOperation(
     parser::ContextualMessages &messages, Expr<SomeType> &&x,
@@ -196,15 +248,9 @@ std::optional<Expr<SomeType>> NumericOperation(
             return Package(PromoteAndCombine<OPR, TypeCategory::Real>(
                 std::move(rx), std::move(ry)));
           },
-          // Mixed INTEGER/REAL operations
+          // Mixed REAL/INTEGER operations
           [](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
-            return Package(std::visit(
-                [&](auto &&rxk) -> Expr<SomeReal> {
-                  using resultType = ResultType<decltype(rxk)>;
-                  return AsCategoryExpr(AsExpr(OPR<resultType>{std::move(rxk),
-                      ConvertToType<resultType>(std::move(iy))}));
-                },
-                std::move(rx.u)));
+            return MixedRealLeft<OPR>(std::move(rx), std::move(iy));
           },
           [](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
             return Package(std::visit(
@@ -216,7 +262,7 @@ std::optional<Expr<SomeType>> NumericOperation(
                 },
                 std::move(ry.u)));
           },
-          // Homogenous and mixed COMPLEX operations
+          // Homogeneous and mixed COMPLEX operations
           [](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) {
             return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
                 std::move(zx), std::move(zy)));
@@ -263,14 +309,16 @@ std::optional<Expr<SomeType>> NumericOperation(
       std::move(x.u), std::move(y.u));
 }
 
-template std::optional<Expr<SomeType>> NumericOperation<Add>(
-    parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
-template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
+template std::optional<Expr<SomeType>> NumericOperation<Power>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 template std::optional<Expr<SomeType>> NumericOperation<Divide>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
+template std::optional<Expr<SomeType>> NumericOperation<Add>(
+    parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
+template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
+    parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 
 std::optional<Expr<SomeType>> Negation(
     parser::ContextualMessages &messages, Expr<SomeType> &&x) {
@@ -296,4 +344,120 @@ std::optional<Expr<SomeType>> Negation(
       std::move(x.u));
 }
 
+template<typename T>
+Expr<LogicalResult> PackageRelation(
+    RelationalOperator opr, Expr<T> &&x, Expr<T> &&y) {
+  static_assert(T::isSpecificType);
+  return Expr<LogicalResult>{
+      Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}};
+}
+
+template<TypeCategory CAT>
+Expr<LogicalResult> PromoteAndRelate(
+    RelationalOperator opr, Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
+  return std::visit(
+      [=](auto &&xy) {
+        return PackageRelation(opr, std::move(xy[0]), std::move(xy[1]));
+      },
+      AsSameKindExprs(std::move(x), std::move(y)));
+}
+
+std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
+    RelationalOperator opr, Expr<SomeType> &&x, Expr<SomeType> &&y) {
+  return std::visit(
+      common::visitors{[=](Expr<SomeInteger> &&ix, Expr<SomeInteger> &&iy) {
+                         return std::make_optional(PromoteAndRelate(
+                             opr, std::move(ix), std::move(iy)));
+                       },
+          [=](Expr<SomeReal> &&rx, Expr<SomeReal> &&ry) {
+            return std::make_optional(
+                PromoteAndRelate(opr, std::move(rx), std::move(ry)));
+          },
+          [&](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
+            return Relate(
+                messages, opr, std::move(x), ConvertTo(rx, std::move(iy)));
+          },
+          [&](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
+            return Relate(
+                messages, opr, ConvertTo(ry, std::move(ix)), std::move(y));
+          },
+          [&](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) {
+            if (opr != RelationalOperator::EQ &&
+                opr != RelationalOperator::NE) {
+              messages.Say(
+                  "COMPLEX data may be compared only for equality"_err_en_US);
+              return std::optional<Expr<LogicalResult>>{};
+            } else {
+              auto rr{Relate(messages, opr, GetComplexPart(zx, false),
+                  GetComplexPart(zy, false))};
+              auto ri{Relate(messages, opr, GetComplexPart(zx, true),
+                  GetComplexPart(zy, true))};
+              if (auto parts{
+                      common::AllPresent(std::move(rr), std::move(ri))}) {
+                // (a,b)==(c,d) -> (a==c) .AND. (b==d)
+                // (a,b)/=(c,d) -> (a/=c) .OR. (b/=d)
+                LogicalOperator combine{opr == RelationalOperator::EQ
+                        ? LogicalOperator::And
+                        : LogicalOperator::Or};
+                return std::make_optional(
+                    Expr<LogicalResult>{LogicalOperation<LogicalResult::kind>{
+                        combine, std::move(std::get<0>(*parts)),
+                        std::move(std::get<1>(*parts))}});
+              } else {
+                return std::optional<Expr<LogicalResult>>{};
+              }
+            }
+          },
+          [&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
+            return Relate(
+                messages, opr, std::move(x), ConvertTo(zx, std::move(iy)));
+          },
+          [&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
+            return Relate(
+                messages, opr, std::move(x), ConvertTo(zx, std::move(ry)));
+          },
+          [&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) {
+            return Relate(
+                messages, opr, ConvertTo(zy, std::move(ix)), std::move(y));
+          },
+          [&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
+            return Relate(
+                messages, opr, ConvertTo(zy, std::move(rx)), std::move(y));
+          },
+          [&](Expr<SomeCharacter> &&cx, Expr<SomeCharacter> &&cy) {
+            return std::visit(
+                [&](auto &&cxk, auto &&cyk) {
+                  using Ty = ResultType<decltype(cxk)>;
+                  if constexpr (std::is_same_v<Ty, ResultType<decltype(cyk)>>) {
+                    return std::make_optional(
+                        PackageRelation(opr, std::move(cxk), std::move(cyk)));
+                  } else {
+                    messages.Say(
+                        "CHARACTER operands do not have same KIND"_err_en_US);
+                    return std::optional<Expr<LogicalResult>>{};
+                  }
+                },
+                std::move(cx.u), std::move(cy.u));
+          },
+          // Default case
+          [&](auto &&, auto &&) {
+            // TODO: defined operator
+            messages.Say(
+                "relational operands do not have comparable types"_err_en_US);
+            return std::optional<Expr<LogicalResult>>{};
+          }},
+      std::move(x.u), std::move(y.u));
+}
+
+Expr<SomeLogical> BinaryLogicalOperation(
+    LogicalOperator opr, Expr<SomeLogical> &&x, Expr<SomeLogical> &&y) {
+  return std::visit(
+      [=](auto &&xy) {
+        using Ty = ResultType<decltype(xy[0])>;
+        return Expr<SomeLogical>{Expr<Ty>{LogicalOperation<Ty::kind>{
+            opr, std::move(xy[0]), std::move(xy[1])}}};
+      },
+      AsSameKindExprs(std::move(x), std::move(y)));
+}
+
 }  // namespace Fortran::evaluate
index 1f054e5..161c70e 100644 (file)
@@ -193,14 +193,14 @@ Expr<SomeType> ConvertTo(const Expr<SomeType> &to, Expr<FT> &&from) {
 }
 
 template<TypeCategory CAT>
-Expr<SomeType> ConvertTo(
+Expr<SomeKind<CAT>> ConvertTo(
     const Expr<SomeKind<CAT>> &to, BOZLiteralConstant &&from) {
-  return AsGenericExpr(std::visit(
+  return std::visit(
       [&](const auto &tok) {
         using Ty = ResultType<decltype(tok)>;
         return AsCategoryExpr(ConvertToType<Ty>(std::move(from)));
       },
-      to.u));
+      to.u);
 }
 
 template<typename A, int N = 2> using SameExprs = std::array<Expr<A>, N>;
@@ -267,6 +267,27 @@ template<typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
   return {Constant<Ty>{x}};
 }
 
+// Combine two expressions of the same specific numeric type with an operation
+// to produce a new expression.  Implements piecewise addition and subtraction
+// for COMPLEX.
+template<template<typename> class OPR, typename SPECIFIC>
+Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
+  static_assert(SPECIFIC::isSpecificType);
+  if constexpr (SPECIFIC::category == TypeCategory::Complex &&
+      (std::is_same_v<OPR<DefaultReal>, Add<DefaultReal>> ||
+          std::is_same_v<OPR<DefaultReal>, Subtract<DefaultReal>>)) {
+    static constexpr int kind{SPECIFIC::kind};
+    using Part = Type<TypeCategory::Real, kind>;
+    return AsExpr(
+        ComplexConstructor<kind>{OPR<Part>{ComplexComponent<kind>{false, x},
+                                     ComplexComponent<kind>{false, y}},
+            OPR<Part>{ComplexComponent<kind>{true, x},
+                ComplexComponent<kind>{true, y}}});
+  } else {
+    return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
+  }
+}
+
 // Given two expressions of arbitrary kind in the same intrinsic type
 // category, convert one of them if necessary to the larger kind of the
 // other, then combine the resulting homogenized operands with a given
@@ -278,34 +299,46 @@ Expr<SomeKind<CAT>> PromoteAndCombine(
       [](auto &&xy) {
         using Ty = ResultType<decltype(xy[0])>;
         return AsCategoryExpr(
-            AsExpr(OPR<Ty>{std::move(xy[0]), std::move(xy[1])}));
+            Combine<OPR, Ty>(std::move(xy[0]), std::move(xy[1])));
       },
       AsSameKindExprs(std::move(x), std::move(y)));
 }
 
 // Given two expressions of arbitrary type, try to combine them with a
 // binary numeric operation (e.g., Add), possibly with data type conversion of
-// one of the operands to the type of the other.
+// one of the operands to the type of the other.  Handles special cases with
+// typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
+// powers.
 template<template<typename> class OPR>
 std::optional<Expr<SomeType>> NumericOperation(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 
-extern template std::optional<Expr<SomeType>> NumericOperation<Add>(
-    parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
-extern template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
+extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
+extern template std::optional<Expr<SomeType>> NumericOperation<Add>(
+    parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
+extern template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
+    parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 
 std::optional<Expr<SomeType>> Negation(
     parser::ContextualMessages &, Expr<SomeType> &&);
 
+// Given two expressions of arbitrary type, try to combine them with a
+// relational operator (e.g., .LT.), possibly with data type conversion.
+std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &,
+    RelationalOperator, Expr<SomeType> &&, Expr<SomeType> &&);
+
+Expr<SomeLogical> BinaryLogicalOperation(
+    LogicalOperator, Expr<SomeLogical> &&, Expr<SomeLogical> &&);
+
 // Convenience functions and operator overloadings for expression construction.
-// These interfaces are defined only for those situations that cannot possibly
-// need to emit any messages.  Use the more general NumericOperation<>
-// template (above) in other situations.
+// These interfaces are defined only for those situations that can never
+// emit any message.  Use the more general templates (above) in other
+// situations.
 
 template<TypeCategory C, int K>
 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
@@ -322,22 +355,22 @@ Expr<Type<TypeCategory::Complex, K>> operator-(
 
 template<TypeCategory C, int K>
 Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
-  return {Add<Type<C, K>>{std::move(x), std::move(y)}};
+  return {Combine<Add, Type<C, K>>(std::move(x), std::move(y))};
 }
 
 template<TypeCategory C, int K>
 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
-  return {Subtract<Type<C, K>>{std::move(x), std::move(y)}};
+  return {Combine<Subtract, Type<C, K>>(std::move(x), std::move(y))};
 }
 
 template<TypeCategory C, int K>
 Expr<Type<C, K>> operator*(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
-  return {Multiply<Type<C, K>>{std::move(x), std::move(y)}};
+  return {Combine<Multiply, Type<C, K>>(std::move(x), std::move(y))};
 }
 
 template<TypeCategory C, int K>
 Expr<Type<C, K>> operator/(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
-  return {Divide<Type<C, K>>{std::move(x), std::move(y)}};
+  return {Combine<Divide, Type<C, K>>(std::move(x), std::move(y))};
 }
 
 template<TypeCategory C> Expr<SomeKind<C>> operator-(Expr<SomeKind<C>> &&x) {
index ddb8683..18d4810 100644 (file)
@@ -31,6 +31,7 @@ using common::TypeCategory;
 
 using MaybeExpr = std::optional<Expr<SomeType>>;
 
+// Utility subroutines for repackaging optional values.
 template<typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) {
   if (x.has_value()) {
     return {AsGenericExpr(AsCategoryExpr(AsExpr(std::move(*x))))};
@@ -38,18 +39,18 @@ template<typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) {
   return std::nullopt;
 }
 
-template<TypeCategory CAT, int KIND>
-MaybeExpr PackageGeneric(std::optional<Expr<Type<CAT, KIND>>> &&x) {
+template<TypeCategory CAT>
+MaybeExpr AsMaybeExpr(std::optional<Expr<SomeKind<CAT>>> &&x) {
   if (x.has_value()) {
-    return {AsGenericExpr(AsCategoryExpr(std::move(*x)))};
+    return {AsGenericExpr(std::move(*x))};
   }
   return std::nullopt;
 }
 
-template<TypeCategory CAT>
-MaybeExpr AsMaybeExpr(std::optional<Expr<SomeKind<CAT>>> &&x) {
+template<TypeCategory CAT, int KIND>
+MaybeExpr AsMaybeExpr(std::optional<Expr<Type<CAT, KIND>>> &&x) {
   if (x.has_value()) {
-    return {AsGenericExpr(std::move(*x))};
+    return {AsGenericExpr(AsCategoryExpr(std::move(*x)))};
   }
   return std::nullopt;
 }
@@ -511,12 +512,8 @@ MaybeExpr BinaryOperationHelper(ExprAnalyzer &ea, const PARSED &x) {
   return std::nullopt;
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Add &x) {
-  return BinaryOperationHelper<Add>(*this, x);
-}
-
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Subtract &x) {
-  return BinaryOperationHelper<Subtract>(*this, x);
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Power &x) {
+  return BinaryOperationHelper<Power>(*this, x);
 }
 
 MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Multiply &x) {
@@ -527,75 +524,102 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Divide &x) {
   return BinaryOperationHelper<Divide>(*this, x);
 }
 
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Add &x) {
+  return BinaryOperationHelper<Add>(*this, x);
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Subtract &x) {
+  return BinaryOperationHelper<Subtract>(*this, x);
+}
+
 MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::ComplexConstructor &x) {
   return AsMaybeExpr(ConstructComplex(context.messages,
       AnalyzeHelper(*this, *std::get<0>(x.t)),
       AnalyzeHelper(*this, *std::get<1>(x.t))));
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Power &) {
-  context.messages.Say("pmk: Power unimplemented\n"_err_en_US);
-  return std::nullopt;
-}
-
 MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Concat &) {
   context.messages.Say("pmk: Concat unimplemented\n"_err_en_US);
   return std::nullopt;
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LT &) {
-  context.messages.Say("pmk: .LT. unimplemented\n"_err_en_US);
+// TODO: check defined operators for illegal intrinsic operator cases
+template<typename PARSED>
+MaybeExpr RelationHelper(
+    ExprAnalyzer &ea, RelationalOperator opr, const PARSED &x) {
+  if (auto both{common::AllPresent(AnalyzeHelper(ea, *std::get<0>(x.t)),
+          AnalyzeHelper(ea, *std::get<1>(x.t)))}) {
+    return AsMaybeExpr(Relate(ea.context.messages, opr,
+        std::move(std::get<0>(*both)), std::move(std::get<1>(*both))));
+  }
   return std::nullopt;
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LE &) {
-  context.messages.Say("pmk: .LE. unimplemented\n"_err_en_US);
-  return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LT &x) {
+  return RelationHelper(*this, RelationalOperator::LT, x);
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQ &) {
-  context.messages.Say("pmk: .EQ. unimplemented\n"_err_en_US);
-  return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LE &x) {
+  return RelationHelper(*this, RelationalOperator::LE, x);
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NE &) {
-  context.messages.Say("pmk: .NE. unimplemented\n"_err_en_US);
-  return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQ &x) {
+  return RelationHelper(*this, RelationalOperator::EQ, x);
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GT &) {
-  context.messages.Say("pmk: .GT. unimplemented\n"_err_en_US);
-  return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NE &x) {
+  return RelationHelper(*this, RelationalOperator::NE, x);
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GE &) {
-  context.messages.Say("pmk: .GE. unimplemented\n"_err_en_US);
-  return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GE &x) {
+  return RelationHelper(*this, RelationalOperator::GE, x);
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::AND &) {
-  context.messages.Say("pmk: .AND. unimplemented\n"_err_en_US);
-  return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GT &x) {
+  return RelationHelper(*this, RelationalOperator::GT, x);
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::OR &) {
-  context.messages.Say("pmk: .OR. unimplemented\n"_err_en_US);
+// TODO: check defined operators for illegal intrinsic operator cases
+template<typename PARSED>
+MaybeExpr LogicalHelper(
+    ExprAnalyzer &ea, LogicalOperator opr, const PARSED &x) {
+  if (auto both{common::AllPresent(AnalyzeHelper(ea, *std::get<0>(x.t)),
+          AnalyzeHelper(ea, *std::get<1>(x.t)))}) {
+    return std::visit(
+        common::visitors{
+            [=](Expr<SomeLogical> &&lx, Expr<SomeLogical> &&ly) -> MaybeExpr {
+              return {AsGenericExpr(
+                  BinaryLogicalOperation(opr, std::move(lx), std::move(ly)))};
+            },
+            [&](auto &&, auto &&) -> MaybeExpr {
+              // TODO pmk: extensions: INTEGER and typeless operands
+              ea.context.messages.Say(
+                  "operands to LOGICAL operation must be LOGICAL"_err_en_US);
+              return {};
+            }},
+        std::move(std::get<0>(*both).u), std::move(std::get<1>(*both).u));
+  }
   return std::nullopt;
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQV &) {
-  context.messages.Say("pmk: .EQV. unimplemented\n"_err_en_US);
-  return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::AND &x) {
+  return LogicalHelper(*this, LogicalOperator::And, x);
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NEQV &) {
-  context.messages.Say("pmk: .NEQV. unimplemented\n"_err_en_US);
-  return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::OR &x) {
+  return LogicalHelper(*this, LogicalOperator::Or, x);
 }
 
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::XOR &) {
-  context.messages.Say("pmk: .XOR. unimplemented\n"_err_en_US);
-  return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQV &x) {
+  return LogicalHelper(*this, LogicalOperator::Eqv, x);
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NEQV &x) {
+  return LogicalHelper(*this, LogicalOperator::Neqv, x);
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::XOR &x) {
+  return LogicalHelper(*this, LogicalOperator::Neqv, x);
 }
 
 MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedBinary &) {
index 8144821..a1917a4 100644 (file)
@@ -284,7 +284,7 @@ std::ostream &operator<<(std::ostream &os, const ProcEntityDetails &x) {
 }
 
 std::ostream &operator<<(std::ostream &os, const DerivedTypeDetails &x) {
-  if (const Symbol *extends{x.extends()}) {
+  if (const Symbol * extends{x.extends()}) {
     os << " extends:" << extends->name();
   }
   if (x.sequence()) {