[flang] add left(), right(), and comments
authorpeter klausler <pklausler@nvidia.com>
Fri, 7 Sep 2018 17:33:32 +0000 (10:33 -0700)
committerpeter klausler <pklausler@nvidia.com>
Wed, 12 Sep 2018 23:29:16 +0000 (16:29 -0700)
Original-commit: flang-compiler/f18@372fd0650895153286e66f21caff9302f635cb36
Reviewed-on: https://github.com/flang-compiler/f18/pull/183
Tree-same-pre-rewrite: false

flang/lib/common/indirection.h
flang/lib/evaluate/expression.cc
flang/lib/evaluate/expression.h
flang/lib/evaluate/tools.cc
flang/lib/evaluate/tools.h
flang/lib/parser/parse-tree.h
flang/lib/semantics/expression.cc
flang/lib/semantics/expression.h
flang/tools/f18/CMakeLists.txt
flang/tools/f18/f18.cc

index 2fbcc51..6521748 100644 (file)
@@ -22,6 +22,7 @@
 // Intended to be as invisible as a reference, wherever possible.
 
 #include "../common/idioms.h"
+#include <memory>
 #include <type_traits>
 #include <utility>
 
@@ -113,5 +114,44 @@ private:
   A *p_{nullptr};
 };
 
+// A variant of Indirection suitable for use with forward-referenced types.
+// These are nullable pointers, not references.  Allocation is not available,
+// and a single externalized destructor must be defined.
+template<typename A> class OwningPointer {
+public:
+  using element_type = A;
+
+  OwningPointer() {}
+  OwningPointer(OwningPointer &&that) : p_{that.release()} {}
+  explicit OwningPointer(std::unique_ptr<A> &&that) : p_{that.release()} {}
+  explicit OwningPointer(A *&&p) : p_{p} { p = nullptr; }
+  ~OwningPointer();
+  OwningPointer &operator=(OwningPointer &&that) {
+    reset(that.release());
+    return *this;
+  }
+
+  A &operator*() { return *p_; }
+  const A &operator*() const { return *p_; }
+  A *operator->() { return p_; }
+  const A *operator->() const { return p_; }
+
+  A *get() const { return p_; }
+
+  A *release() {
+    A *result{p_};
+    p_ = nullptr;
+    return result;
+  }
+
+  void reset(A *p) {
+    this->~OwningPointer();
+    p_ = p;
+  }
+
+private:
+  A *p_{nullptr};
+};
+
 }  // namespace Fortran::common
 #endif  // FORTRAN_COMMON_INDIRECTION_H_
