[flang] restructure work in progress in semantics/expression.cc; checkpoint before...
authorpeter klausler <pklausler@nvidia.com>
Fri, 31 Aug 2018 20:28:21 +0000 (13:28 -0700)
committerpeter klausler <pklausler@nvidia.com>
Wed, 12 Sep 2018 23:29:10 +0000 (16:29 -0700)
Original-commit: flang-compiler/f18@5d991b0df38937f59aeade66fe413b2b5d4d3a4d
Reviewed-on: https://github.com/flang-compiler/f18/pull/183
Tree-same-pre-rewrite: false

flang/lib/common/template.h
flang/lib/evaluate/tools.cc
flang/lib/evaluate/tools.h
flang/lib/evaluate/type.h
flang/lib/semantics/expression.cc
flang/lib/semantics/expression.h

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