From e798ab7d9148638d9db5022f37b9603ad681df1f Mon Sep 17 00:00:00 2001
From: peter klausler <pklausler@nvidia.com>
Date: Thu, 16 Aug 2018 11:46:18 -0700
Subject: [PATCH] [flang] checkpoint with clang workaround

Original-commit: flang-compiler/f18@c4a2aaf8b10adc2aec0d66f020d358e2a2ac417b
Reviewed-on: https://github.com/flang-compiler/f18/pull/183
Tree-same-pre-rewrite: false
---
 flang/lib/common/idioms.h         |   6 +-
 flang/lib/evaluate/expression.cc  | 303 +++++++++-------------------
 flang/lib/evaluate/expression.h   | 315 +++++++++++++++---------------
 flang/lib/evaluate/type.h         |  12 +-
 flang/lib/semantics/expression.cc |  14 +-
 5 files changed, 263 insertions(+), 387 deletions(-)

diff --git a/flang/lib/common/idioms.h b/flang/lib/common/idioms.h
index 26044db3be88..1e58172af9b6 100644
--- a/flang/lib/common/idioms.h
+++ b/flang/lib/common/idioms.h
@@ -54,10 +54,10 @@ namespace Fortran::common {
 // Helper templates for combining a list of lambdas into an anonymous
 // struct for use with std::visit() on a std::variant<> sum type.
 // E.g.: std::visit(visitors{
-//         [&](const UnaryExpr &x) { ... },
-//         [&](const BinaryExpr &x) { ... },
+//         [&](const firstType &x) { ... },
+//         [&](const secondType &x) { ... },
 //         ...
-//       }, structure.unionMember);
+//         [&](const auto &catchAll) { ... }}, variantObject);
 
 template<typename... LAMBDAS> struct visitors : LAMBDAS... {
   using LAMBDAS::operator()...;
diff --git a/flang/lib/evaluate/expression.cc b/flang/lib/evaluate/expression.cc
index 7ffeefd4e73a..dd40567fa01f 100644
--- a/flang/lib/evaluate/expression.cc
+++ b/flang/lib/evaluate/expression.cc
@@ -33,9 +33,11 @@ std::ostream &Operation<D, R, O...>::Dump(std::ostream &o) const {
   if constexpr (operands() > 1) {
     operand<1>().Dump(o << infix_);
   }
-  return o << derived().postfix_;
+  return o << derived().suffix_;
 }
 
+// TODO: dump Convert<Integer,x> as INT(x,KIND=), &c.
+
 template<typename D, typename R, typename... O>
 auto Operation<D, R, O...>::Fold(FoldingContext &context)
     -> std::optional<Scalar<Result>> {
@@ -58,42 +60,32 @@ auto Convert<TO, FROM>::FoldScalar(FoldingContext &context,
     const Scalar<Operand> &c) -> std::optional<Scalar<Result>> {
   if constexpr (std::is_same_v<Result, Operand>) {
     return {c};
-  }
-  if constexpr (std::is_same_v<Result, SomeType>) {
+  } else if constexpr (std::is_same_v<Result, SomeType>) {
     using Generic = SomeKind<Operand::category>;
     if constexpr (std::is_same_v<Operand, Generic>) {
       return {Scalar<Result>{c}};
     } else {
       return {Scalar<Result>{Generic{c}}};
     }
-  }
-  if constexpr (std::is_same_v<Operand, SomeType>) {
+  } else if constexpr (std::is_same_v<Operand, SomeType>) {
     return std::visit(
         [&](const auto &x) -> std::optional<Scalar<Result>> {
-          return Convert<Result, std::decay_t<decltype(x)>>::FoldScalar(
-              context, x);
+          using Ty = std::decay_t<decltype(x)>;
+          return Convert<Result, Ty>::FoldScalar(context, x);
         },
         c.u);
-  }
-  // Result and Operand are distinct types with known categories.
-  if constexpr (std::is_same_v<Result, SomeKind<Result::category>>) {
+  } else if constexpr (std::is_same_v<Result, SomeKind<Result::category>>) {
     if constexpr (Result::category == Operand::category) {
       return {Scalar<Result>{c}};
     }
-    return std::nullopt;
-  }
-  // Result is a specific type.
-  if constexpr (std::is_same_v<Operand, SomeKind<Operand::category>>) {
+  } else if constexpr (std::is_same_v<Operand, SomeKind<Operand::category>>) {
     return std::visit(
         [&](const auto &x) -> std::optional<Scalar<Result>> {
-          return Convert<Result,
-              ScalarValueType<std::decay_t<decltype(x)>>>::FoldScalar(context,
-              x);
+          using Ty = ScalarValueType<std::decay_t<decltype(x)>>;
+          return Convert<Result, Ty>::FoldScalar(context, x);
         },
         c.u);
-  }
-  // Result and Operand are distinct specific types.
-  if constexpr (Result::category == TypeCategory::Integer) {
+  } else if constexpr (Result::category == TypeCategory::Integer) {
     if constexpr (Operand::category == TypeCategory::Integer) {
       auto converted{Scalar<Result>::ConvertSigned(c)};
       if (converted.overflow) {
@@ -101,8 +93,7 @@ auto Convert<TO, FROM>::FoldScalar(FoldingContext &context,
       } else {
         return {std::move(converted.value)};
       }
-    }
-    if constexpr (Operand::category == TypeCategory::Real) {
+    } else if constexpr (Operand::category == TypeCategory::Real) {
       auto converted{c.template ToInteger<Scalar<Result>>()};
       if (converted.flags.test(RealFlag::InvalidArgument)) {
         context.messages.Say(
@@ -113,14 +104,12 @@ auto Convert<TO, FROM>::FoldScalar(FoldingContext &context,
         return {std::move(converted.value)};
       }
     }
-  }
-  if constexpr (Result::category == TypeCategory::Real) {
+  } else if constexpr (Result::category == TypeCategory::Real) {
     if constexpr (Operand::category == TypeCategory::Integer) {
       auto converted{Scalar<Result>::FromInteger(c)};
       RealFlagWarnings(context, converted.flags, "INTEGER to REAL conversion");
       return {std::move(converted.value)};
-    }
-    if constexpr (Operand::category == TypeCategory::Real) {
+    } else if constexpr (Operand::category == TypeCategory::Real) {
       auto converted{Scalar<Result>::Convert(c)};
       RealFlagWarnings(context, converted.flags, "REAL to REAL conversion");
       return {std::move(converted.value)};
@@ -129,18 +118,35 @@ auto Convert<TO, FROM>::FoldScalar(FoldingContext &context,
   return std::nullopt;
 }
 
-// Dumping
-template<typename... A>
-std::ostream &DumpExprWithType(std::ostream &o, const std::variant<A...> &u) {
-  std::visit(
-      [&](const auto &x) {
-        using Ty = typename std::remove_reference_t<decltype(x)>::Result;
-        x.Dump(o << '(' << Ty::Dump() << "::") << ')';
-      },
-      u);
-  return o;
+template<typename A>
+auto Negate<A>::FoldScalar(FoldingContext &context, const Scalar<Operand> &c)
+    -> std::optional<Scalar<Result>> {
+  if constexpr (Result::category == TypeCategory::Integer) {
+    auto negated{c.Negate()};
+    if (negated.overflow) {
+      context.messages.Say("INTEGER negation overflowed"_en_US);
+    } else {
+      return {std::move(negated.value)};
+    }
+  } else {
+    return {c.Negate()};  // REAL & COMPLEX: no exceptions possible
+  }
+  return std::nullopt;
 }
 
+template<int KIND, bool R>
+auto ComplexComponent<KIND, R>::FoldScalar(FoldingContext &context,
+    const Scalar<Operand> &z) -> std::optional<Scalar<Result>> {
+  return {isRealPart ? z.REAL() : z.AIMAG()};
+}
+
+template<int KIND>
+auto Not<KIND>::FoldScalar(FoldingContext &context, const Scalar<Operand> &x)
+    -> std::optional<Scalar<Result>> {
+  return {Scalar<Result>{!x.IsTrue()}};
+}
+
+// Dumping
 template<typename... A>
 std::ostream &DumpExpr(std::ostream &o, const std::variant<A...> &u) {
   std::visit(common::visitors{[&](const BOZLiteralConstant &x) {
@@ -165,12 +171,6 @@ std::ostream &Expr<SomeType>::Dump(std::ostream &o) const {
   return DumpExpr(o, u);
 }
 
-template<typename CRTP, typename RESULT, typename A>
-std::ostream &Unary<CRTP, RESULT, A>::Dump(
-    std::ostream &o, const char *opr) const {
-  return operand().Dump(o << opr) << ')';
-}
-
 template<typename CRTP, typename RESULT, typename A, typename B>
 std::ostream &Binary<CRTP, RESULT, A, B>::Dump(
     std::ostream &o, const char *opr, const char *before) const {
@@ -184,8 +184,6 @@ std::ostream &Expr<Type<TypeCategory::Integer, KIND>>::Dump(
       common::visitors{[&](const Scalar<Result> &n) { o << n.SignedDecimal(); },
           [&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
           [&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
-          [&](const Parentheses<Result> &p) { p.Dump(o); },
-          [&](const Negate &n) { n.Dump(o, "(-"); },
           [&](const Add &a) { a.Dump(o, "+"); },
           [&](const Subtract &s) { s.Dump(o, "-"); },
           [&](const Multiply &m) { m.Dump(o, "*"); },
@@ -193,9 +191,7 @@ std::ostream &Expr<Type<TypeCategory::Integer, KIND>>::Dump(
           [&](const Power &p) { p.Dump(o, "**"); },
           [&](const Max &m) { m.Dump(o, ",", "MAX("); },
           [&](const Min &m) { m.Dump(o, ",", "MIN("); },
-          [&](const auto &convert) {
-            DumpExprWithType(o, convert.operand().u);
-          }},
+          [&](const auto &x) { x.Dump(o); }},
       u_);
   return o;
 }
@@ -209,8 +205,12 @@ std::ostream &Expr<Type<TypeCategory::Real, KIND>>::Dump(
                  [&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
                  [&](const CopyableIndirection<ComplexPart> &d) { d->Dump(o); },
                  [&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
+                 [&](const Convert<Result, SomeInteger> &c) { c.Dump(o); },
+                 [&](const Convert<Result, SomeReal> &c) { c.Dump(o); },
+                 [&](const ComplexComponent<KIND, true> &z) { z.Dump(o); },
+                 [&](const ComplexComponent<KIND, false> &z) { z.Dump(o); },
                  [&](const Parentheses<Result> &p) { p.Dump(o); },
-                 [&](const Negate &n) { n.Dump(o, "(-"); },
+                 [&](const Negate<Result> &n) { n.Dump(o); },
                  [&](const Add &a) { a.Dump(o, "+"); },
                  [&](const Subtract &s) { s.Dump(o, "-"); },
                  [&](const Multiply &m) { m.Dump(o, "*"); },
@@ -218,12 +218,7 @@ std::ostream &Expr<Type<TypeCategory::Real, KIND>>::Dump(
                  [&](const Power &p) { p.Dump(o, "**"); },
                  [&](const IntPower &p) { p.Dump(o, "**"); },
                  [&](const Max &m) { m.Dump(o, ",", "MAX("); },
-                 [&](const Min &m) { m.Dump(o, ",", "MIN("); },
-                 [&](const RealPart &z) { z.Dump(o, "REAL("); },
-                 [&](const AIMAG &p) { p.Dump(o, "AIMAG("); },
-                 [&](const auto &convert) {
-                   DumpExprWithType(o, convert.operand().u);
-                 }},
+                 [&](const Min &m) { m.Dump(o, ",", "MIN("); }},
       u_);
   return o;
 }
@@ -236,15 +231,14 @@ std::ostream &Expr<Type<TypeCategory::Complex, KIND>>::Dump(
                               },
                  [&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
                  [&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
-                 [&](const Parentheses<Result> &p) { p.Dump(o); },
-                 [&](const Negate &n) { n.Dump(o, "(-"); },
                  [&](const Add &a) { a.Dump(o, "+"); },
                  [&](const Subtract &s) { s.Dump(o, "-"); },
                  [&](const Multiply &m) { m.Dump(o, "*"); },
                  [&](const Divide &d) { d.Dump(o, "/"); },
                  [&](const Power &p) { p.Dump(o, "**"); },
                  [&](const IntPower &p) { p.Dump(o, "**"); },
-                 [&](const CMPLX &c) { c.Dump(o, ","); }},
+                 [&](const CMPLX &c) { c.Dump(o, ","); },
+                 [&](const auto &x) { x.Dump(o); }},
       u_);
   return o;
 }
@@ -280,7 +274,7 @@ std::ostream &Expr<Type<TypeCategory::Logical, KIND>>::Dump(
                  [&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
                  [&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
                  //          [&](const Parentheses<Result> &p) { p.Dump(o); },
-                 [&](const Not &n) { n.Dump(o, "(.NOT."); },
+                 [&](const Not<KIND> &n) { n.Dump(o); },
                  [&](const And &a) { a.Dump(o, ".AND."); },
                  [&](const Or &a) { a.Dump(o, ".OR."); },
                  [&](const Eqv &a) { a.Dump(o, ".EQV."); },
@@ -328,15 +322,6 @@ int Binary<CRTP, RESULT, A, B>::Rank() const {
 }
 
 // Folding
-template<typename CRTP, typename RESULT, typename A>
-auto Unary<CRTP, RESULT, A>::Fold(FoldingContext &context)
-    -> std::optional<Scalar<Result>> {
-  if (std::optional<Scalar<Operand>> c{operand_->Fold(context)}) {
-    return static_cast<CRTP *>(this)->FoldScalar(context, *c);
-  }
-  return std::nullopt;
-}
-
 template<typename CRTP, typename RESULT, typename A, typename B>
 auto Binary<CRTP, RESULT, A, B>::Fold(FoldingContext &context)
     -> std::optional<Scalar<Result>> {
@@ -348,55 +333,6 @@ auto Binary<CRTP, RESULT, A, B>::Fold(FoldingContext &context)
   return std::nullopt;
 }
 
-template<int KIND>
-auto Expr<Type<TypeCategory::Integer, KIND>>::ConvertInteger::FoldScalar(
-    FoldingContext &context, const SomeKindScalar<TypeCategory::Integer> &c)
-    -> std::optional<Scalar<Result>> {
-  return std::visit(
-      [&](auto &x) -> std::optional<Scalar<Result>> {
-        auto converted{Scalar<Result>::ConvertSigned(x)};
-        if (converted.overflow) {
-          context.messages.Say("integer conversion overflowed"_en_US);
-          return std::nullopt;
-        }
-        return {std::move(converted.value)};
-      },
-      c.u);
-}
-
-template<int KIND>
-auto Expr<Type<TypeCategory::Integer, KIND>>::ConvertReal::FoldScalar(
-    FoldingContext &context, const SomeKindScalar<TypeCategory::Real> &c)
-    -> std::optional<Scalar<Result>> {
-  return std::visit(
-      [&](auto &x) -> std::optional<Scalar<Result>> {
-        auto converted{x.template ToInteger<Scalar<Result>>()};
-        if (converted.flags.test(RealFlag::Overflow)) {
-          context.messages.Say("real->integer conversion overflowed"_en_US);
-          return std::nullopt;
-        }
-        if (converted.flags.test(RealFlag::InvalidArgument)) {
-          context.messages.Say(
-              "real->integer conversion: invalid argument"_en_US);
-          return std::nullopt;
-        }
-        return {std::move(converted.value)};
-      },
-      c.u);
-}
-
-template<int KIND>
-auto Expr<Type<TypeCategory::Integer, KIND>>::Negate::FoldScalar(
-    FoldingContext &context, const Scalar<Result> &c)
-    -> std::optional<Scalar<Result>> {
-  auto negated{c.Negate()};
-  if (negated.overflow) {
-    context.messages.Say("integer negation overflowed"_en_US);
-    return std::nullopt;
-  }
-  return {std::move(negated.value)};
-}
-
 template<int KIND>
 auto Expr<Type<TypeCategory::Integer, KIND>>::Add::FoldScalar(
     FoldingContext &context, const Scalar<Result> &a, const Scalar<Result> &b)
@@ -492,12 +428,12 @@ auto Expr<Type<TypeCategory::Integer, KIND>>::Min::FoldScalar(
 template<int KIND>
 auto Expr<Type<TypeCategory::Integer, KIND>>::Fold(FoldingContext &context)
     -> std::optional<Scalar<Result>> {
+  if (auto c{ScalarValue()}) {
+    return c;
+  }
   return std::visit(
       [&](auto &x) -> std::optional<Scalar<Result>> {
         using Ty = std::decay_t<decltype(x)>;
-        if constexpr (std::is_same_v<Ty, Scalar<Result>>) {
-          return {x};
-        }
         if constexpr (evaluate::FoldableTrait<Ty>) {
           if (auto c{x.Fold(context)}) {
             if constexpr (std::is_same_v<Ty, Parentheses<Result>>) {
@@ -514,39 +450,6 @@ auto Expr<Type<TypeCategory::Integer, KIND>>::Fold(FoldingContext &context)
       u_);
 }
 
-template<int KIND>
-auto Expr<Type<TypeCategory::Real, KIND>>::ConvertInteger::FoldScalar(
-    FoldingContext &context, const SomeKindScalar<TypeCategory::Integer> &c)
-    -> std::optional<Scalar<Result>> {
-  return std::visit(
-      [&](auto &x) -> std::optional<Scalar<Result>> {
-        auto converted{Scalar<Result>::FromInteger(x)};
-        RealFlagWarnings(context, converted.flags, "integer->real conversion");
-        return {std::move(converted.value)};
-      },
-      c.u);
-}
-
-template<int KIND>
-auto Expr<Type<TypeCategory::Real, KIND>>::ConvertReal::FoldScalar(
-    FoldingContext &context, const SomeKindScalar<TypeCategory::Real> &c)
-    -> std::optional<Scalar<Result>> {
-  return std::visit(
-      [&](auto &x) -> std::optional<Scalar<Result>> {
-        auto converted{Scalar<Result>::Convert(x)};
-        RealFlagWarnings(context, converted.flags, "real conversion");
-        return {std::move(converted.value)};
-      },
-      c.u);
-}
-
-template<int KIND>
-auto Expr<Type<TypeCategory::Real, KIND>>::Negate::FoldScalar(
-    FoldingContext &context, const Scalar<Result> &c)
-    -> std::optional<Scalar<Result>> {
-  return {c.Negate()};
-}
-
 template<int KIND>
 auto Expr<Type<TypeCategory::Real, KIND>>::Add::FoldScalar(
     FoldingContext &context, const Scalar<Result> &a, const Scalar<Result> &b)
@@ -624,37 +527,26 @@ auto Expr<Type<TypeCategory::Real, KIND>>::Min::FoldScalar(
   return {a};
 }
 
-template<int KIND>
-auto Expr<Type<TypeCategory::Real, KIND>>::RealPart::FoldScalar(
-    FoldingContext &context,
-    const Scalar<SameKind<TypeCategory::Complex, Result>> &z)
-    -> std::optional<Scalar<Result>> {
-  return {z.REAL()};
-}
-
-template<int KIND>
-auto Expr<Type<TypeCategory::Real, KIND>>::AIMAG::FoldScalar(
-    FoldingContext &context,
-    const Scalar<SameKind<TypeCategory::Complex, Result>> &z)
-    -> std::optional<Scalar<Result>> {
-  return {z.AIMAG()};
-}
-
 template<int KIND>
 auto Expr<Type<TypeCategory::Real, KIND>>::Fold(FoldingContext &context)
     -> std::optional<Scalar<Result>> {
+  if (auto c{ScalarValue()}) {
+    return c;
+  }
   return std::visit(
       [&](auto &x) -> std::optional<Scalar<Result>> {
         using Ty = std::decay_t<decltype(x)>;
-        if constexpr (std::is_same_v<Ty, Scalar<Result>>) {
-          return {x};
-        }
         if constexpr (evaluate::FoldableTrait<Ty>) {
           if (auto c{x.Fold(context)}) {
             if (context.flushDenormalsToZero) {
               *c = c->FlushDenormalToZero();
             }
-            u_ = *c;
+            if constexpr (std::is_same_v<Ty, Parentheses<Result>>) {
+              // Preserve parentheses around constants.
+              u_ = Parentheses<Result>{Expr{*c}};
+            } else {
+              u_ = *c;
+            }
             return c;
           }
         }
@@ -663,13 +555,6 @@ auto Expr<Type<TypeCategory::Real, KIND>>::Fold(FoldingContext &context)
       u_);
 }
 
-template<int KIND>
-auto Expr<Type<TypeCategory::Complex, KIND>>::Negate::FoldScalar(
-    FoldingContext &context, const Scalar<Result> &c)
-    -> std::optional<Scalar<Result>> {
-  return {c.Negate()};
-}
-
 template<int KIND>
 auto Expr<Type<TypeCategory::Complex, KIND>>::Add::FoldScalar(
     FoldingContext &context, const Scalar<Result> &a, const Scalar<Result> &b)
@@ -739,18 +624,23 @@ auto Expr<Type<TypeCategory::Complex, KIND>>::CMPLX::FoldScalar(
 template<int KIND>
 auto Expr<Type<TypeCategory::Complex, KIND>>::Fold(FoldingContext &context)
     -> std::optional<Scalar<Result>> {
+  if (auto c{ScalarValue()}) {
+    return c;
+  }
   return std::visit(
       [&](auto &x) -> std::optional<Scalar<Result>> {
         using Ty = std::decay_t<decltype(x)>;
-        if constexpr (std::is_same_v<Ty, Scalar<Result>>) {
-          return {x};
-        }
         if constexpr (evaluate::FoldableTrait<Ty>) {
           if (auto c{x.Fold(context)}) {
             if (context.flushDenormalsToZero) {
               *c = c->FlushDenormalToZero();
             }
-            u_ = *c;
+            if constexpr (std::is_same_v<Ty, Parentheses<Result>>) {
+              // Preserve parentheses around constants.
+              u_ = Parentheses<Result>{Expr{*c}};
+            } else {
+              u_ = *c;
+            }
             return c;
           }
         }
@@ -792,12 +682,12 @@ auto Expr<Type<TypeCategory::Character, KIND>>::Min::FoldScalar(
 template<int KIND>
 auto Expr<Type<TypeCategory::Character, KIND>>::Fold(FoldingContext &context)
     -> std::optional<Scalar<Result>> {
+  if (auto c{ScalarValue()}) {
+    return c;
+  }
   return std::visit(
       [&](auto &x) -> std::optional<Scalar<Result>> {
         using Ty = std::decay_t<decltype(x)>;
-        if constexpr (std::is_same_v<Ty, Scalar<Result>>) {
-          return {x};
-        }
         if constexpr (evaluate::FoldableTrait<Ty>) {
           if (auto c{x.Fold(context)}) {
             u_ = *c;
@@ -860,13 +750,6 @@ auto Comparison<A>::FoldScalar(FoldingContext &c, const Scalar<Operand> &a,
   return std::nullopt;
 }
 
-template<int KIND>
-auto Expr<Type<TypeCategory::Logical, KIND>>::Not::FoldScalar(
-    FoldingContext &context, const Scalar<Result> &x)
-    -> std::optional<Scalar<Result>> {
-  return {Scalar<Result>{!x.IsTrue()}};
-}
-
 template<int KIND>
 auto Expr<Type<TypeCategory::Logical, KIND>>::And::FoldScalar(
     FoldingContext &context, const Scalar<Result> &a, const Scalar<Result> &b)
@@ -898,12 +781,12 @@ auto Expr<Type<TypeCategory::Logical, KIND>>::Neqv::FoldScalar(
 template<int KIND>
 auto Expr<Type<TypeCategory::Logical, KIND>>::Fold(FoldingContext &context)
     -> std::optional<Scalar<Result>> {
+  if (auto c{ScalarValue()}) {
+    return c;
+  }
   return std::visit(
       [&](auto &x) -> std::optional<Scalar<Result>> {
         using Ty = std::decay_t<decltype(x)>;
-        if constexpr (std::is_same_v<Ty, Scalar<Result>>) {
-          return {x};
-        }
         if constexpr (evaluate::FoldableTrait<Ty>) {
           if (auto c{x.Fold(context)}) {
             u_ = *c;
@@ -989,13 +872,6 @@ int Expr<SomeType>::Rank() const {
       u);
 }
 
-template class Expr<SomeType>;
-template class Expr<SomeKind<TypeCategory::Integer>>;
-template class Expr<SomeKind<TypeCategory::Real>>;
-template class Expr<SomeKind<TypeCategory::Complex>>;
-template class Expr<SomeKind<TypeCategory::Character>>;
-template class Expr<SomeKind<TypeCategory::Logical>>;
-
 template class Expr<Type<TypeCategory::Integer, 1>>;
 template class Expr<Type<TypeCategory::Integer, 2>>;
 template class Expr<Type<TypeCategory::Integer, 4>>;
@@ -1011,11 +887,7 @@ template class Expr<Type<TypeCategory::Complex, 4>>;
 template class Expr<Type<TypeCategory::Complex, 8>>;
 template class Expr<Type<TypeCategory::Complex, 10>>;
 template class Expr<Type<TypeCategory::Complex, 16>>;
-template class Expr<Type<TypeCategory::Character, 1>>;
-template class Expr<Type<TypeCategory::Logical, 1>>;
-template class Expr<Type<TypeCategory::Logical, 2>>;
-template class Expr<Type<TypeCategory::Logical, 4>>;
-template class Expr<Type<TypeCategory::Logical, 8>>;
+template class Expr<Type<TypeCategory::Character, 1>>;  // TODO others
 
 template struct Comparison<Type<TypeCategory::Integer, 1>>;
 template struct Comparison<Type<TypeCategory::Integer, 2>>;
@@ -1032,5 +904,18 @@ template struct Comparison<Type<TypeCategory::Complex, 4>>;
 template struct Comparison<Type<TypeCategory::Complex, 8>>;
 template struct Comparison<Type<TypeCategory::Complex, 10>>;
 template struct Comparison<Type<TypeCategory::Complex, 16>>;
-template struct Comparison<Type<TypeCategory::Character, 1>>;
+template struct Comparison<Type<TypeCategory::Character, 1>>;  // TODO others
+
+template class Expr<Type<TypeCategory::Logical, 1>>;
+template class Expr<Type<TypeCategory::Logical, 2>>;
+template class Expr<Type<TypeCategory::Logical, 4>>;
+template class Expr<Type<TypeCategory::Logical, 8>>;
+
+template class Expr<SomeInteger>;
+template class Expr<SomeReal>;
+template class Expr<SomeComplex>;
+template class Expr<SomeCharacter>;
+template class Expr<SomeLogical>;
+
+template class Expr<SomeType>;
 }  // namespace Fortran::evaluate
diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h
index a8e2beaaedb9..54c03bd450b6 100644
--- a/flang/lib/evaluate/expression.h
+++ b/flang/lib/evaluate/expression.h
@@ -46,7 +46,19 @@ public:
   template<int J> using Operand = std::tuple_element_t<J, OperandTypes>;
   using FoldableTrait = std::true_type;
 
+  static_assert(Result::kind > 0);  // Operations have specific Result types
+
+#if !__clang__
   CLASS_BOILERPLATE(Operation)
+#else  // clang 6.0 erroneously deletes the "=default" copy constructor
+  Operation() = delete;
+  Operation(const Operation &that) : operand_{that.operand_} {}
+  Operation(Operation &&) = default;
+  Operation &operator=(const Operation &) = default;
+  Operation &operator=(Operation &&) = default;
+  std::ostream &Dump(std::ostream &) const;
+#endif
+
   Operation(Expr<OPERAND> &&... x) : operand_{std::move(x)...} {}
   Operation(const Expr<OPERAND> &... x) : operand_{x...} {}
 
@@ -63,16 +75,25 @@ public:
 
 protected:
   // Overridable strings for Dump()
-  static constexpr const char *prefix_{"("}, *infix_{""}, *postfix_{")"};
+  static constexpr const char *prefix_{"("}, *infix_{""}, *suffix_{")"};
 
 private:
   std::tuple<CopyableIndirection<Expr<OPERAND>>...> operand_;
 };
 
+template<typename TO, typename FROM>
+struct Convert : public Operation<Convert<TO, FROM>, TO, FROM> {
+  using Base = Operation<Convert<TO, FROM>, TO, FROM>;
+  using Base::Base;
+  using typename Base::Result;
+  using Operand = typename Base::template Operand<0>;
+  static std::optional<Scalar<Result>> FoldScalar(
+      FoldingContext &, const Scalar<Operand> &);
+};
+
 template<typename A>
-class Parentheses : public Operation<Parentheses<A>, A, A> {
+struct Parentheses : public Operation<Parentheses<A>, A, A> {
   using Base = Operation<Parentheses, A, A>;
-  friend Base;
   using Base::Base;
   using typename Base::Result;
   using Operand = typename Base::template Operand<0>;
@@ -82,34 +103,44 @@ class Parentheses : public Operation<Parentheses<A>, A, A> {
   }
 };
 
-template<typename TO, typename FROM>
-class Convert : public Operation<Convert<TO, FROM>, TO, FROM> {
-  using Base = Operation<Convert<TO, FROM>, TO, FROM>;
-  friend Base;
+template<typename A> struct Negate : public Operation<Negate<A>, A, A> {
+  using Base = Operation<Negate, A, A>;
   using Base::Base;
   using typename Base::Result;
   using Operand = typename Base::template Operand<0>;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &);
+  static constexpr const char *prefix_{"(-"};
 };
 
-// Helper base classes for packaging subexpressions.
-template<typename CRTP, typename RESULT, typename A = RESULT> class Unary {
-public:
-  using Result = RESULT;
-  using Operand = A;
-  using FoldableTrait = std::true_type;
-  CLASS_BOILERPLATE(Unary)
-  Unary(const Expr<Operand> &a) : operand_{a} {}
-  Unary(Expr<Operand> &&a) : operand_{std::move(a)} {}
-  Unary(CopyableIndirection<Expr<Operand>> &&a) : operand_{std::move(a)} {}
-  const Expr<Operand> &operand() const { return *operand_; }
-  Expr<Operand> &operand() { return *operand_; }
-  std::ostream &Dump(std::ostream &, const char *opr) const;
-  int Rank() const { return operand_.Rank(); }
-  std::optional<Scalar<Result>> Fold(FoldingContext &);  // TODO: array result
-private:
-  CopyableIndirection<Expr<Operand>> operand_;
+// TODO: re vs. im can be dynamic
+template<int KIND, bool realPart = true>
+struct ComplexComponent
+  : public Operation<ComplexComponent<KIND, realPart>,
+        Type<TypeCategory::Real, KIND>, Type<TypeCategory::Complex, KIND>> {
+  using Base = Operation<ComplexComponent, Type<TypeCategory::Real, KIND>,
+      Type<TypeCategory::Complex, KIND>>;
+  static constexpr bool isRealPart{realPart};
+  using Base::Base;
+  using typename Base::Result;
+  using Operand = typename Base::template Operand<0>;
+  static std::optional<Scalar<Result>> FoldScalar(
+      FoldingContext &, const Scalar<Operand> &);
+  static constexpr const char *prefix_{"(("};
+  static constexpr const char *suffix_{isRealPart ? ")%RE)" : ")%IM)"};
+};
+
+template<int KIND>
+struct Not : public Operation<Not<KIND>, Type<TypeCategory::Logical, KIND>,
+                 Type<TypeCategory::Logical, KIND>> {
+  using Base = Operation<Not, Type<TypeCategory::Logical, KIND>,
+      Type<TypeCategory::Logical, KIND>>;
+  using Base::Base;
+  using typename Base::Result;
+  using Operand = typename Base::template Operand<0>;
+  static std::optional<Scalar<Result>> FoldScalar(
+      FoldingContext &, const Scalar<Operand> &);
+  static constexpr const char *prefix_{"(.NOT."};
 };
 
 template<typename CRTP, typename RESULT, typename A = RESULT, typename B = A>
@@ -147,25 +178,7 @@ public:
   using Result = Type<TypeCategory::Integer, KIND>;
   using FoldableTrait = std::true_type;
 
-  struct ConvertInteger : public Unary<ConvertInteger, Result, SomeInteger> {
-    using Unary<ConvertInteger, Result, SomeInteger>::Unary;
-    static std::optional<Scalar<Result>> FoldScalar(
-        FoldingContext &, const SomeKindScalar<TypeCategory::Integer> &);
-  };
-
-  struct ConvertReal : public Unary<ConvertReal, Result, SomeReal> {
-    using Unary<ConvertReal, Result, SomeReal>::Unary;
-    static std::optional<Scalar<Result>> FoldScalar(
-        FoldingContext &, const SomeKindScalar<TypeCategory::Real> &);
-  };
-
-  template<typename CRTP> using Un = Unary<CRTP, Result>;
   template<typename CRTP> using Bin = Binary<CRTP, Result>;
-  struct Negate : public Un<Negate> {
-    using Un<Negate>::Un;
-    static std::optional<Scalar<Result>> FoldScalar(
-        FoldingContext &, const Scalar<Result> &);
-  };
   struct Add : public Bin<Add> {
     using Bin<Add>::Bin;
     static std::optional<Scalar<Result>> FoldScalar(
@@ -208,31 +221,30 @@ public:
   Expr(std::int64_t n) : u_{Scalar<Result>{n}} {}
   Expr(std::uint64_t n) : u_{Scalar<Result>{n}} {}
   Expr(int n) : u_{Scalar<Result>{n}} {}
-  Expr(const Expr<SomeInteger> &x) : u_{ConvertInteger{x}} {}
-  Expr(Expr<SomeInteger> &&x) : u_{ConvertInteger{std::move(x)}} {}
+  Expr(const Expr<SomeInteger> &x) : u_{Convert<Result, SomeInteger>{x}} {}
+  Expr(Expr<SomeInteger> &&x)
+    : u_{Convert<Result, SomeInteger>{std::move(x)}} {}
   template<int K>
   Expr(const Expr<Type<TypeCategory::Integer, K>> &x)
-    : u_{ConvertInteger{Expr<SomeInteger>{x}}} {}
+    : u_{Convert<Result, SomeInteger>{Expr<SomeInteger>{x}}} {}
   template<int K>
   Expr(Expr<Type<TypeCategory::Integer, K>> &&x)
-    : u_{ConvertInteger{Expr<SomeInteger>{std::move(x)}}} {}
-  Expr(const Expr<SomeReal> &x) : u_{ConvertReal{x}} {}
-  Expr(Expr<SomeReal> &&x) : u_{ConvertReal{std::move(x)}} {}
+    : u_{Convert<Result, SomeInteger>{Expr<SomeInteger>{std::move(x)}}} {}
+  Expr(const Expr<SomeReal> &x) : u_{Convert<Result, SomeReal>{x}} {}
+  Expr(Expr<SomeReal> &&x) : u_{Convert<Result, SomeReal>{std::move(x)}} {}
   template<int K>
   Expr(const Expr<Type<TypeCategory::Real, K>> &x)
-    : u_{ConvertReal{Expr<SomeReal>{x}}} {}
+    : u_{Convert<Result, SomeReal>{Expr<SomeReal>{x}}} {}
   template<int K>
   Expr(Expr<Type<TypeCategory::Real, K>> &&x)
-    : u_{ConvertReal{Expr<SomeReal>{std::move(x)}}} {}
+    : u_{Convert<Result, SomeReal>{Expr<SomeReal>{std::move(x)}}} {}
   template<typename A> Expr(const A &x) : u_{x} {}
   template<typename A>
-  Expr(std::enable_if_t<!std::is_reference_v<A> &&
-          (std::is_base_of_v<Un, A> || std::is_base_of_v<Bin, A>),
-      A> &&x)
-    : u_(std::move(x)) {}
+  Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_(std::move(x)) {}
   template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
 
   std::optional<Scalar<Result>> ScalarValue() const {
+    // TODO: Also succeed when parenthesized constant
     return common::GetIf<Scalar<Result>>(u_);
   }
   std::optional<Scalar<Result>> Fold(FoldingContext &c);
@@ -240,9 +252,9 @@ public:
 
 private:
   std::variant<Scalar<Result>, CopyableIndirection<DataRef>,
-      CopyableIndirection<FunctionRef>, ConvertInteger, ConvertReal,
-      Parentheses<Result>, Negate, Add, Subtract, Multiply, Divide, Power, Max,
-      Min>
+      CopyableIndirection<FunctionRef>, Convert<Result, SomeInteger>,
+      Convert<Result, SomeReal>, Parentheses<Result>, Negate<Result>, Add,
+      Subtract, Multiply, Divide, Power, Max, Min>
       u_;
 };
 
@@ -254,23 +266,8 @@ public:
   // 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.
-  struct ConvertInteger : public Unary<ConvertInteger, Result, SomeInteger> {
-    using Unary<ConvertInteger, Result, SomeInteger>::Unary;
-    static std::optional<Scalar<Result>> FoldScalar(
-        FoldingContext &, const SomeKindScalar<TypeCategory::Integer> &);
-  };
-  struct ConvertReal : public Unary<ConvertReal, Result, SomeReal> {
-    using Unary<ConvertReal, Result, SomeReal>::Unary;
-    static std::optional<Scalar<Result>> FoldScalar(
-        FoldingContext &, const SomeKindScalar<TypeCategory::Real> &);
-  };
-  template<typename CRTP> using Un = Unary<CRTP, Result>;
+
   template<typename CRTP> using Bin = Binary<CRTP, Result>;
-  struct Negate : public Un<Negate> {
-    using Un<Negate>::Un;
-    static std::optional<Scalar<Result>> FoldScalar(
-        FoldingContext &, const Scalar<Result> &);
-  };
   struct Add : public Bin<Add> {
     using Bin<Add>::Bin;
     static std::optional<Scalar<Result>> FoldScalar(
@@ -311,44 +308,33 @@ public:
     static std::optional<Scalar<Result>> FoldScalar(
         FoldingContext &, const Scalar<Result> &, const Scalar<Result> &);
   };
-  template<typename CRTP>
-  using ComplexUn =
-      Unary<CRTP, Result, SameKind<TypeCategory::Complex, Result>>;
-  struct RealPart : public ComplexUn<RealPart> {
-    using ComplexUn<RealPart>::ComplexUn;
-    static std::optional<Scalar<Result>> FoldScalar(FoldingContext &,
-        const Scalar<SameKind<TypeCategory::Complex, Result>> &);
-  };
-  struct AIMAG : public ComplexUn<AIMAG> {
-    using ComplexUn<AIMAG>::ComplexUn;
-    static std::optional<Scalar<Result>> FoldScalar(FoldingContext &,
-        const Scalar<SameKind<TypeCategory::Complex, Result>> &);
-  };
 
   CLASS_BOILERPLATE(Expr)
   Expr(const Scalar<Result> &x) : u_{x} {}
-  Expr(const Expr<SomeInteger> &x) : u_{ConvertInteger{x}} {}
-  Expr(Expr<SomeInteger> &&x) : u_{ConvertInteger{std::move(x)}} {}
+  Expr(const Expr<SomeInteger> &x) : u_{Convert<Result, SomeInteger>{x}} {}
+  Expr(Expr<SomeInteger> &&x)
+    : u_{Convert<Result, SomeInteger>{std::move(x)}} {}
   template<int K>
   Expr(const Expr<Type<TypeCategory::Integer, K>> &x)
-    : u_{ConvertInteger{Expr<SomeInteger>{x}}} {}
+    : u_{Convert<Result, SomeInteger>{Expr<SomeInteger>{x}}} {}
   template<int K>
   Expr(Expr<Type<TypeCategory::Integer, K>> &&x)
-    : u_{ConvertInteger{Expr<SomeInteger>{std::move(x)}}} {}
-  Expr(const Expr<SomeReal> &x) : u_{ConvertReal{x}} {}
-  Expr(Expr<SomeReal> &&x) : u_{ConvertReal{std::move(x)}} {}
+    : u_{Convert<Result, SomeInteger>{Expr<SomeInteger>{std::move(x)}}} {}
+  Expr(const Expr<SomeReal> &x) : u_{Convert<Result, SomeReal>{x}} {}
+  Expr(Expr<SomeReal> &&x) : u_{Convert<Result, SomeReal>{std::move(x)}} {}
   template<int K>
   Expr(const Expr<Type<TypeCategory::Real, K>> &x)
-    : u_{ConvertReal{Expr<SomeReal>{x}}} {}
+    : u_{Convert<Result, SomeReal>{Expr<SomeReal>{x}}} {}
   template<int K>
   Expr(Expr<Type<TypeCategory::Real, K>> &&x)
-    : u_{ConvertReal{Expr<SomeReal>{std::move(x)}}} {}
+    : u_{Convert<Result, SomeReal>{Expr<SomeReal>{std::move(x)}}} {}
   template<typename A> Expr(const A &x) : u_{x} {}
   template<typename A>
   Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_{std::move(x)} {}
   template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
 
   std::optional<Scalar<Result>> ScalarValue() const {
+    // TODO: parenthesized constants too
     return common::GetIf<Scalar<Result>>(u_);
   }
   std::optional<Scalar<Result>> Fold(FoldingContext &c);
@@ -357,8 +343,10 @@ public:
 private:
   std::variant<Scalar<Result>, CopyableIndirection<DataRef>,
       CopyableIndirection<ComplexPart>, CopyableIndirection<FunctionRef>,
-      ConvertInteger, ConvertReal, Parentheses<Result>, Negate, Add, Subtract,
-      Multiply, Divide, Power, IntPower, Max, Min, RealPart, AIMAG>
+      Convert<Result, SomeInteger>, Convert<Result, SomeReal>,
+      ComplexComponent<KIND, true>, ComplexComponent<KIND, false>,
+      Parentheses<Result>, Negate<Result>, Add, Subtract, Multiply, Divide,
+      Power, IntPower, Max, Min>
       u_;
 };
 
@@ -367,13 +355,7 @@ public:
   using Result = Type<TypeCategory::Complex, KIND>;
   using FoldableTrait = std::true_type;
 
-  template<typename CRTP> using Un = Unary<CRTP, Result>;
   template<typename CRTP> using Bin = Binary<CRTP, Result>;
-  struct Negate : public Un<Negate> {
-    using Un<Negate>::Un;
-    static std::optional<Scalar<Result>> FoldScalar(
-        FoldingContext &, const Scalar<Result> &);
-  };
   struct Add : public Bin<Add> {
     using Bin<Add>::Bin;
     static std::optional<Scalar<Result>> FoldScalar(
@@ -420,6 +402,7 @@ public:
   template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
 
   std::optional<Scalar<Result>> ScalarValue() const {
+    // TODO: parenthesized constants too
     return common::GetIf<Scalar<Result>>(u_);
   }
   std::optional<Scalar<Result>> Fold(FoldingContext &c);
@@ -427,11 +410,27 @@ public:
 
 private:
   std::variant<Scalar<Result>, CopyableIndirection<DataRef>,
-      CopyableIndirection<FunctionRef>, Parentheses<Result>, Negate, Add,
-      Subtract, Multiply, Divide, Power, IntPower, CMPLX>
+      CopyableIndirection<FunctionRef>, Parentheses<Result>, Negate<Result>,
+      Add, Subtract, Multiply, Divide, Power, IntPower, CMPLX>
       u_;
 };
 
+extern template class Expr<Type<TypeCategory::Integer, 1>>;
+extern template class Expr<Type<TypeCategory::Integer, 2>>;
+extern template class Expr<Type<TypeCategory::Integer, 4>>;
+extern template class Expr<Type<TypeCategory::Integer, 8>>;
+extern template class Expr<Type<TypeCategory::Integer, 16>>;
+extern template class Expr<Type<TypeCategory::Real, 2>>;
+extern template class Expr<Type<TypeCategory::Real, 4>>;
+extern template class Expr<Type<TypeCategory::Real, 8>>;
+extern template class Expr<Type<TypeCategory::Real, 10>>;
+extern template class Expr<Type<TypeCategory::Real, 16>>;
+extern template class Expr<Type<TypeCategory::Complex, 2>>;
+extern template class Expr<Type<TypeCategory::Complex, 4>>;
+extern template class Expr<Type<TypeCategory::Complex, 8>>;
+extern template class Expr<Type<TypeCategory::Complex, 10>>;
+extern template class Expr<Type<TypeCategory::Complex, 16>>;
+
 template<int KIND> class Expr<Type<TypeCategory::Character, KIND>> {
 public:
   using Result = Type<TypeCategory::Character, KIND>;
@@ -462,6 +461,7 @@ public:
   template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
 
   std::optional<Scalar<Result>> ScalarValue() const {
+    // TODO: parenthesized constants too
     return common::GetIf<Scalar<Result>>(u_);
   }
   std::optional<Scalar<Result>> Fold(FoldingContext &c);
@@ -482,9 +482,8 @@ private:
 ENUM_CLASS(RelationalOperator, LT, LE, EQ, NE, GE, GT)
 
 template<typename A>
-struct Comparison
-  : public Binary<Comparison<A>, Type<TypeCategory::Logical, 1>, A> {
-  using Result = Type<TypeCategory::Logical, 1>;
+struct Comparison : public Binary<Comparison<A>, LogicalResult, A> {
+  using Result = LogicalResult;
   using Operand = A;
   using Base = Binary<Comparison, Result, Operand>;
   CLASS_BOILERPLATE(Comparison)
@@ -498,27 +497,10 @@ struct Comparison
   RelationalOperator opr;
 };
 
-extern template struct Comparison<Type<TypeCategory::Integer, 1>>;
-extern template struct Comparison<Type<TypeCategory::Integer, 2>>;
-extern template struct Comparison<Type<TypeCategory::Integer, 4>>;
-extern template struct Comparison<Type<TypeCategory::Integer, 8>>;
-extern template struct Comparison<Type<TypeCategory::Integer, 16>>;
-extern template struct Comparison<Type<TypeCategory::Real, 2>>;
-extern template struct Comparison<Type<TypeCategory::Real, 4>>;
-extern template struct Comparison<Type<TypeCategory::Real, 8>>;
-extern template struct Comparison<Type<TypeCategory::Real, 10>>;
-extern template struct Comparison<Type<TypeCategory::Real, 16>>;
-extern template struct Comparison<Type<TypeCategory::Complex, 2>>;
-extern template struct Comparison<Type<TypeCategory::Complex, 4>>;
-extern template struct Comparison<Type<TypeCategory::Complex, 8>>;
-extern template struct Comparison<Type<TypeCategory::Complex, 10>>;
-extern template struct Comparison<Type<TypeCategory::Complex, 16>>;
-extern template struct Comparison<Type<TypeCategory::Character, 1>>;
-
 // Dynamically polymorphic comparisons whose operands are expressions of
 // the same supported kind of a particular type category.
 template<TypeCategory CAT> struct CategoryComparison {
-  using Result = Type<TypeCategory::Logical, 1>;
+  using Result = LogicalResult;
   CLASS_BOILERPLATE(CategoryComparison)
   template<int KIND> using KindComparison = Comparison<Type<CAT, KIND>>;
   template<int KIND> CategoryComparison(const KindComparison<KIND> &x) : u{x} {}
@@ -526,18 +508,14 @@ template<TypeCategory CAT> struct CategoryComparison {
   CategoryComparison(KindComparison<KIND> &&x) : u{std::move(x)} {}
   std::optional<Scalar<Result>> Fold(FoldingContext &c);
   int Rank() const { return 1; }  // TODO
-  typename KindsVariant<CAT, KindComparison>::type u;
+
+  KindsVariant<CAT, KindComparison> u;
 };
 
 template<int KIND> class Expr<Type<TypeCategory::Logical, KIND>> {
 public:
   using Result = Type<TypeCategory::Logical, KIND>;
   using FoldableTrait = std::true_type;
-  struct Not : Unary<Not, Result> {
-    using Unary<Not, Result>::Unary;
-    static std::optional<Scalar<Result>> FoldScalar(
-        FoldingContext &, const Scalar<Result> &);
-  };
   template<typename CRTP> using Bin = Binary<CRTP, Result>;
   struct And : public Bin<And> {
     using Bin<And>::Bin;
@@ -574,6 +552,7 @@ public:
   template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
 
   std::optional<Scalar<Result>> ScalarValue() const {
+    // TODO: parenthesized constants too
     return common::GetIf<Scalar<Result>>(u_);
   }
   std::optional<Scalar<Result>> Fold(FoldingContext &c);
@@ -583,34 +562,13 @@ private:
   std::variant<Scalar<Result>, CopyableIndirection<DataRef>,
       CopyableIndirection<FunctionRef>,
       //      Parentheses<Result>,
-      Not, And, Or, Eqv, Neqv, CategoryComparison<TypeCategory::Integer>,
+      Not<KIND>, And, Or, Eqv, Neqv, CategoryComparison<TypeCategory::Integer>,
       CategoryComparison<TypeCategory::Real>,
       CategoryComparison<TypeCategory::Complex>,
       CategoryComparison<TypeCategory::Character>>
       u_;
 };
 
-extern template class Expr<Type<TypeCategory::Integer, 1>>;
-extern template class Expr<Type<TypeCategory::Integer, 2>>;
-extern template class Expr<Type<TypeCategory::Integer, 4>>;
-extern template class Expr<Type<TypeCategory::Integer, 8>>;
-extern template class Expr<Type<TypeCategory::Integer, 16>>;
-extern template class Expr<Type<TypeCategory::Real, 2>>;
-extern template class Expr<Type<TypeCategory::Real, 4>>;
-extern template class Expr<Type<TypeCategory::Real, 8>>;
-extern template class Expr<Type<TypeCategory::Real, 10>>;
-extern template class Expr<Type<TypeCategory::Real, 16>>;
-extern template class Expr<Type<TypeCategory::Complex, 2>>;
-extern template class Expr<Type<TypeCategory::Complex, 4>>;
-extern template class Expr<Type<TypeCategory::Complex, 8>>;
-extern template class Expr<Type<TypeCategory::Complex, 10>>;
-extern template class Expr<Type<TypeCategory::Complex, 16>>;
-extern template class Expr<Type<TypeCategory::Character, 1>>;
-extern template class Expr<Type<TypeCategory::Logical, 1>>;
-extern template class Expr<Type<TypeCategory::Logical, 2>>;
-extern template class Expr<Type<TypeCategory::Logical, 4>>;
-extern template class Expr<Type<TypeCategory::Logical, 8>>;
-
 // Dynamically polymorphic expressions that can hold any supported kind
 // of a specific intrinsic type category.
 template<TypeCategory CAT> class Expr<SomeKind<CAT>> {
@@ -618,20 +576,16 @@ public:
   using Result = SomeKind<CAT>;
   using FoldableTrait = std::true_type;
   CLASS_BOILERPLATE(Expr)
+
   template<int KIND> using KindExpr = Expr<Type<CAT, KIND>>;
   template<int KIND> Expr(const KindExpr<KIND> &x) : u{x} {}
   template<int KIND> Expr(KindExpr<KIND> &&x) : u{std::move(x)} {}
   std::optional<Scalar<Result>> ScalarValue() const;
   std::optional<Scalar<Result>> Fold(FoldingContext &);
   int Rank() const;
-  typename KindsVariant<CAT, KindExpr>::type u;
-};
 
-extern template class Expr<SomeInteger>;
-extern template class Expr<SomeReal>;
-extern template class Expr<SomeComplex>;
-extern template class Expr<SomeCharacter>;
-extern template class Expr<SomeLogical>;
+  KindsVariant<CAT, KindExpr> u;
+};
 
 // BOZ literal constants need to be wide enough to hold an integer or real
 // value of any supported kind.  They also need to be distinguishable from
@@ -666,7 +620,6 @@ public:
       u;
 };
 
-extern template class Expr<SomeType>;
 using GenericExpr = Expr<SomeType>;  // TODO: delete name?
 
 template<typename A> using ResultType = typename std::decay_t<A>::Result;
@@ -675,7 +628,15 @@ template<typename A> using ResultType = typename std::decay_t<A>::Result;
 // These definitions are created with temporary helper macros to reduce
 // C++ boilerplate.  All combinations of lvalue and rvalue references are
 // allowed for operands.
-template<typename A> A operator-(const A &x) { return {typename A::Negate{x}}; }
+template<TypeCategory C, int K>
+Expr<Type<C, K>> operator-(const Expr<Type<C, K>> &x) {
+  return {Negate<Type<C, K>>{x}};
+}
+template<TypeCategory C>
+Expr<SomeKind<C>> operator-(const Expr<SomeKind<C>> &x) {
+  return std::visit(
+      [](const auto &y) -> Expr<SomeKind<C>> { return {-y}; }, x.u);
+}
 
 #define BINARY(FUNC, CONSTR) \
   template<typename A> A FUNC(const A &x, const A &y) { \
@@ -728,5 +689,35 @@ BINARY(operator!=, RelationalOperator::NE)
 BINARY(operator>=, RelationalOperator::GE)
 BINARY(operator>, RelationalOperator::GT)
 #undef BINARY
+
+extern template class Expr<Type<TypeCategory::Character, 1>>;  // TODO others
+extern template struct Comparison<Type<TypeCategory::Integer, 1>>;
+extern template struct Comparison<Type<TypeCategory::Integer, 2>>;
+extern template struct Comparison<Type<TypeCategory::Integer, 4>>;
+extern template struct Comparison<Type<TypeCategory::Integer, 8>>;
+extern template struct Comparison<Type<TypeCategory::Integer, 16>>;
+extern template struct Comparison<Type<TypeCategory::Real, 2>>;
+extern template struct Comparison<Type<TypeCategory::Real, 4>>;
+extern template struct Comparison<Type<TypeCategory::Real, 8>>;
+extern template struct Comparison<Type<TypeCategory::Real, 10>>;
+extern template struct Comparison<Type<TypeCategory::Real, 16>>;
+extern template struct Comparison<Type<TypeCategory::Complex, 2>>;
+extern template struct Comparison<Type<TypeCategory::Complex, 4>>;
+extern template struct Comparison<Type<TypeCategory::Complex, 8>>;
+extern template struct Comparison<Type<TypeCategory::Complex, 10>>;
+extern template struct Comparison<Type<TypeCategory::Complex, 16>>;
+extern template struct Comparison<Type<TypeCategory::Character, 1>>;  // TODO
+                                                                      // more
+extern template class Expr<Type<TypeCategory::Logical, 1>>;
+extern template class Expr<Type<TypeCategory::Logical, 2>>;
+extern template class Expr<Type<TypeCategory::Logical, 4>>;
+extern template class Expr<Type<TypeCategory::Logical, 8>>;
+extern template class Expr<SomeInteger>;
+extern template class Expr<SomeReal>;
+extern template class Expr<SomeComplex>;
+extern template class Expr<SomeCharacter>;
+extern template class Expr<SomeLogical>;
+extern template class Expr<SomeType>;
+
 }  // namespace Fortran::evaluate
 #endif  // FORTRAN_EVALUATE_EXPRESSION_H_
diff --git a/flang/lib/evaluate/type.h b/flang/lib/evaluate/type.h
index a2bf59855e02..5e3d2c108e1c 100644
--- a/flang/lib/evaluate/type.h
+++ b/flang/lib/evaluate/type.h
@@ -149,16 +149,20 @@ using LogicalResult = Type<TypeCategory::Logical, 1>;
 // These macros and template create instances of std::variant<> that can contain
 // applications of some class template to all of the supported kinds of
 // a category of intrinsic type.
-template<TypeCategory CAT, template<int> class T> struct KindsVariant;
+template<TypeCategory CAT, template<int> class T> struct VariantOverKinds;
 #define TKIND(K) T<K>
 #define MAKE(Cat, CAT) \
-  template<template<int> class T> struct KindsVariant<TypeCategory::Cat, T> { \
+  template<template<int> class T> \
+  struct VariantOverKinds<TypeCategory::Cat, T> { \
     using type = std::variant<FOR_EACH_##CAT##_KIND(TKIND, COMMA)>; \
   };
 FOR_EACH_CATEGORY(MAKE)
 #undef MAKE
 #undef TKIND
 
+template<TypeCategory CAT, template<int> class T>
+using KindsVariant = typename VariantOverKinds<CAT, T>::type;
+
 // Map scalar value types back to their Fortran types.
 // For every type T = Type<CAT, KIND>, TypeOfScalarValue<T>> == T.
 // E.g., TypeOfScalarValue<Integer<32>> is Type<TypeCategory::Integer, 4>.
@@ -195,7 +199,6 @@ template<TypeCategory CAT> struct SomeKindScalar {
   static constexpr TypeCategory category{CAT};
   CLASS_BOILERPLATE(SomeKindScalar)
 
-  template<int KIND> using KindScalar = Scalar<Type<CAT, KIND>>;
   template<typename A> SomeKindScalar(const A &x) : u{x} {}
   template<typename A>
   SomeKindScalar(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
@@ -213,7 +216,8 @@ template<TypeCategory CAT> struct SomeKindScalar {
     return common::GetIf<std::string>(u);
   }
 
-  typename KindsVariant<CAT, KindScalar>::type u;
+  template<int KIND> using KindScalar = Scalar<Type<CAT, KIND>>;
+  KindsVariant<CAT, KindScalar> u;
 };
 
 // Holds a scalar constant of any intrinsic category and size.
diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc
index 4fcce0cdb107..eb69ed8e4ccb 100644
--- a/flang/lib/semantics/expression.cc
+++ b/flang/lib/semantics/expression.cc
@@ -265,20 +265,16 @@ static std::optional<Expr<evaluate::SomeReal>> AnalyzeLiteral(
 
 static std::optional<Expr<evaluate::SomeReal>> AnalyzeLiteral(
     ExpressionAnalyzer &ea, const parser::SignedRealLiteralConstant &x) {
-  auto result{AnalyzeLiteral(ea, std::get<parser::RealLiteralConstant>(x.t))};
-  if (result.has_value()) {
+  if (auto result{
+          AnalyzeLiteral(ea, std::get<parser::RealLiteralConstant>(x.t))}) {
     if (auto sign{std::get<std::optional<parser::Sign>>(x.t)}) {
       if (sign == parser::Sign::Negative) {
-        std::visit(
-            [](auto &rk) {
-              using t = std::decay_t<decltype(rk)>;
-              rk = typename t::Negate{rk};
-            },
-            result->u);
+        return {-*result};
       }
     }
+    return result;
   }
-  return result;
+  return std::nullopt;
 }
 
 template<>