index 531dd34..03776c6 100644 (file)
@@ -33,16 +33,16 @@ namespace Fortran::evaluate {
 template<typename D, typename R, typename... O>
 auto Operation<D, R, O...>::Fold(FoldingContext &context)
     -> std::optional<Constant<Result>> {
-  auto c0{operand<0>().Fold(context)};
-  if constexpr (operands() == 1) {
+  auto c0{left().Fold(context)};
+  if constexpr (operands == 1) {
     if (c0.has_value()) {
       if (auto scalar{derived().FoldScalar(context, c0->value)}) {
         return {Constant<Result>{std::move(*scalar)}};
       }
     }
   } else {
-    static_assert(operands() == 2);  // TODO: generalize to N operands?
-    auto c1{operand<1>().Fold(context)};
+    static_assert(operands == 2);  // TODO: generalize to N operands?
+    auto c1{right().Fold(context)};
     if (c0.has_value() && c1.has_value()) {
       if (auto scalar{derived().FoldScalar(context, c0->value, c1->value)}) {
         return {Constant<Result>{std::move(*scalar)}};
@@ -399,15 +399,30 @@ auto LogicalOperation<KIND>::FoldScalar(FoldingContext &context,
 
 template<typename D, typename R, typename... O>
 std::ostream &Operation<D, R, O...>::Dump(std::ostream &o) const {
-  operand<0>().Dump(o << derived().prefix());
-  if constexpr (operands() > 1) {
-    operand<1>().Dump(o << derived().infix());
+  left().Dump(derived().Prefix(o));
+  if constexpr (operands > 1) {
+    right().Dump(derived().Infix(o));
   }
-  return o << derived().suffix();
+  return derived().Suffix(o);
 }
 
-template<typename A> std::string Relational<A>::infix() const {
-  return "."s + EnumToString(opr) + '.';
+template<typename TO, TypeCategory FROMCAT>
+std::ostream &Convert<TO, FROMCAT>::Dump(std::ostream &o) const {
+  static_assert(TO::category == TypeCategory::Integer ||
+      TO::category == TypeCategory::Real ||
+      TO::category == TypeCategory::Logical || !"Convert<> to bad category!");
+  if constexpr (TO::category == TypeCategory::Integer) {
+    o << "INT";
+  } else if constexpr (TO::category == TypeCategory::Real) {
+    o << "REAL";
+  } else if constexpr (TO::category == TypeCategory::Logical) {
+    o << "LOGICAL";
+  }
+  return this->left().Dump(o << '(') << ",KIND=" << TO::kind << ')';
+}
+
+template<typename A> std::ostream &Relational<A>::Infix(std::ostream &o) const {
+  return o << '.' << EnumToString(opr) << '.';
 }
 
 std::ostream &Relational<SomeType>::Dump(std::ostream &o) const {
@@ -415,15 +430,15 @@ std::ostream &Relational<SomeType>::Dump(std::ostream &o) const {
   return o;
 }
 
-template<int KIND> const char *LogicalOperation<KIND>::infix() const {
-  const char *result{nullptr};
+template<int KIND>
+std::ostream &LogicalOperation<KIND>::Infix(std::ostream &o) const {
   switch (logicalOperator) {
-  case LogicalOperator::And: result = ".AND."; break;
-  case LogicalOperator::Or: result = ".OR."; break;
-  case LogicalOperator::Eqv: result = ".EQV."; break;
-  case LogicalOperator::Neqv: result = ".NEQV."; break;
+  case LogicalOperator::And: o << ".AND."; break;
+  case LogicalOperator::Or: o << ".OR."; break;
+  case LogicalOperator::Eqv: o << ".EQV."; break;
+  case LogicalOperator::Neqv: o << ".NEQV."; break;
   }
-  return result;
+  return o;
 }
 
 template<typename T> std::ostream &Constant<T>::Dump(std::ostream &o) const {
@@ -470,12 +485,11 @@ Expr<SubscriptInteger> Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
                              static_cast<std::uint64_t>(c.value.size())});
                        },
           [](const Concat<KIND> &c) {
-            return c.template operand<0>().LEN() +
-                c.template operand<1>().LEN();
+            return c.left().LEN() + c.template right().LEN();
           },
           [](const Extremum<Result> &c) {
-            return Expr<SubscriptInteger>{Extremum<SubscriptInteger>{
-                c.template operand<0>().LEN(), c.template operand<1>().LEN()}};
+            return Expr<SubscriptInteger>{
+                Extremum<SubscriptInteger>{c.left().LEN(), c.right().LEN()}};
           },
           [](const DataReference<Result> &dr) { return dr.reference->LEN(); },
           [](const CopyableIndirection<Substring> &ss) { return ss->LEN(); },
@@ -496,7 +510,7 @@ auto ExpressionBase<RESULT>::ScalarValue() const
     if constexpr (common::HasMember<Parentheses<Result>,
                       decltype(derived().u)>) {
       if (auto p{common::GetIf<Parentheses<Result>>(derived().u)}) {
-        return p->template operand<0>().ScalarValue();
+        return p->left().ScalarValue();
       }
     }
   } else if constexpr (std::is_same_v<Result, SomeType>) {
@@ -526,6 +540,8 @@ auto ExpressionBase<RESULT>::ScalarValue() const
   return std::nullopt;
 }
 
+Expr<SomeType>::~Expr() {}
+
 // Template instantiations to resolve the "extern template" declarations
 // in expression.h.
 
@@ -602,3 +618,15 @@ template struct ExpressionBase<SomeLogical>;
 template struct ExpressionBase<SomeType>;
 
 }  // namespace Fortran::evaluate
+
+// For reclamation of analyzed expressions to which owning pointers have
+// been embedded in the parse tree.  This destructor appears here, where
+// definitions for all the necessary types are available, to obviate a
+// need to include lib/evaluate/*.h headers in the parser proper.
+namespace Fortran::common {
+template<> OwningPointer<evaluate::GenericExprWrapper>::~OwningPointer() {
+  delete p_;
+  p_ = nullptr;
+}
+template class OwningPointer<evaluate::GenericExprWrapper>;
+}  // namespace Fortran::common
index 090619e..017b603 100644 (file)
@@ -86,18 +86,27 @@ template<typename T> struct FunctionReference {
   CopyableIndirection<FunctionRef> reference;
 };
 
-// Abstract Operation<> base class. The first type parameter is a "CRTP"
-// reference to the specific operation class; e.g., Add is defined with
-// struct Add : public Operation<Add, ...>.
+// Operations always have specific Fortran result types (i.e., with known
+// intrinsic type category and kind parameter value).  The classes that
+// represent the operations all inherit from this Operation<> base class
+// template.  Note that Operation has as its first type parameter (DERIVED) a
+// "curiously reoccurring template pattern (CRTP)" reference to the specific
+// operation class being derived from Operation; e.g., Add is defined with
+// struct Add : public Operation<Add, ...>.  Uses of instances of Operation<>,
+// including its own member functions, can access each specific class derived
+// from it via its derived() member function with compile-time type safety.
 template<typename DERIVED, typename RESULT, typename... OPERANDS>
 class Operation {
-  using OperandTypes = std::tuple<OPERANDS...>;
-  static_assert(RESULT::kind > 0 || !"bad result Type");
+  static_assert(RESULT::isSpecificType || !"bad result Type");
+  // The extra "int" member is a dummy that allows a safe unused reference
+  // to element 1 to arise indirectly in the definition of "right()" below
+  // when the operation has but a single operand.
+  using OperandTypes = std::tuple<OPERANDS..., int>;
 
 public:
   using Derived = DERIVED;
   using Result = RESULT;
-  static constexpr auto operands() { return std::tuple_size_v<OperandTypes>; }
+  static constexpr std::size_t operands{sizeof...(OPERANDS)};
   template<int J> using Operand = std::tuple_element_t<J, OperandTypes>;
   using IsFoldableTrait = std::true_type;
 
@@ -105,7 +114,7 @@ public:
   // Binary operations wrap a tuple of CopyableIndirections to Exprs.
 private:
   using Container =
-      std::conditional_t<operands() == 1, CopyableIndirection<Expr<Operand<0>>>,
+      std::conditional_t<operands == 1, CopyableIndirection<Expr<Operand<0>>>,
           std::tuple<CopyableIndirection<Expr<OPERANDS>>...>>;
 
 public:
@@ -117,8 +126,13 @@ public:
   Derived &derived() { return *static_cast<Derived *>(this); }
   const Derived &derived() const { return *static_cast<const Derived *>(this); }
 
+  // References to operand expressions from member functions of derived
+  // classes for specific operators can be made by index, e.g. operand<0>(),
+  // which must be spelled like "this->template operand<0>()" when
+  // inherited in a derived class template.  There are convenience aliases
+  // left() and right() that are not templates.
   template<int J> Expr<Operand<J>> &operand() {
-    if constexpr (operands() == 1) {
+    if constexpr (operands == 1) {
       static_assert(J == 0);
       return *operand_;
     } else {
@@ -126,7 +140,7 @@ public:
     }
   }
   template<int J> const Expr<Operand<J>> &operand() const {
-    if constexpr (operands() == 1) {
+    if constexpr (operands == 1) {
       static_assert(J == 0);
       return *operand_;
     } else {
@@ -134,14 +148,29 @@ public:
     }
   }
 
+  Expr<Operand<0>> &left() { return operand<0>(); }
+  const Expr<Operand<0>> &left() const { return operand<0>(); }
+
+  std::conditional_t<(operands > 1), Expr<Operand<1>> &, void> right() {
+    if constexpr (operands > 1) {
+      return operand<1>();
+    }
+  }
+  std::conditional_t<(operands > 1), const Expr<Operand<1>> &, void>
+  right() const {
+    if constexpr (operands > 1) {
+      return operand<1>();
+    }
+  }
+
   std::ostream &Dump(std::ostream &) const;
   std::optional<Constant<Result>> Fold(FoldingContext &);
 
 protected:
-  // Overridable string functions for Dump()
-  static const char *prefix() { return "("; }
-  static const char *infix() { return ","; }
-  static const char *suffix() { return ")"; }
+  // Overridable functions for Dump()
+  static std::ostream &Prefix(std::ostream &o) { return o << '('; }
+  static std::ostream &Infix(std::ostream &o) { return o << ','; }
+  static std::ostream &Suffix(std::ostream &o) { return o << ')'; }
 
 private:
   Container operand_;
@@ -149,14 +178,25 @@ private:
 
 // Unary operations
 
+// Conversions to specific types from expressions of known category and
+// dynamic kind.
 template<typename TO, TypeCategory FROMCAT>
 struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
+  // Fortran doesn't have conversions between kinds of CHARACTER.
+  // Conversions between kinds of COMPLEX are represented piecewise.
+  static_assert(((TO::category == TypeCategory::Integer ||
+                     TO::category == TypeCategory::Real) &&
+                    (FROMCAT == TypeCategory::Integer ||
+                        FROMCAT == TypeCategory::Real)) ||
+      (TO::category == TypeCategory::Logical &&
+          FROMCAT == TypeCategory::Logical));
   using Result = TO;
   using Operand = SomeKind<FROMCAT>;
   using Base = Operation<Convert, Result, Operand>;
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &);
+  std::ostream &Dump(std::ostream &) const;
 };
 
 template<typename A>
@@ -178,7 +218,7 @@ template<typename A> struct Negate : public Operation<Negate<A>, A, A> {
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &);
-  static const char *prefix() { return "(-"; }
+  static std::ostream &Prefix(std::ostream &o) { return o << "(-"; }
 };
 
 template<int KIND>
@@ -196,7 +236,9 @@ struct ComplexComponent
 
   std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &) const;
-  const char *suffix() const { return isImaginaryPart ? "%IM)" : "%RE)"; }
+  std::ostream &Suffix(std::ostream &o) const {
+    return o << (isImaginaryPart ? "%IM)" : "%RE)");
+  }
 
   bool isImaginaryPart{true};
 };
@@ -210,7 +252,7 @@ struct Not : public Operation<Not<KIND>, Type<TypeCategory::Logical, KIND>,
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &);
-  static const char *prefix() { return "(.NOT."; }
+  static std::ostream &Prefix(std::ostream &o) { return o << "(.NOT."; }
 };
 
 // Binary operations
@@ -222,7 +264,7 @@ template<typename A> struct Add : public Operation<Add<A>, A, A, A> {
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
-  static constexpr const char *infix() { return "+"; }
+  static std::ostream &Infix(std::ostream &o) { return o << '+'; }
 };
 
 template<typename A> struct Subtract : public Operation<Subtract<A>, A, A, A> {
@@ -232,7 +274,7 @@ template<typename A> struct Subtract : public Operation<Subtract<A>, A, A, A> {
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
-  static constexpr const char *infix() { return "-"; }
+  static std::ostream &Infix(std::ostream &o) { return o << '-'; }
 };
 
 template<typename A> struct Multiply : public Operation<Multiply<A>, A, A, A> {
@@ -242,7 +284,7 @@ template<typename A> struct Multiply : public Operation<Multiply<A>, A, A, A> {
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
-  static constexpr const char *infix() { return "*"; }
+  static std::ostream &Infix(std::ostream &o) { return o << '*'; }
 };
 
 template<typename A> struct Divide : public Operation<Divide<A>, A, A, A> {
@@ -252,7 +294,7 @@ template<typename A> struct Divide : public Operation<Divide<A>, A, A, A> {
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
-  static constexpr const char *infix() { return "/"; }
+  static std::ostream &Infix(std::ostream &o) { return o << '/'; }
 };
 
 template<typename A> struct Power : public Operation<Power<A>, A, A, A> {
@@ -262,7 +304,7 @@ template<typename A> struct Power : public Operation<Power<A>, A, A, A> {
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
-  static constexpr const char *infix() { return "**"; }
+  static std::ostream &Infix(std::ostream &o) { return o << "**"; }
 };
 
 template<typename A>
@@ -274,7 +316,7 @@ struct RealToIntPower : public Operation<RealToIntPower<A>, A, A, SomeInteger> {
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(FoldingContext &,
       const Scalar<BaseOperand> &, const Scalar<ExponentOperand> &);
-  static constexpr const char *infix() { return "**"; }
+  static std::ostream &Infix(std::ostream &o) { return o << "**"; }
 };
 
 template<typename A> struct Extremum : public Operation<Extremum<A>, A, A, A> {
@@ -291,8 +333,8 @@ template<typename A> struct Extremum : public Operation<Extremum<A>, A, A, A> {
 
   std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &) const;
-  const char *prefix() const {
-    return ordering == Ordering::Less ? "MIN(" : "MAX(";
+  std::ostream &Prefix(std::ostream &o) const {
+    return o << (ordering == Ordering::Less ? "MIN(" : "MAX(");
   }
 
   Ordering ordering{Ordering::Greater};
@@ -322,7 +364,7 @@ struct Concat
   using Base::Base;
   static std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
-  static constexpr const char *infix() { return "//"; }
+  static std::ostream &Infix(std::ostream &o) { return o << "//"; }
 };
 
 ENUM_CLASS(LogicalOperator, And, Or, Eqv, Neqv)
@@ -343,7 +385,7 @@ struct LogicalOperation
 
   std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &) const;
-  const char *infix() const;
+  std::ostream &Infix(std::ostream &) const;
 
   LogicalOperator logicalOperator;
 };
@@ -532,7 +574,7 @@ struct Relational : public Operation<Relational<A>, LogicalResult, A, A> {
 
   std::optional<Scalar<Result>> FoldScalar(
       FoldingContext &c, const Scalar<Operand> &, const Scalar<Operand> &);
-  std::string infix() const;
+  std::ostream &Infix(std::ostream &) const;
 
   RelationalOperator opr;
 };
@@ -622,6 +664,11 @@ public:
   using IsFoldableTrait = std::true_type;
   CLASS_BOILERPLATE(Expr)
 
+  // Owning references to these generic expressions can appear in other
+  // compiler data structures (viz., the parse tree and symbol table), so
+  // its destructor is externalized to reduce redundant default instances.
+  ~Expr();
+
   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)} {}
@@ -649,6 +696,14 @@ public:
   common::CombineVariants<Others, Categories> u;
 };
 
+// This wrapper class is used, by means of a forward reference with
+// OwningPointer, to implement owning pointers to analyzed expressions
+// from parse tree nodes.
+struct GenericExprWrapper {
+  GenericExprWrapper(Expr<SomeType> &&x) : v{std::move(x)} {}
+  Expr<SomeType> v;
+};
+
 extern template class Expr<SomeInteger>;
 extern template class Expr<SomeReal>;
 extern template class Expr<SomeComplex>;
index 20bed09..f70b299 100644 (file)
@@ -57,7 +57,9 @@ ConvertRealOperandsResult ConvertRealOperands(
       std::move(x.u), std::move(y.u));
 }
 
-// A helper template for NumericOperation and its subroutines.
+// Helpers for NumericOperation and its subroutines below.
+static std::optional<Expr<SomeType>> NoExpr() { return std::nullopt; }
+
 template<TypeCategory CAT>
 std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) {
   return {AsGenericExpr(std::move(catExpr))};
@@ -68,7 +70,7 @@ std::optional<Expr<SomeType>> Package(
   if (catExpr.has_value()) {
     return {AsGenericExpr(std::move(*catExpr))};
   }
-  return std::nullopt;
+  return NoExpr();
 }
 
 std::optional<Expr<SomeComplex>> ConstructComplex(
@@ -141,7 +143,7 @@ std::optional<Expr<SomeType>> MixedComplexLeft(
     Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))};
     return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
   }
