[flang] source provenances for expressions
authorpeter klausler <pklausler@nvidia.com>
Tue, 18 Sep 2018 16:34:59 +0000 (09:34 -0700)
committerpeter klausler <pklausler@nvidia.com>
Tue, 25 Sep 2018 22:24:00 +0000 (15:24 -0700)
Original-commit: flang-compiler/f18@5fe292fcac12ff64a4a80494cb85c4277024371a
Reviewed-on: https://github.com/flang-compiler/f18/pull/195
Tree-same-pre-rewrite: false

flang/lib/evaluate/CMakeLists.txt
flang/lib/evaluate/common.h
flang/lib/evaluate/expression.cc
flang/lib/evaluate/expression.h
flang/lib/evaluate/variable.cc
flang/lib/evaluate/variable.h
flang/lib/parser/grammar.h
flang/lib/parser/parse-tree.h
flang/lib/semantics/expression.cc
flang/lib/semantics/symbol.cc
flang/lib/semantics/symbol.h

index 9b6db7d..402824c 100644 (file)
@@ -26,4 +26,5 @@ add_library(FortranEvaluate
 
 target_link_libraries(FortranEvaluate
   FortranCommon
+  FortranSemantics
 )
index 5faf950..4bec50d 100644 (file)
@@ -142,14 +142,19 @@ template<typename A> using CopyableIndirection = common::Indirection<A, true>;
 // IsFoldableTrait.
 CLASS_TRAIT(IsFoldableTrait)
 struct FoldingContext {
-  explicit FoldingContext(parser::ContextualMessages &m,
+  explicit FoldingContext(const parser::ContextualMessages &m,
       Rounding round = defaultRounding, bool flush = false)
     : messages{m}, rounding{round}, flushDenormalsToZero{flush} {}
-  FoldingContext(parser::ContextualMessages &m, const FoldingContext &c)
+  FoldingContext(const parser::ContextualMessages &m, const FoldingContext &c)
     : messages{m}, rounding{c.rounding}, flushDenormalsToZero{
                                              c.flushDenormalsToZero} {}
 
-  parser::ContextualMessages &messages;
+  // For narrowed contexts
+  FoldingContext(const FoldingContext &c, const parser::ContextualMessages &m)
+    : messages{m}, rounding{c.rounding}, flushDenormalsToZero{
+                                             c.flushDenormalsToZero} {}
+
+  parser::ContextualMessages messages;
   Rounding rounding{defaultRounding};
   bool flushDenormalsToZero{false};
 };
index da08533..84fc817 100644 (file)
@@ -550,6 +550,14 @@ auto ExpressionBase<RESULT>::ScalarValue() const
 
 Expr<SomeType>::~Expr() {}
 
+// Rank()
+template<typename A> int ExpressionBase<A>::Rank() const {
+  return std::visit(
+      common::visitors{[](const BOZLiteralConstant &) { return 0; },
+          [](const auto &x) { return x.Rank(); }},
+      derived().u);
+}
+
 // Template instantiations to resolve the "extern template" declarations
 // that appear in expression.h.
 
index 72b037b..29d5427 100644 (file)
@@ -61,12 +61,13 @@ template<typename A> using ResultType = typename std::decay_t<A>::Result;
 // Wraps a constant value in a class with its resolved type.
 template<typename T> struct Constant {
   using Result = T;
-  using Value = Scalar<Result>;  // TODO rank > 0
+  using Value = Scalar<Result>;
   CLASS_BOILERPLATE(Constant)
   template<typename A> Constant(const A &x) : value{x} {}
   template<typename A>
   Constant(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
     : value(std::move(x)) {}
+  int Rank() const { return 0; }
   std::ostream &Dump(std::ostream &) const;
   Value value;
 };
@@ -80,6 +81,7 @@ using BOZLiteralConstant = typename LargestReal::Scalar::Word;
 template<typename T> struct FunctionReference {
   using Result = T;
   static_assert(Result::isSpecificType);
+  int Rank() const { return reference->Rank(); }
   CopyableIndirection<FunctionRef> reference;
 };
 
@@ -160,6 +162,17 @@ public:
     }
   }
 
+  int Rank() const {
+    int rank{left().Rank()};
+    if constexpr (operands > 1) {
+      int rightRank{right().Rank()};
+      if (rightRank > rank) {
+        rank = rightRank;
+      }
+    }
+    return rank;
+  }
+
   std::ostream &Dump(std::ostream &) const;
   std::optional<Constant<Result>> Fold(FoldingContext &);
 
@@ -397,8 +410,6 @@ template<typename RESULT> struct ExpressionBase {
   Derived &derived() { return *static_cast<Derived *>(this); }
   const Derived &derived() const { return *static_cast<const Derived *>(this); }
 
-  int Rank() const { return 0; }  // TODO
-
   template<typename A> Derived &operator=(const A &x) {
     Derived &d{derived()};
     d.u = x;
@@ -412,6 +423,7 @@ template<typename RESULT> struct ExpressionBase {
     return d;
   }
 
+  int Rank() const;
   std::ostream &Dump(std::ostream &) const;
   std::optional<Constant<Result>> Fold(FoldingContext &c);
   std::optional<Scalar<Result>> ScalarValue() const;
@@ -563,6 +575,9 @@ template<> class Relational<SomeType> {
 public:
   using Result = LogicalResult;
   EVALUATE_UNION_CLASS_BOILERPLATE(Relational)
+  int Rank() const {
+    return std::visit([](const auto &x) { return x.Rank(); }, u);
+  }
   std::ostream &Dump(std::ostream &o) const;
   common::MapTemplate<Relational, DirectlyComparableTypes> u;
 };
index 389fadd..e9ec82f 100644 (file)
@@ -369,6 +369,72 @@ Expr<SubscriptInteger> ProcedureDesignator::LEN() const {
       u);
 }
 
+// Rank()
+int Component::Rank() const { return symbol_->Rank(); }
+int Subscript::Rank() const {
+  return std::visit(common::visitors{[](const IndirectSubscriptIntegerExpr &x) {
+                                       int rank{x->Rank()};
+                                       CHECK(rank <= 1);
+                                       return rank;
+                                     },
+                        [](const Triplet &) { return 1; }},
+      u);
+}
+int ArrayRef::Rank() const {
+  int rank{0};
+  for (std::size_t j{0}; j < subscript.size(); ++j) {
+    rank += subscript[j].Rank();
+  }
+  return rank;
+}
+int CoarrayRef::Rank() const {
+  int rank{0};
+  for (std::size_t j{0}; j < subscript_.size(); ++j) {
+    rank += subscript_[j].Rank();
+  }
+  return rank;
+}
+int DataRef::Rank() const {
+  return std::visit(
+      common::visitors{[](const Symbol *sym) { return sym->Rank(); },
+          [](const auto &x) { return x.Rank(); }},
+      u);
+}
+int Substring::Rank() const {
+  return std::visit(common::visitors{[](const std::string &) { return 0; },
+                        [](const auto &x) { return x.Rank(); }},
+      u_);
+}
+int ComplexPart::Rank() const { return complex_.Rank(); }
+template<> int FunctionRef::Rank() const {
+  // TODO: Rank of elemental function reference depends on actual arguments
+  return std::visit(
+      common::visitors{[](IntrinsicProcedure) { return 0 /*TODO!!*/; },
+          [](const Symbol *sym) { return sym->Rank(); },
+          [](const Component &c) { return c.symbol().Rank(); }},
+      proc().u);
+}
+int Variable::Rank() const {
+  return std::visit([](const auto &x) { return x.Rank(); }, u);
+}
+int ActualFunctionArg::Rank() const {
+  return std::visit(
+      common::visitors{[](const CopyableIndirection<Expr<SomeType>> &x) {
+                         return x->Rank();
+                       },
+          [](const auto &x) { return x.Rank(); }},
+      u);
+}
+int ActualSubroutineArg::Rank() const {
+  return std::visit(
+      common::visitors{[](const CopyableIndirection<Expr<SomeType>> &x) {
+                         return x->Rank();
+                       },
+          [](const Label *) { return 0; },
+          [](const auto &x) { return x.Rank(); }},
+      u);
+}
+
 template class Designator<Type<TypeCategory::Character, 1>>;
 template class Designator<Type<TypeCategory::Character, 2>>;
 template class Designator<Type<TypeCategory::Character, 4>>;
index 277063c..d974475 100644 (file)
@@ -50,7 +50,7 @@ using IndirectSubscriptIntegerExpr =
 // R913 structure-component & C920: Defined to be a multi-part
 // data-ref whose last part has no subscripts (or image-selector, although
 // that isn't explicit in the document).  Pointer and allocatable components
-// are not explicitly indirected in this representation.
+// are not explicitly indirected in this representation (TODO: yet?)
 // Complex components (%RE, %IM) are isolated below in ComplexPart.
 class Component {
 public:
@@ -63,6 +63,7 @@ public:
   const DataRef &base() const { return *base_; }
   DataRef &base() { return *base_; }
   const Symbol &symbol() const { return *symbol_; }
+  int Rank() const;
   Expr<SubscriptInteger> LEN() const;
   std::ostream &Dump(std::ostream &) const;
 
@@ -93,6 +94,7 @@ struct Subscript {
   EVALUATE_UNION_CLASS_BOILERPLATE(Subscript)
   explicit Subscript(Expr<SubscriptInteger> &&s)
     : u{IndirectSubscriptIntegerExpr::Make(std::move(s))} {}
+  int Rank() const;
   std::ostream &Dump(std::ostream &) const;
   std::variant<IndirectSubscriptIntegerExpr, Triplet> u;
 };
@@ -108,6 +110,8 @@ struct ArrayRef {
     : u{&n}, subscript(std::move(ss)) {}
   ArrayRef(Component &&c, std::vector<Subscript> &&ss)
     : u{std::move(c)}, subscript(std::move(ss)) {}
+
+  int Rank() const;
   Expr<SubscriptInteger> LEN() const;
   std::ostream &Dump(std::ostream &) const;
 
@@ -130,6 +134,7 @@ public:
       std::vector<Expr<SubscriptInteger>> &&);  // TODO: stat & team?
   CoarrayRef &setStat(Variable &&);
   CoarrayRef &setTeam(Variable &&, bool isTeamNumber = false);
+  int Rank() const;
   Expr<SubscriptInteger> LEN() const;
   std::ostream &Dump(std::ostream &) const;
 
@@ -148,6 +153,8 @@ private:
 struct DataRef {
   EVALUATE_UNION_CLASS_BOILERPLATE(DataRef)
   explicit DataRef(const Symbol &n) : u{&n} {}
+
+  int Rank() const;
   Expr<SubscriptInteger> LEN() const;
   std::ostream &Dump(std::ostream &) const;
 
@@ -169,6 +176,7 @@ public:
 
   Expr<SubscriptInteger> first() const;
   Expr<SubscriptInteger> last() const;
+  int Rank() const;
   Expr<SubscriptInteger> LEN() const;
   std::optional<std::string> Fold(FoldingContext &);
   std::ostream &Dump(std::ostream &) const;
@@ -189,6 +197,7 @@ public:
   ComplexPart(DataRef &&z, Part p) : complex_{std::move(z)}, part_{p} {}
   const DataRef &complex() const { return complex_; }
   Part part() const { return part_; }
+  int Rank() const;
   std::ostream &Dump(std::ostream &) const;
 
 private:
@@ -220,7 +229,15 @@ public:
     return *this;
   }
 
+  int Rank() const {
+    return std::visit(
+        common::visitors{[](const Symbol *sym) { return sym->Rank(); },
+            [](const auto &x) { return x.Rank(); }},
+        u);
+  }
+
   Expr<SubscriptInteger> LEN() const;
+
   std::ostream &Dump(std::ostream &o) const {
     std::visit(common::visitors{[&](const Symbol *sym) {
                                   o << sym->name().ToString();
@@ -244,7 +261,6 @@ struct ProcedureDesignator {
   Expr<SubscriptInteger> LEN() const;
   std::ostream &Dump(std::ostream &) const;
 
-private:
   std::variant<IntrinsicProcedure, const Symbol *, Component> u;
 };
 
@@ -256,6 +272,7 @@ public:
     : proc_{std::move(p)}, argument_(std::move(a)) {}
   const ProcedureDesignator &proc() const { return proc_; }
   const std::vector<ArgumentType> &argument() const { return argument_; }
+  int Rank() const;
   std::ostream &Dump(std::ostream &) const;
 
 private:
@@ -267,6 +284,7 @@ using FunctionRef = ProcedureRef<ActualFunctionArg>;
 
 struct Variable {
   EVALUATE_UNION_CLASS_BOILERPLATE(Variable)
+  int Rank() const;
   std::ostream &Dump(std::ostream &) const;
   std::variant<DataRef, Substring, ComplexPart, FunctionRef> u;
 };
@@ -274,6 +292,7 @@ struct Variable {
 struct ActualFunctionArg {
   EVALUATE_UNION_CLASS_BOILERPLATE(ActualFunctionArg)
   explicit ActualFunctionArg(Expr<SomeType> &&x) : u{std::move(x)} {}
+  int Rank() const;
   std::ostream &Dump(std::ostream &) const;
 
   // Subtlety: There is a distinction to be respected here between a variable
@@ -293,6 +312,7 @@ public:
   EVALUATE_UNION_CLASS_BOILERPLATE(ActualSubroutineArg)
   explicit ActualSubroutineArg(Expr<SomeType> &&x) : u{std::move(x)} {}
   explicit ActualSubroutineArg(const Label &l) : u{&l} {}
+  int Rank() const;
   std::ostream &Dump(std::ostream &) const;
 
 public:
index b3da6bc..73dc0c7 100644 (file)
@@ -1622,22 +1622,24 @@ constexpr auto primary{instrumented("primary"_en_US,
 
 // R1002 level-1-expr -> [defined-unary-op] primary
 // TODO: Reasonable extension: permit multiple defined-unary-ops
-constexpr auto level1Expr{first(
+constexpr auto level1Expr{sourced(first(
     construct<Expr>(construct<Expr::DefinedUnary>(definedOpName, primary)),
     primary,
     extension<LanguageFeature::SignedPrimary>(
         construct<Expr>(construct<Expr::UnaryPlus>("+" >> primary))),
     extension<LanguageFeature::SignedPrimary>(
-        construct<Expr>(construct<Expr::Negate>("-" >> primary))))};
+        construct<Expr>(construct<Expr::Negate>("-" >> primary)))))};
 
 // R1004 mult-operand -> level-1-expr [power-op mult-operand]
 // R1007 power-op -> **
 // Exponentiation (**) is Fortran's only right-associative binary operation.
-constexpr struct MultOperand {
+struct MultOperand {
   using resultType = Expr;
   constexpr MultOperand() {}
   static inline std::optional<Expr> Parse(ParseState &);
-} multOperand;
+};
+
+static constexpr auto multOperand{sourced(MultOperand{})};
 
 inline std::optional<Expr> MultOperand::Parse(ParseState &state) {
   std::optional<Expr> result{level1Expr.Parse(state)};
@@ -1647,7 +1649,8 @@ inline std::optional<Expr> MultOperand::Parse(ParseState &state) {
       std::function<Expr(Expr &&)> power{[&result](Expr &&right) {
         return Expr{Expr::Power(std::move(result).value(), std::move(right))};
       }};
-      return applyLambda(power, multOperand).Parse(state);  // right-recursive
+      return sourced(applyLambda(power, multOperand))
+          .Parse(state);  // right-recursive
     }
   }
   return result;
@@ -1665,14 +1668,13 @@ constexpr struct AddOperand {
       std::function<Expr(Expr &&)> multiply{[&result](Expr &&right) {
         return Expr{
             Expr::Multiply(std::move(result).value(), std::move(right))};
-      }},
-          divide{[&result](Expr &&right) {
-            return Expr{
-                Expr::Divide(std::move(result).value(), std::move(right))};
-          }};
-      auto more{"*" >> applyLambda(multiply, multOperand) ||
-          "/" >> applyLambda(divide, multOperand)};
-      while (std::optional<Expr> next{attempt(more).Parse(state)}) {
+      }};
+      std::function<Expr(Expr &&)> divide{[&result](Expr &&right) {
+        return Expr{Expr::Divide(std::move(result).value(), std::move(right))};
+      }};
+      auto more{attempt(sourced("*" >> applyLambda(multiply, multOperand) ||
+          "/" >> applyLambda(divide, multOperand)))};
+      while (std::optional<Expr> next{more.Parse(state)}) {
         result = std::move(next);
       }
     }
@@ -1692,21 +1694,22 @@ constexpr struct Level2Expr {
   constexpr Level2Expr() {}
   static inline std::optional<Expr> Parse(ParseState &state) {
     static constexpr auto unary{
-        construct<Expr>(construct<Expr::UnaryPlus>("+" >> addOperand)) ||
-        construct<Expr>(construct<Expr::Negate>("-" >> addOperand)) ||
+        sourced(
+            construct<Expr>(construct<Expr::UnaryPlus>("+" >> addOperand)) ||
+            construct<Expr>(construct<Expr::Negate>("-" >> addOperand))) ||
         addOperand};
     std::optional<Expr> result{unary.Parse(state)};
     if (result) {
       std::function<Expr(Expr &&)> add{[&result](Expr &&right) {
         return Expr{Expr::Add(std::move(result).value(), std::move(right))};
-      }},
-          subtract{[&result](Expr &&right) {
-            return Expr{
-                Expr::Subtract(std::move(result).value(), std::move(right))};
-          }};
-      auto more{"+" >> applyLambda(add, addOperand) ||
-          "-" >> applyLambda(subtract, addOperand)};
-      while (std::optional<Expr> next{attempt(more).Parse(state)}) {
+      }};
+      std::function<Expr(Expr &&)> subtract{[&result](Expr &&right) {
+        return Expr{
+            Expr::Subtract(std::move(result).value(), std::move(right))};
+      }};
+      auto more{attempt(sourced("+" >> applyLambda(add, addOperand) ||
+          "-" >> applyLambda(subtract, addOperand)))};
+      while (std::optional<Expr> next{more.Parse(state)}) {
         result = std::move(next);
       }
     }
@@ -1727,8 +1730,8 @@ constexpr struct Level3Expr {
       std::function<Expr(Expr &&)> concat{[&result](Expr &&right) {
         return Expr{Expr::Concat(std::move(result).value(), std::move(right))};
       }};
-      auto more{"//" >> applyLambda(concat, level2Expr)};
-      while (std::optional<Expr> next{attempt(more).Parse(state)}) {
+      auto more{attempt(sourced("//" >> applyLambda(concat, level2Expr)))};
+      while (std::optional<Expr> next{more.Parse(state)}) {
         result = std::move(next);
       }
     }
@@ -1749,32 +1752,33 @@ constexpr struct Level4Expr {
     if (result) {
       std::function<Expr(Expr &&)> lt{[&result](Expr &&right) {
         return Expr{Expr::LT(std::move(result).value(), std::move(right))};
-      }},
-          le{[&result](Expr &&right) {
-            return Expr{Expr::LE(std::move(result).value(), std::move(right))};
-          }},
-          eq{[&result](Expr &&right) {
-            return Expr{Expr::EQ(std::move(result).value(), std::move(right))};
-          }},
-          ne{[&result](Expr &&right) {
-            return Expr{Expr::NE(std::move(result).value(), std::move(right))};
-          }},
-          ge{[&result](Expr &&right) {
-            return Expr{Expr::GE(std::move(result).value(), std::move(right))};
-          }},
-          gt{[&result](Expr &&right) {
-            return Expr{Expr::GT(std::move(result).value(), std::move(right))};
-          }};
-      auto more{(".LT."_tok || "<"_tok) >> applyLambda(lt, level3Expr) ||
-          (".LE."_tok || "<="_tok) >> applyLambda(le, level3Expr) ||
-          (".EQ."_tok || "=="_tok) >> applyLambda(eq, level3Expr) ||
-          (".NE."_tok || "/="_tok ||
-              extension<LanguageFeature::AlternativeNE>(
-                  "<>"_tok /* PGI/Cray extension; Cray also has .LG. */)) >>
-              applyLambda(ne, level3Expr) ||
-          (".GE."_tok || ">="_tok) >> applyLambda(ge, level3Expr) ||
-          (".GT."_tok || ">"_tok) >> applyLambda(gt, level3Expr)};
-      if (std::optional<Expr> next{attempt(more).Parse(state)}) {
+      }};
+      std::function<Expr(Expr &&)> le{[&result](Expr &&right) {
+        return Expr{Expr::LE(std::move(result).value(), std::move(right))};
+      }};
+      std::function<Expr(Expr &&)> eq{[&result](Expr &&right) {
+        return Expr{Expr::EQ(std::move(result).value(), std::move(right))};
+      }};
+      std::function<Expr(Expr &&)> ne{[&result](Expr &&right) {
+        return Expr{Expr::NE(std::move(result).value(), std::move(right))};
+      }};
+      std::function<Expr(Expr &&)> ge{[&result](Expr &&right) {
+        return Expr{Expr::GE(std::move(result).value(), std::move(right))};
+      }};
+      std::function<Expr(Expr &&)> gt{[&result](Expr &&right) {
+        return Expr{Expr::GT(std::move(result).value(), std::move(right))};
+      }};
+      auto more{attempt(
+          sourced((".LT."_tok || "<"_tok) >> applyLambda(lt, level3Expr) ||
+              (".LE."_tok || "<="_tok) >> applyLambda(le, level3Expr) ||
+              (".EQ."_tok || "=="_tok) >> applyLambda(eq, level3Expr) ||
+              (".NE."_tok || "/="_tok ||
+                  extension<LanguageFeature::AlternativeNE>(
+                      "<>"_tok /* PGI/Cray extension; Cray also has .LG. */)) >>
+                  applyLambda(ne, level3Expr) ||
+              (".GE."_tok || ">="_tok) >> applyLambda(ge, level3Expr) ||
+              (".GT."_tok || ">"_tok) >> applyLambda(gt, level3Expr)))};
+      if (std::optional<Expr> next{more.Parse(state)}) {
         return next;
       }
     }
@@ -1819,8 +1823,9 @@ constexpr struct OrOperand {
       std::function<Expr(Expr &&)> logicalAnd{[&result](Expr &&right) {
         return Expr{Expr::AND(std::move(result).value(), std::move(right))};
       }};
-      auto more{".AND." >> applyLambda(logicalAnd, andOperand)};
-      while (std::optional<Expr> next{attempt(more).Parse(state)}) {
+      auto more{
+          attempt(sourced(".AND." >> applyLambda(logicalAnd, andOperand)))};
+      while (std::optional<Expr> next{more.Parse(state)}) {
         result = std::move(next);
       }
     }
@@ -1840,8 +1845,8 @@ constexpr struct EquivOperand {
       std::function<Expr(Expr &&)> logicalOr{[&result](Expr &&right) {
         return Expr{Expr::OR(std::move(result).value(), std::move(right))};
       }};
-      auto more{".OR." >> applyLambda(logicalOr, orOperand)};
-      while (std::optional<Expr> next{attempt(more).Parse(state)}) {
+      auto more{attempt(sourced(".OR." >> applyLambda(logicalOr, orOperand)))};
+      while (std::optional<Expr> next{more.Parse(state)}) {
         result = std::move(next);
       }
     }
@@ -1861,19 +1866,18 @@ constexpr struct Level5Expr {
     if (result) {
       std::function<Expr(Expr &&)> eqv{[&result](Expr &&right) {
         return Expr{Expr::EQV(std::move(result).value(), std::move(right))};
-      }},
-          neqv{[&result](Expr &&right) {
-            return Expr{
-                Expr::NEQV(std::move(result).value(), std::move(right))};
-          }},
-          logicalXor{[&result](Expr &&right) {
-            return Expr{Expr::XOR(std::move(result).value(), std::move(right))};
-          }};
-      auto more{".EQV." >> applyLambda(eqv, equivOperand) ||
+      }};
+      std::function<Expr(Expr &&)> neqv{[&result](Expr &&right) {
+        return Expr{Expr::NEQV(std::move(result).value(), std::move(right))};
+      }};
+      std::function<Expr(Expr &&)> logicalXor{[&result](Expr &&right) {
+        return Expr{Expr::XOR(std::move(result).value(), std::move(right))};
+      }};
+      auto more{attempt(sourced(".EQV." >> applyLambda(eqv, equivOperand) ||
           ".NEQV." >> applyLambda(neqv, equivOperand) ||
           extension<LanguageFeature::XOROperator>(
-              ".XOR." >> applyLambda(logicalXor, equivOperand))};
-      while (std::optional<Expr> next{attempt(more).Parse(state)}) {
+              ".XOR." >> applyLambda(logicalXor, equivOperand))))};
+      while (std::optional<Expr> next{more.Parse(state)}) {
         result = std::move(next);
       }
     }
@@ -1891,8 +1895,9 @@ template<> inline std::optional<Expr> Parser<Expr>::Parse(ParseState &state) {
           return Expr{Expr::DefinedBinary(
               std::move(op), std::move(result).value(), std::move(right))};
         }};
-    auto more{applyLambda(defBinOp, definedOpName, level5Expr)};
-    while (std::optional<Expr> next{attempt(more).Parse(state)}) {
+    auto more{
+        attempt(sourced(applyLambda(defBinOp, definedOpName, level5Expr)))};
+    while (std::optional<Expr> next{more.Parse(state)}) {
       result = std::move(next);
     }
   }
index 7ca6595..ddca7aa 100644 (file)
@@ -1695,7 +1695,9 @@ struct Expr {
   explicit Expr(FunctionReference &&);
 
   // Filled in later during semantic analysis of the expression.
+  // TODO: May be temporary; remove if caching no longer required.
   common::OwningPointer<evaluate::GenericExprWrapper> typedExpr;
+  CharBlock source;
 
   std::variant<common::Indirection<CharLiteralConstantSubstring>,
       LiteralConstant, common::Indirection<Designator>, ArrayConstructor,
index bd67d43..eb71847 100644 (file)
@@ -47,6 +47,60 @@ template<typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) {
   return std::nullopt;
 }
 
+// If a generic expression simply wraps a DataRef, extract it.
+// TODO: put in tools.h?
+template<typename A> std::optional<DataRef> ExtractDataRef(A &&) {
+  return std::nullopt;
+}
+
+template<typename A> std::optional<DataRef> ExtractDataRef(Designator<A> &&d) {
+  return std::visit(
+      [](auto &&x) -> std::optional<DataRef> {
+        using Ty = std::decay_t<decltype(x)>;
+        if constexpr (common::HasMember<Ty, decltype(DataRef::u)>) {
+          return {DataRef{std::move(x)}};
+        }
+        return std::nullopt;
+      },
+      std::move(d.u));
+}
+
+template<TypeCategory CAT, int KIND>
+std::optional<DataRef> ExtractDataRef(Expr<Type<CAT, KIND>> &&expr) {
+  using Ty = ResultType<decltype(expr)>;
+  if (auto *designator{std::get_if<Designator<Ty>>(&expr.u)}) {
+    return ExtractDataRef(std::move(*designator));
+  } else {
+    return std::nullopt;
+  }
+}
+
+template<TypeCategory CAT>
+std::optional<DataRef> ExtractDataRef(Expr<SomeKind<CAT>> &&expr) {
+  return std::visit(
+      [](auto &&specificExpr) {
+        return ExtractDataRef(std::move(specificExpr));
+      },
+      std::move(expr.u));
+}
+
+template<> std::optional<DataRef> ExtractDataRef(Expr<SomeType> &&expr) {
+  return std::visit(
+      common::visitors{[](BOZLiteralConstant &&) -> std::optional<DataRef> {
+                         return std::nullopt;
+                       },
+          [](auto &&catExpr) { return ExtractDataRef(std::move(catExpr)); }},
+      std::move(expr.u));
+}
+
+template<typename A>
+std::optional<DataRef> ExtractDataRef(std::optional<A> &&x) {
+  if (x.has_value()) {
+    return ExtractDataRef(std::move(*x));
+  }
+  return std::nullopt;
+}
+
 // This local class wraps some state and a highly overloaded Analyze()
 // member function that converts parse trees into (usually) generic
 // expressions.
@@ -55,6 +109,11 @@ struct ExprAnalyzer {
       FoldingContext &ctx, const semantics::IntrinsicTypeDefaultKinds &dfts)
     : context{ctx}, defaults{dfts} {}
 
+  ExprAnalyzer(const ExprAnalyzer &that, const parser::CharBlock &source)
+    : context{that.context,
+          parser::ContextualMessages{source, that.context.messages}},
+      defaults{that.defaults} {}
+
   MaybeExpr Analyze(const parser::Expr &);
   MaybeExpr Analyze(const parser::CharLiteralConstantSubstring &);
   MaybeExpr Analyze(const parser::LiteralConstant &);
@@ -115,7 +174,9 @@ struct ExprAnalyzer {
       const std::optional<parser::Subscript> &);
   MaybeExpr Subscripts(const Symbol &, ArrayRef &&);
 
-  FoldingContext &context;
+  void ComponentRankCheck(const Component &);
+
+  FoldingContext context;
   const semantics::IntrinsicTypeDefaultKinds &defaults;
 };
 
@@ -134,8 +195,14 @@ template<typename A> MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const A &x) {
 
 template<typename A>
 MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const parser::Scalar<A> &x) {
-  // TODO: check rank == 0
-  return AnalyzeHelper(ea, x.thing);
+  if (MaybeExpr result{AnalyzeHelper(ea, x.thing)}) {
+    int rank{result->Rank()};
+    if (rank > 0) {
+      ea.context.messages.Say(
+          "expression must be scalar, but has rank %d"_err_en_US, rank);
+    }
+  }
+  return std::nullopt;
 }
 
 template<typename A>
@@ -170,11 +237,40 @@ MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const common::Indirection<A> &x) {
   return AnalyzeHelper(ea, *x);
 }
 
+template<>
+MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const parser::Designator &d) {
+  // These check have to be deferred to these "top level" data-refs where
+  // we can be sure that there are no following subscripts.
+  if (MaybeExpr result{AnalyzeHelper(ea, d.u)}) {
+    if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(result))}) {
+      if (Component * component{std::get_if<Component>(&dataRef->u)}) {
+        ea.ComponentRankCheck(*component);
+      } else if (const Symbol **symbol{
+                     std::get_if<const Symbol *>(&dataRef->u)}) {
+        // TODO: Whole array reference: append : subscripts, enforce C1002
+        // Possibly use EA::Subscripts() below.
+      }
+    }
+    return result;
+  }
+  return std::nullopt;
+}
+
+// Analyze something with source provenance
+template<typename A> MaybeExpr AnalyzeSourced(ExprAnalyzer &ea, const A &x) {
+  if (!x.source.empty()) {
+    ExprAnalyzer nestedAnalyzer{ea, x.source};
+    return AnalyzeHelper(nestedAnalyzer, x);
+  } else {
+    return AnalyzeHelper(ea, x);
+  }
+}
+
 // Implementations of ExprAnalyzer::Analyze follow for various parse tree
 // node types.
 
 MaybeExpr ExprAnalyzer::Analyze(const parser::Expr &x) {
-  return AnalyzeHelper(*this, x);
+  return AnalyzeSourced(*this, x);
 }
 
 int ExprAnalyzer::Analyze(const std::optional<parser::KindParam> &kindParam,
@@ -463,6 +559,8 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::NamedConstant &n) {
 
 MaybeExpr ExprAnalyzer::Analyze(const parser::Substring &ss) {
   context.messages.Say("TODO: Substring unimplemented"_err_en_US);
+  // TODO: be sure to run ComponentRankCheck() here on base of substring if
+  // it's a Component.
   return std::nullopt;
 }
 
@@ -471,6 +569,12 @@ std::optional<Expr<SubscriptInteger>> ExprAnalyzer::AsSubscript(
   if (expr.has_value()) {
     if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) {
       if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) {
+        int rank{ssIntExpr->Rank()};
+        if (rank > 1) {
+          context.messages.Say(
+              "subscript expression has rank %d"_err_en_US, rank);
+          return std::nullopt;
+        }
         return {std::move(*ssIntExpr)};
       }
       return {Expr<SubscriptInteger>{
@@ -512,6 +616,7 @@ std::optional<Subscript> ExprAnalyzer::Analyze(
 
 std::vector<Subscript> ExprAnalyzer::Analyze(
     const std::list<parser::SectionSubscript> &sss) {
+  // TODO: enforce restrictions on vector-valued subscripts
   std::vector<Subscript> subscripts;
   for (const auto &s : sss) {
     if (auto subscript{Analyze(s)}) {
@@ -521,71 +626,33 @@ std::vector<Subscript> ExprAnalyzer::Analyze(
   return subscripts;
 }
 
-// If a generic expression represents a DataRef, convert it to one.
-// TODO: put in tools.h?
-template<typename A> std::optional<DataRef> AsDataRef(A &&) {
-  return std::nullopt;
-}
-
-template<TypeCategory CAT, int KIND>
-std::optional<DataRef> AsDataRef(Expr<Type<CAT, KIND>> &&expr) {
-  using Ty = ResultType<decltype(expr)>;
-  if (auto *designator{std::get_if<Designator<Ty>>(&expr.u)}) {
-    return std::visit(
-        [](auto &&x) -> std::optional<DataRef> {
-          using Ty = std::decay_t<decltype(x)>;
-          if constexpr (common::HasMember<Ty, decltype(DataRef::u)>) {
-            return {DataRef{std::move(x)}};
-          }
-          return std::nullopt;
-        },
-        std::move(designator->u));
-  } else {
-    return std::nullopt;
+MaybeExpr ExprAnalyzer::Subscripts(const Symbol &symbol, ArrayRef &&ref) {
+  int symbolRank{symbol.Rank()};
+  if (ref.subscript.empty()) {
+    // A -> A(:,:)
+    for (int j{0}; j < symbolRank; ++j) {
+      ref.subscript.emplace_back(Subscript{Triplet{}});
+    }
   }
-}
-
-template<TypeCategory CAT>
-std::optional<DataRef> AsDataRef(Expr<SomeKind<CAT>> &&expr) {
-  return std::visit(
-      [](auto &&specificExpr) { return AsDataRef(std::move(specificExpr)); },
-      std::move(expr.u));
-}
-
-template<> std::optional<DataRef> AsDataRef(Expr<SomeType> &&expr) {
-  return std::visit(
-      common::visitors{[](BOZLiteralConstant &&) -> std::optional<DataRef> {
-                         return std::nullopt;
-                       },
-          [](auto &&catExpr) { return AsDataRef(std::move(catExpr)); }},
-      std::move(expr.u));
-}
-
-template<typename A> std::optional<DataRef> AsDataRef(std::optional<A> &&x) {
-  if (x.has_value()) {
-    return AsDataRef(std::move(*x));
+  int subscripts = ref.subscript.size();
+  if (subscripts != symbolRank) {
+    context.messages.Say(
+        "reference to rank-%d object '%s' has %d subscripts"_err_en_US,
+        symbolRank, symbol.name().ToString().data(), subscripts);
   }
-  return std::nullopt;
-}
-
-MaybeExpr ExprAnalyzer::Subscripts(const Symbol &symbol, ArrayRef &&ref) {
-  if (auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
-    int symbolRank = details->shape().size();
-    if (ref.subscript.empty()) {
-      // A -> A(:,:)
-      for (int j{0}; j < symbolRank; ++j) {
-        ref.subscript.emplace_back(Subscript{Triplet{}});
+  // TODO: fill in bounds of triplets?
+  // TODO: subtract lowers bounds?
+  // TODO: enforce constraints, like lack of uppermost bound on assumed-size
+  if (Component * component{std::get_if<Component>(&ref.u)}) {
+    int baseRank{component->Rank()};
+    if (baseRank > 0) {
+      int rank{ref.Rank()};
+      if (rank > 0) {
+        context.messages.Say(
+            "subscripts of rank-%d component reference have rank %d, but must all be scalar"_err_en_US,
+            baseRank, rank);
       }
     }
-    int subscripts = ref.subscript.size();
-    if (subscripts != symbolRank) {
-      context.messages.Say(
-          "reference to rank-%d object '%s' has %d subscripts"_err_en_US,
-          symbolRank, symbol.name().ToString().data(), subscripts);
-    }
-    // TODO: rank analysis, enforce vector-valued subscript constraints
-    // fill in bounds of triplets?
-    // subtract lowers bounds?
   }
   return Designate(symbol, DataRef{std::move(ref)});
 }
@@ -593,7 +660,7 @@ MaybeExpr ExprAnalyzer::Subscripts(const Symbol &symbol, ArrayRef &&ref) {
 MaybeExpr ExprAnalyzer::Analyze(const parser::ArrayElement &ae) {
   std::vector<Subscript> subscripts{Analyze(ae.subscripts)};
   if (MaybeExpr baseExpr{AnalyzeHelper(*this, ae.base)}) {
-    if (std::optional<DataRef> dataRef{AsDataRef(std::move(*baseExpr))}) {
+    if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(*baseExpr))}) {
       if (const Symbol **symbol{std::get_if<const Symbol *>(&dataRef->u)}) {
         return Subscripts(**symbol, ArrayRef{**symbol, std::move(subscripts)});
       } else if (Component * component{std::get_if<Component>(&dataRef->u)}) {
@@ -623,7 +690,7 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::StructureComponent &sc) {
             "component is not in scope of derived TYPE(%s)"_err_en_US,
             dtExpr->result.spec().name().ToString().data());
       } else if (std::optional<DataRef> dataRef{
-                     AsDataRef(std::move(*dtExpr))}) {
+                     ExtractDataRef(std::move(*dtExpr))}) {
         Component component{std::move(*dataRef), *sym};
         return Designate(*sym, DataRef{std::move(component)});
       } else {
@@ -639,7 +706,7 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::StructureComponent &sc) {
             "component of complex value must be %%RE or %%IM"_err_en_US);
         return std::nullopt;
       }
-      if (std::optional<DataRef> dataRef{AsDataRef(std::move(*zExpr))}) {
+      if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(*zExpr))}) {
         Expr<SomeReal> realExpr{std::visit(
             [&](const auto &z) {
               using PartType = typename ResultType<decltype(z)>::Part;
@@ -658,6 +725,7 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::StructureComponent &sc) {
 }
 
 MaybeExpr ExprAnalyzer::Analyze(const parser::CoindexedNamedObject &co) {
+  // TODO: ComponentRankCheck or its equivalent
   context.messages.Say("TODO: CoindexedNamedObject unimplemented"_err_en_US);
   return std::nullopt;
 }
@@ -767,6 +835,13 @@ 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)),
           AnalyzeHelper(ea, *std::get<1>(x.t)))}) {
+    int leftRank{std::get<0>(*both).Rank()};
+    int rightRank{std::get<1>(*both).Rank()};
+    if (leftRank > 0 && rightRank > 0 && leftRank != rightRank) {
+      ea.context.messages.Say(
+          "left operand has rank %d, right operand has rank %d"_err_en_US,
+          leftRank, rightRank);
+    }
     return NumericOperation<OPR>(ea.context.messages,
         std::move(std::get<0>(*both)), std::move(std::get<1>(*both)));
   }
@@ -916,6 +991,16 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedBinary &) {
   return std::nullopt;
 }
 
+void ExprAnalyzer::ComponentRankCheck(const Component &component) {
+  int baseRank{component.base().Rank()};
+  int componentRank{component.symbol().Rank()};
+  if (baseRank > 0 && componentRank > 0) {
+    context.messages.Say(
+        "reference to rank-%d component '%%%s' of rank-%d array of derived type is not allowed"_err_en_US,
+        componentRank, component.symbol().name().ToString().data(), baseRank);
+  }
+}
+
 }  // namespace Fortran::evaluate
 
 namespace Fortran::semantics {
index a1917a4..3d588d6 100644 (file)
@@ -244,6 +244,28 @@ bool Symbol::HasExplicitInterface() const {
       details_);
 }
 
+int Symbol::Rank() const {
+  return std::visit(
+      common::visitors{
+          [](const SubprogramDetails &sd) {
+            if (sd.isFunction()) {
+              return sd.result().Rank();
+            } else {
+              return 0;
+            }
+          },
+          [](const GenericDetails &) {
+            return 0; /*TODO*/
+          },
+          [](const UseDetails &x) { return x.symbol().Rank(); },
+          [](const ObjectEntityDetails &oed) {
+            return static_cast<int>(oed.shape().size());
+          },
+          [](const auto &) { return 0; },
+      },
+      details_);
+}
+
 ObjectEntityDetails::ObjectEntityDetails(const EntityDetails &d)
   : isDummy_{d.isDummy()}, type_{d.type()} {}
 
index bba8ec3..d27daf0 100644 (file)
@@ -341,6 +341,8 @@ public:
   bool operator==(const Symbol &that) const { return this == &that; }
   bool operator!=(const Symbol &that) const { return this != &that; }
 
+  int Rank() const;
+
 private:
   const Scope *owner_;
   std::list<SourceName> occurrences_;