-  return std::nullopt;
+  return NoExpr();
 }
 
 // Mixed COMPLEX operations with the COMPLEX operand on the right.
@@ -173,7 +175,7 @@ std::optional<Expr<SomeType>> MixedComplexRight(
     Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
     return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
   }
-  return std::nullopt;
+  return NoExpr();
 }
 
 // N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
@@ -254,8 +256,9 @@ std::optional<Expr<SomeType>> NumericOperation(
           },
           // Default case
           [&](auto &&, auto &&) {
+            // TODO: defined operator
             messages.Say("non-numeric operands to numeric operation"_err_en_US);
-            return std::optional<Expr<SomeType>>{std::nullopt};
+            return NoExpr();
           }},
       std::move(x.u), std::move(y.u));
 }
@@ -269,4 +272,28 @@ template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
 template std::optional<Expr<SomeType>> NumericOperation<Divide>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 
+std::optional<Expr<SomeType>> Negation(
+    parser::ContextualMessages &messages, Expr<SomeType> &&x) {
+  return std::visit(
+      common::visitors{[&](BOZLiteralConstant &&) {
+                         messages.Say(
+                             "BOZ literal cannot be negated"_err_en_US);
+                         return NoExpr();
+                       },
+          [&](Expr<SomeInteger> &&x) { return Package(std::move(x)); },
+          [&](Expr<SomeReal> &&x) { return Package(-std::move(x)); },
+          [&](Expr<SomeComplex> &&x) { return Package(-std::move(x)); },
+          [&](Expr<SomeCharacter> &&x) {
+            // TODO: defined operator
+            messages.Say("CHARACTER cannot be negated"_err_en_US);
+            return NoExpr();
+          },
+          [&](Expr<SomeLogical> &&x) {
+            // TODO: defined operator
+            messages.Say("LOGICAL cannot be negated"_err_en_US);
+            return NoExpr();
+          }},
+      std::move(x.u));
+}
+
 }  // namespace Fortran::evaluate
index 88d9738..1f054e5 100644 (file)
@@ -299,6 +299,9 @@ extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
 extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
 
+std::optional<Expr<SomeType>> Negation(
+    parser::ContextualMessages &, Expr<SomeType> &&);
+
 // Convenience functions and operator overloadings for expression construction.
 // These interfaces are defined only for those situations that cannot possibly
 // need to emit any messages.  Use the more general NumericOperation<>
@@ -309,6 +312,14 @@ Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
   return {Negate<Type<C, K>>{std::move(x)}};
 }
 
+template<int K>
+Expr<Type<TypeCategory::Complex, K>> operator-(
+    Expr<Type<TypeCategory::Complex, K>> &&x) {
+  using Part = Type<TypeCategory::Real, K>;
+  return {ComplexConstructor<K>{Negate<Part>{ComplexComponent<K>{false, x}},
+      Negate<Part>{ComplexComponent<K>{true, x}}}};
+}
+
 template<TypeCategory C, int K>
 Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
   return {Add<Type<C, K>>{std::move(x), std::move(y)}};
index 5a180b8..8a5b149 100644 (file)
@@ -62,6 +62,17 @@ namespace Fortran::semantics {
 class Symbol;
 }  // namespace Fortran::semantics
 
+// Expressions in the parse tree have owning pointers that can be set to
+// type-checked generic expression representations by semantic analysis.
+// OwningPointer<> is used for leak safety without having to include
+// the bulk of lib/evaluate/*.h headers into the parser proper.
+namespace Fortran::evaluate {
+struct GenericExprWrapper;  // forward definition, wraps Expr<SomeType>
+}  // namespace Fortran::evaluate
+namespace Fortran::common {
+extern template class OwningPointer<evaluate::GenericExprWrapper>;
+}  // namespace Fortran::common
+
 // Most non-template classes in this file use these default definitions
 // for their move constructor and move assignment operator=, and disable
 // their copy constructor and copy assignment operator=.
@@ -1684,6 +1695,9 @@ struct Expr {
   explicit Expr(Designator &&);
   explicit Expr(FunctionReference &&);
 
+  // Filled in later during semantic analysis of the expression.
+  common::OwningPointer<evaluate::GenericExprWrapper> typedExpr;
+
   std::variant<common::Indirection<CharLiteralConstantSubstring>,
       LiteralConstant, common::Indirection<Designator>, ArrayConstructor,
       StructureConstructor, common::Indirection<TypeParamInquiry>,
index 239a254..ddb8683 100644 (file)
 // limitations under the License.
 
 #include "expression.h"
+#include "dump-parse-tree.h"  // TODO pmk temporary
 #include "symbol.h"
 #include "../common/idioms.h"
 #include "../evaluate/common.h"
 #include "../evaluate/tools.h"
+#include "../parser/parse-tree-visitor.h"
+#include "../parser/parse-tree.h"
 #include <functional>
 #include <optional>
 
@@ -63,7 +66,9 @@ struct ExprAnalyzer {
 
   int Analyze(
       const std::optional<parser::KindParam> &, int defaultKind, int kanjiKind);
+
   MaybeExpr Analyze(const parser::Expr &);
+  MaybeExpr Analyze(const parser::CharLiteralConstantSubstring &);
   MaybeExpr Analyze(const parser::LiteralConstant &);
   MaybeExpr Analyze(const parser::HollerithLiteralConstant &);
   MaybeExpr Analyze(const parser::IntLiteralConstant &);
@@ -77,32 +82,36 @@ struct ExprAnalyzer {
   MaybeExpr Analyze(const parser::Name &);
   MaybeExpr Analyze(const parser::NamedConstant &);
   MaybeExpr Analyze(const parser::ComplexPart &);
-
+  MaybeExpr Analyze(const parser::Designator &);
+  MaybeExpr Analyze(const parser::ArrayConstructor &);
+  MaybeExpr Analyze(const parser::StructureConstructor &);
+  MaybeExpr Analyze(const parser::TypeParamInquiry &);
+  MaybeExpr Analyze(const parser::FunctionReference &);
   MaybeExpr Analyze(const parser::Expr::Parentheses &);
-  MaybeExpr Analyze(const parser::Expr::UnaryPlus &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::Negate &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::NOT &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::DefinedUnary &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::Power &);  // TODO
+  MaybeExpr Analyze(const parser::Expr::UnaryPlus &);
+  MaybeExpr Analyze(const parser::Expr::Negate &);
+  MaybeExpr Analyze(const parser::Expr::NOT &);
+  MaybeExpr Analyze(const parser::Expr::PercentLoc &);
+  MaybeExpr Analyze(const parser::Expr::DefinedUnary &);
+  MaybeExpr Analyze(const parser::Expr::Power &);
   MaybeExpr Analyze(const parser::Expr::Multiply &);
   MaybeExpr Analyze(const parser::Expr::Divide &);
   MaybeExpr Analyze(const parser::Expr::Add &);
   MaybeExpr Analyze(const parser::Expr::Subtract &);
-  MaybeExpr Analyze(const parser::Expr::Concat &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::LT &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::LE &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::EQ &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::NE &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::GE &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::GT &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::AND &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::OR &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::EQV &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::NEQV &);  // TODO
-  MaybeExpr Analyze(const parser::Expr::XOR &);  // TODO
+  MaybeExpr Analyze(const parser::Expr::Concat &);
+  MaybeExpr Analyze(const parser::Expr::LT &);
+  MaybeExpr Analyze(const parser::Expr::LE &);
+  MaybeExpr Analyze(const parser::Expr::EQ &);
+  MaybeExpr Analyze(const parser::Expr::NE &);
+  MaybeExpr Analyze(const parser::Expr::GE &);
+  MaybeExpr Analyze(const parser::Expr::GT &);
+  MaybeExpr Analyze(const parser::Expr::AND &);
+  MaybeExpr Analyze(const parser::Expr::OR &);
+  MaybeExpr Analyze(const parser::Expr::EQV &);
+  MaybeExpr Analyze(const parser::Expr::NEQV &);
+  MaybeExpr Analyze(const parser::Expr::XOR &);
   MaybeExpr Analyze(const parser::Expr::ComplexConstructor &);
-  MaybeExpr Analyze(const parser::Expr::DefinedBinary &);  // TODO
-  // TODO more remain
+  MaybeExpr Analyze(const parser::Expr::DefinedBinary &);
 
   FoldingContext &context;
   const semantics::IntrinsicTypeDefaultKinds &defaults;
@@ -149,13 +158,14 @@ MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const std::variant<As...> &u) {
   return std::visit([&](const auto &x) { return AnalyzeHelper(ea, x); }, u);
 }
 
+template<typename A>
+MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const common::Indirection<A> &x) {
+  return AnalyzeHelper(ea, *x);
+}
+
 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);
+  return std::visit(
+      [&](const auto &x) { return AnalyzeHelper(*this, x); }, expr.u);
 }
 
 MaybeExpr ExprAnalyzer::Analyze(const parser::LiteralConstant &x) {
@@ -295,9 +305,9 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::RealLiteralConstant &x) {
   for (const char *p{x.real.source.begin()}; p < end; ++p) {
     if (parser::IsLetter(*p)) {
       switch (*p) {
-      case 'e': letterKind = 4; break;
-      case 'd': letterKind = 8; break;
-      case 'q': letterKind = 16; break;
+      case 'e': letterKind = defaults.defaultRealKind; break;
+      case 'd': letterKind = defaults.defaultDoublePrecisionKind; break;
+      case 'q': letterKind = defaults.defaultQuadPrecisionKind; break;
       default: ctxMsgs.Say("unknown exponent letter '%c'"_err_en_US, *p);
       }
       break;
@@ -407,6 +417,37 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::NamedConstant &n) {
   return Analyze(n.v);
 }
 
+MaybeExpr ExprAnalyzer::Analyze(const parser::CharLiteralConstantSubstring &) {
+  context.messages.Say(
+      "pmk: CharLiteralConstantSubstring unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Designator &) {
+  context.messages.Say("pmk: Designator unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::ArrayConstructor &) {
+  context.messages.Say("pmk: ArrayConstructor unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::StructureConstructor &) {
+  context.messages.Say("pmk: StructureConstructor unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::TypeParamInquiry &) {
+  context.messages.Say("pmk: TypeParamInquiry unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::FunctionReference &) {
+  context.messages.Say("pmk: FunctionReference unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
 MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
   if (MaybeExpr operand{AnalyzeHelper(*this, *x.v)}) {
     return std::visit(
@@ -433,7 +474,33 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
   return std::nullopt;
 }
 
-// TODO: defined operators for illegal intrinsic operator cases
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::UnaryPlus &x) {
+  return AnalyzeHelper(*this, *x.v);
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Negate &x) {
+  if (MaybeExpr operand{AnalyzeHelper(*this, *x.v)}) {
+    return Negation(context.messages, std::move(operand->u));
+  }
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NOT &) {
+  context.messages.Say("pmk: NOT unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::PercentLoc &) {
+  context.messages.Say("pmk: %LOC unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedUnary &) {
+  context.messages.Say("pmk: DefinedUnary unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+// TODO: check defined operators for illegal intrinsic operator cases
 template<template<typename> class OPR, typename PARSED>
 MaybeExpr BinaryOperationHelper(ExprAnalyzer &ea, const PARSED &x) {
   if (auto both{common::AllPresent(AnalyzeHelper(ea, *std::get<0>(x.t)),
@@ -466,6 +533,76 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::ComplexConstructor &x) {
       AnalyzeHelper(*this, *std::get<1>(x.t))));
 }
 
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Power &) {
+  context.messages.Say("pmk: Power unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Concat &) {
+  context.messages.Say("pmk: Concat unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LT &) {
+  context.messages.Say("pmk: .LT. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LE &) {
+  context.messages.Say("pmk: .LE. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQ &) {
+  context.messages.Say("pmk: .EQ. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NE &) {
+  context.messages.Say("pmk: .NE. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GT &) {
+  context.messages.Say("pmk: .GT. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GE &) {
+  context.messages.Say("pmk: .GE. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::AND &) {
+  context.messages.Say("pmk: .AND. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::OR &) {
+  context.messages.Say("pmk: .OR. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQV &) {
+  context.messages.Say("pmk: .EQV. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NEQV &) {
+  context.messages.Say("pmk: .NEQV. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::XOR &) {
+  context.messages.Say("pmk: .XOR. unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedBinary &) {
+  context.messages.Say("pmk: DefinedBinary unimplemented\n"_err_en_US);
+  return std::nullopt;
+}
+
 }  // namespace Fortran::evaluate
 
 namespace Fortran::semantics {
@@ -475,4 +612,40 @@ MaybeExpr AnalyzeExpr(evaluate::FoldingContext &context,
   return evaluate::ExprAnalyzer{context, defaults}.Analyze(expr);
 }
 
+class Mutator {
+public:
+  Mutator(evaluate::FoldingContext &context,
+      const IntrinsicTypeDefaultKinds &defaults, std::ostream &o)
+    : context_{context}, defaults_{defaults}, out_{o} {}
+
+  template<typename A> bool Pre(A &) { return true /* visit children */; }
+  template<typename A> void Post(A &) {}
+
+  bool Pre(parser::Expr &expr) {
+    if (expr.typedExpr.get() == nullptr) {
+      if (MaybeExpr checked{AnalyzeExpr(context_, defaults_, expr)}) {
+        checked->Dump(out_ << "pmk checked: ") << '\n';
+        expr.typedExpr.reset(
+            new evaluate::GenericExprWrapper{std::move(*checked)});
+      } else {
+        out_ << "pmk: expression analysis failed for an expression: ";
+        DumpTree(out_, expr);
+      }
+    }
+    return false;
+  }
+
+private:
+  evaluate::FoldingContext &context_;
+  const IntrinsicTypeDefaultKinds &defaults_;
+  std::ostream &out_;
+};
+
+void AnalyzeExpressions(parser::Program &program,
+    evaluate::FoldingContext &context,
+    const IntrinsicTypeDefaultKinds &defaults, std::ostream &o) {
+  Mutator mutator{context, defaults, o};
+  parser::Walk(program, mutator);
+}
+
 }  // namespace Fortran::semantics
index 4ed7ab7..9b18b7e 100644 (file)
@@ -21,6 +21,7 @@
 #include "../parser/parse-tree.h"
 #include <cinttypes>
 #include <optional>
+#include <ostream>  // TODO pmk
 
 namespace Fortran::semantics {
 
@@ -29,6 +30,8 @@ using MaybeExpr = std::optional<evaluate::Expr<evaluate::SomeType>>;
 struct IntrinsicTypeDefaultKinds {
   int defaultIntegerKind{evaluate::DefaultInteger::kind};
   int defaultRealKind{evaluate::DefaultReal::kind};
+  int defaultDoublePrecisionKind{evaluate::DefaultDoublePrecision::kind};
+  int defaultQuadPrecisionKind{evaluate::DefaultDoublePrecision::kind};
   int defaultCharacterKind{evaluate::DefaultCharacter::kind};
   int defaultLogicalKind{evaluate::DefaultLogical::kind};
 };
@@ -36,5 +39,8 @@ struct IntrinsicTypeDefaultKinds {
 MaybeExpr AnalyzeExpr(evaluate::FoldingContext &,
     const IntrinsicTypeDefaultKinds &, const parser::Expr &);
 
+void AnalyzeExpressions(parser::Program &, evaluate::FoldingContext &,
+    const IntrinsicTypeDefaultKinds &, std::ostream &);
+
 }  // namespace Fortran::semantics
 #endif  // FORTRAN_SEMANTICS_EXPRESSION_H_
index 1500d8f..e0b06a8 100644 (file)
@@ -20,4 +20,5 @@ add_executable(f18
 target_link_libraries(f18
   FortranParser
   FortranSemantics
+  FortranEvaluate
 )
index 10fb78c..1a19f5e 100644 (file)
@@ -23,6 +23,7 @@
 #include "../../lib/parser/provenance.h"
 #include "../../lib/parser/unparse.h"
 #include "../../lib/semantics/dump-parse-tree.h"
+#include "../../lib/semantics/expression.h"
 #include "../../lib/semantics/mod-file.h"
 #include "../../lib/semantics/resolve-labels.h"
 #include "../../lib/semantics/resolve-names.h"
@@ -92,6 +93,7 @@ struct DriverOptions {
   bool dumpUnparseWithSymbols{false};
   bool dumpParseTree{false};
   bool dumpSymbols{false};
+  bool debugExpressions{false};
   bool debugResolveNames{false};
   bool measureTree{false};
   std::vector<std::string> pgf90Args;
@@ -208,7 +210,7 @@ std::string CompileFortran(
     MeasureParseTree(parseTree);
   }
   if (driver.debugResolveNames || driver.dumpSymbols ||
-      driver.dumpUnparseWithSymbols) {
+      driver.dumpUnparseWithSymbols || driver.debugExpressions) {
     std::vector<std::string> directories{options.searchDirectories};
     directories.insert(directories.begin(), "."s);
     if (driver.moduleDirectory != "."s) {
@@ -236,6 +238,22 @@ std::string CompileFortran(
       return {};
     }
   }
+  if (driver.debugExpressions) {
+    Fortran::parser::CharBlock whole{parsing.cooked().data()};
+    Fortran::parser::Messages messages;
+    Fortran::parser::ContextualMessages contextualMessages{whole, &messages};
+    Fortran::evaluate::FoldingContext context{contextualMessages};
+    Fortran::semantics::IntrinsicTypeDefaultKinds defaults;
+    Fortran::semantics::AnalyzeExpressions(
+        parseTree, context, defaults, std::cout);
+    messages.Emit(std::cerr, parsing.cooked());
+    if (!messages.empty() &&
+        (driver.warningsAreErrors || messages.AnyFatalError())) {
+      std::cerr << driver.prefix << "semantic errors in " << path << '\n';
+      exitStatus = EXIT_FAILURE;
+      return {};
+    }
+  }
   if (driver.dumpParseTree) {
     Fortran::semantics::DumpTree(std::cout, parseTree);
   }
@@ -394,6 +412,8 @@ int main(int argc, char *const argv[]) {
       driver.dumpParseTree = true;
     } else if (arg == "-fdebug-dump-symbols") {
       driver.dumpSymbols = true;
+    } else if (arg == "-fdebug-expressions") {
+      driver.debugExpressions = true;
     } else if (arg == "-fdebug-resolve-names") {
       driver.debugResolveNames = true;
     } else if (arg == "-fdebug-measure-parse-tree") {