[flang] Support coarrays in name resolution
authorTim Keith <tkeith@nvidia.com>
Thu, 4 Apr 2019 21:46:40 +0000 (14:46 -0700)
committerTim Keith <tkeith@nvidia.com>
Thu, 4 Apr 2019 21:47:59 +0000 (14:47 -0700)
A coarray is represented as a `Symbol` with `ObjectEntityDetails` that
has a non-empty coshape. The coshape is represented using the same type
(`ArrayShape`) as the shape is, so the fact that it is a coshape is
determined from context.

Move code for analyzing shapes to `resolve-names-utils.cc` and
generalize it for coshapes.

In `symbol.cc` add dumping of coshapes. Simplify some of the functions
by adding some `Dump*` functions to handle common cases.

In `mod-file.cc` generalize the code for writing shapes to also write
coshapes. Fix a bug in `PutShapeSpec()`.

Original-commit: flang-compiler/f18@9d2482c40c78cad55701a9cbc52a2294cae94d44
Reviewed-on: https://github.com/flang-compiler/f18/pull/384
Tree-same-pre-rewrite: false

flang/lib/semantics/mod-file.cc
flang/lib/semantics/resolve-names-utils.cc
flang/lib/semantics/resolve-names-utils.h
flang/lib/semantics/resolve-names.cc
flang/lib/semantics/symbol.cc
flang/lib/semantics/symbol.h
flang/test/semantics/CMakeLists.txt
flang/test/semantics/modfile24.f90 [new file with mode: 0644]
flang/test/semantics/resolve07.f90

index d0c0500..2fc07a8 100644 (file)
@@ -424,7 +424,7 @@ void PutEntity(std::ostream &os, const Symbol &symbol) {
 }
 
 void PutShapeSpec(std::ostream &os, const ShapeSpec &x) {
-  if (x.ubound().isAssumed()) {
+  if (x.lbound().isAssumed()) {
     CHECK(x.ubound().isAssumed());
     os << "..";
   } else {
@@ -437,9 +437,9 @@ void PutShapeSpec(std::ostream &os, const ShapeSpec &x) {
     }
   }
 }
-void PutShape(std::ostream &os, const ArraySpec &shape) {
+void PutShape(std::ostream &os, const ArraySpec &shape, char open, char close) {
   if (!shape.empty()) {
-    os << '(';
+    os << open;
     bool first{true};
     for (const auto &shapeSpec : shape) {
       if (first) {
@@ -449,7 +449,7 @@ void PutShape(std::ostream &os, const ArraySpec &shape) {
       }
       PutShapeSpec(os, shapeSpec);
     }
-    os << ')';
+    os << close;
   }
 }
 
@@ -460,7 +460,8 @@ void PutObjectEntity(std::ostream &os, const Symbol &symbol) {
     CHECK(type);
     PutLower(os, *type);
   });
-  PutShape(os, details.shape());
+  PutShape(os, details.shape(), '(', ')');
+  PutShape(os, details.coshape(), '[', ']');
   PutInit(os, details.init());
 }
 
@@ -816,6 +817,10 @@ void SubprogramSymbolCollector::DoSymbol(const Symbol &symbol) {
               DoBound(spec.lbound());
               DoBound(spec.ubound());
             }
+            for (const ShapeSpec &spec : details.coshape()) {
+              DoBound(spec.lbound());
+              DoBound(spec.ubound());
+            }
             if (const Symbol * commonBlock{details.commonBlock()}) {
               DoSymbol(*commonBlock);
             }
index ef30339..3056f9d 100644 (file)
 // limitations under the License.
 
 #include "resolve-names-utils.h"
+#include "expression.h"
 #include "semantics.h"
-#include "symbol.h"
-#include "type.h"
 #include "../common/idioms.h"
+#include "../evaluate/fold.h"
+#include "../evaluate/tools.h"
+#include "../evaluate/type.h"
 #include "../parser/char-block.h"
 #include "../parser/features.h"
 #include "../parser/parse-tree.h"
@@ -163,4 +165,114 @@ static GenericKind MapIntrinsicOperator(IntrinsicOperator op) {
   }
 }
 
+class ArraySpecAnalyzer {
+public:
+  ArraySpecAnalyzer(ArraySpec &arraySpec, SemanticsContext &context)
+    : context_{context}, arraySpec_{arraySpec} {
+    CHECK(arraySpec.empty());
+  }
+  void Analyze(const parser::ArraySpec &);
+  void Analyze(const parser::CoarraySpec &);
+
+private:
+  SemanticsContext &context_;
+  ArraySpec &arraySpec_;
+
+  template<typename T> void Analyze(const std::list<T> &list) {
+    for (const auto &elem : list) {
+      Analyze(elem);
+    }
+  }
+  void Analyze(const parser::AssumedShapeSpec &);
+  void Analyze(const parser::ExplicitShapeSpec &);
+  void Analyze(const parser::AssumedImpliedSpec &);
+  void Analyze(const parser::AssumedRankSpec &);
+  void MakeExplicit(const std::optional<parser::SpecificationExpr> &,
+      const parser::SpecificationExpr &);
+  void MakeImplied(const std::optional<parser::SpecificationExpr> &);
+  void MakeDeferred(int);
+  Bound GetBound(const std::optional<parser::SpecificationExpr> &);
+  Bound GetBound(const parser::SpecificationExpr &);
+};
+
+void AnalyzeArraySpec(ArraySpec &result, SemanticsContext &context,
+    const parser::ArraySpec &arraySpec) {
+  ArraySpecAnalyzer{result, context}.Analyze(arraySpec);
+}
+void AnalyzeCoarraySpec(ArraySpec &result, SemanticsContext &context,
+    const parser::CoarraySpec &coarraySpec) {
+  ArraySpecAnalyzer{result, context}.Analyze(coarraySpec);
+}
+
+void ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
+  std::visit(
+      common::visitors{
+          [&](const parser::DeferredShapeSpecList &y) { MakeDeferred(y.v); },
+          [&](const parser::AssumedSizeSpec &y) {
+            Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
+            Analyze(std::get<parser::AssumedImpliedSpec>(y.t));
+          },
+          [&](const parser::ImpliedShapeSpec &y) { Analyze(y.v); },
+          [&](const auto &y) { Analyze(y); },
+      },
+      x.u);
+}
+void ArraySpecAnalyzer::Analyze(const parser::CoarraySpec &x) {
+  std::visit(
+      common::visitors{
+          [&](const parser::DeferredCoshapeSpecList &y) { MakeDeferred(y.v); },
+          [&](const parser::ExplicitCoshapeSpec &y) {
+            Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
+            MakeImplied(
+                std::get<std::optional<parser::SpecificationExpr>>(y.t));
+          },
+      },
+      x.u);
+}
+
+void ArraySpecAnalyzer::Analyze(const parser::AssumedShapeSpec &x) {
+  arraySpec_.push_back(ShapeSpec::MakeAssumed(GetBound(x.v)));
+}
+void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeSpec &x) {
+  MakeExplicit(std::get<std::optional<parser::SpecificationExpr>>(x.t),
+      std::get<parser::SpecificationExpr>(x.t));
+}
+void ArraySpecAnalyzer::Analyze(const parser::AssumedImpliedSpec &x) {
+  MakeImplied(x.v);
+}
+void ArraySpecAnalyzer::Analyze(const parser::AssumedRankSpec &) {
+  arraySpec_.push_back(ShapeSpec::MakeAssumedRank());
+}
+
+void ArraySpecAnalyzer::MakeExplicit(
+    const std::optional<parser::SpecificationExpr> &lb,
+    const parser::SpecificationExpr &ub) {
+  arraySpec_.push_back(ShapeSpec::MakeExplicit(GetBound(lb), GetBound(ub)));
+}
+void ArraySpecAnalyzer::MakeImplied(
+    const std::optional<parser::SpecificationExpr> &lb) {
+  arraySpec_.push_back(ShapeSpec::MakeImplied(GetBound(lb)));
+}
+void ArraySpecAnalyzer::MakeDeferred(int n) {
+  for (int i = 0; i < n; ++i) {
+    arraySpec_.push_back(ShapeSpec::MakeDeferred());
+  }
+}
+
+Bound ArraySpecAnalyzer::GetBound(
+    const std::optional<parser::SpecificationExpr> &x) {
+  return x ? GetBound(*x) : Bound{1};
+}
+Bound ArraySpecAnalyzer::GetBound(const parser::SpecificationExpr &x) {
+  MaybeSubscriptIntExpr expr;
+  if (MaybeExpr maybeExpr{AnalyzeExpr(context_, x.v)}) {
+    if (auto *intExpr{evaluate::UnwrapExpr<SomeIntExpr>(*maybeExpr)}) {
+      expr = evaluate::Fold(context_.foldingContext(),
+          evaluate::ConvertToType<evaluate::SubscriptInteger>(
+              std::move(*intExpr)));
+    }
+  }
+  return Bound{std::move(expr)};
+}
+
 }
index a660667..0f210d2 100644 (file)
 // Utility functions and class for use in resolve-names.cc.
 
 #include "symbol.h"
+#include "type.h"
 #include "../parser/message.h"
 
 namespace Fortran::parser {
 class CharBlock;
+struct ArraySpec;
+struct CoarraySpec;
 struct DefinedOpName;
 struct GenericSpec;
 struct Name;
@@ -30,6 +33,7 @@ struct Name;
 namespace Fortran::semantics {
 
 using SourceName = parser::CharBlock;
+class SemanticsContext;
 
 // Record that a Name has been resolved to a Symbol
 Symbol &Resolve(const parser::Name &, Symbol &);
@@ -64,6 +68,12 @@ private:
   void Analyze(const parser::GenericSpec &);
 };
 
+// Analyze a parser::ArraySpec or parser::CoarraySpec into the provide ArraySpec
+void AnalyzeArraySpec(
+    ArraySpec &, SemanticsContext &, const parser::ArraySpec &);
+void AnalyzeCoarraySpec(
+    ArraySpec &, SemanticsContext &, const parser::CoarraySpec &);
+
 }
 
 #endif  // FORTRAN_SEMANTICS_RESOLVE_NAMES_H_
index 2cd93d2..47328a4 100644 (file)
@@ -353,30 +353,29 @@ private:
 // 6. TODO: BasedPointerStmt
 class ArraySpecVisitor : public virtual BaseVisitor {
 public:
-  bool Pre(const parser::ArraySpec &);
+  void Post(const parser::ArraySpec &);
+  void Post(const parser::CoarraySpec &);
   void Post(const parser::AttrSpec &) { PostAttrSpec(); }
   void Post(const parser::ComponentAttrSpec &) { PostAttrSpec(); }
-  void Post(const parser::DeferredShapeSpecList &);
-  void Post(const parser::AssumedShapeSpec &);
-  void Post(const parser::ExplicitShapeSpec &);
-  void Post(const parser::AssumedImpliedSpec &);
-  void Post(const parser::AssumedRankSpec &);
 
 protected:
   const ArraySpec &arraySpec();
+  const ArraySpec &coarraySpec();
   void BeginArraySpec();
   void EndArraySpec();
   void ClearArraySpec() { arraySpec_.clear(); }
+  void ClearCoarraySpec() { coarraySpec_.clear(); }
 
 private:
-  // arraySpec_ is populated by any ArraySpec
+  // arraySpec_/coarraySpec_ are populated from any ArraySpec/CoarraySpec
   ArraySpec arraySpec_;
+  ArraySpec coarraySpec_;
   // When an ArraySpec is under an AttrSpec or ComponentAttrSpec, it is moved
   // into attrArraySpec_
   ArraySpec attrArraySpec_;
+  ArraySpec attrCoarraySpec_;
 
   void PostAttrSpec();
-  Bound GetBound(const parser::SpecificationExpr &);
 };
 
 // Manage a stack of Scopes
@@ -649,7 +648,6 @@ class DeclarationVisitor : public ArraySpecVisitor,
                            public virtual ScopeHandler {
 public:
   using ArraySpecVisitor::Post;
-  using ArraySpecVisitor::Pre;
   using ScopeHandler::Post;
   using ScopeHandler::Pre;
 
@@ -681,6 +679,7 @@ public:
   }
   void Post(const parser::TargetStmt &) { objectDeclAttr_ = std::nullopt; }
   void Post(const parser::DimensionStmt::Declaration &);
+  void Post(const parser::CodimensionDecl &);
   bool Pre(const parser::TypeDeclarationStmt &) { return BeginDecl(); }
   void Post(const parser::TypeDeclarationStmt &) { EndDecl(); }
   void Post(const parser::IntegerTypeSpec &);
@@ -952,7 +951,6 @@ class ResolveNamesVisitor : public virtual ScopeHandler,
                             public ConstructVisitor {
 public:
   using ArraySpecVisitor::Post;
-  using ArraySpecVisitor::Pre;
   using ConstructVisitor::Post;
   using ConstructVisitor::Pre;
   using DeclarationVisitor::Post;
@@ -1420,65 +1418,42 @@ bool ImplicitRulesVisitor::HandleImplicitNone(
 
 // ArraySpecVisitor implementation
 
-bool ArraySpecVisitor::Pre(const parser::ArraySpec &x) {
-  CHECK(arraySpec_.empty());
-  return true;
+void ArraySpecVisitor::Post(const parser::ArraySpec &x) {
+  AnalyzeArraySpec(arraySpec_, context(), x);
 }
-
-void ArraySpecVisitor::Post(const parser::DeferredShapeSpecList &x) {
-  for (int i = 0; i < x.v; ++i) {
-    arraySpec_.push_back(ShapeSpec::MakeDeferred());
-  }
-}
-
-void ArraySpecVisitor::Post(const parser::AssumedShapeSpec &x) {
-  const auto &lb{x.v};
-  arraySpec_.push_back(
-      lb ? ShapeSpec::MakeAssumed(GetBound(*lb)) : ShapeSpec::MakeAssumed());
-}
-
-void ArraySpecVisitor::Post(const parser::ExplicitShapeSpec &x) {
-  auto &&ub{GetBound(std::get<parser::SpecificationExpr>(x.t))};
-  if (const auto &lb{std::get<std::optional<parser::SpecificationExpr>>(x.t)}) {
-    arraySpec_.push_back(ShapeSpec::MakeExplicit(GetBound(*lb), std::move(ub)));
-  } else {
-    arraySpec_.push_back(ShapeSpec::MakeExplicit(Bound{1}, std::move(ub)));
-  }
-}
-
-void ArraySpecVisitor::Post(const parser::AssumedImpliedSpec &x) {
-  const auto &lb{x.v};
-  arraySpec_.push_back(
-      lb ? ShapeSpec::MakeImplied(GetBound(*lb)) : ShapeSpec::MakeImplied());
-}
-
-void ArraySpecVisitor::Post(const parser::AssumedRankSpec &) {
-  arraySpec_.push_back(ShapeSpec::MakeAssumedRank());
+void ArraySpecVisitor::Post(const parser::CoarraySpec &x) {
+  AnalyzeCoarraySpec(coarraySpec_, context(), x);
 }
 
 const ArraySpec &ArraySpecVisitor::arraySpec() {
   return !arraySpec_.empty() ? arraySpec_ : attrArraySpec_;
 }
+const ArraySpec &ArraySpecVisitor::coarraySpec() {
+  return !coarraySpec_.empty() ? coarraySpec_ : attrCoarraySpec_;
+}
 void ArraySpecVisitor::BeginArraySpec() {
   CHECK(arraySpec_.empty());
+  CHECK(coarraySpec_.empty());
   CHECK(attrArraySpec_.empty());
+  CHECK(attrCoarraySpec_.empty());
 }
 void ArraySpecVisitor::EndArraySpec() {
   CHECK(arraySpec_.empty());
+  CHECK(coarraySpec_.empty());
   attrArraySpec_.clear();
+  attrCoarraySpec_.clear();
 }
 void ArraySpecVisitor::PostAttrSpec() {
+  // Save dimension/codimension from attrs so we can process array/coarray-spec
+  // on the entity-decl
   if (!arraySpec_.empty()) {
-    // Example: integer, dimension(<1>) :: x(<2>)
-    // This saves <1> in attrArraySpec_ so we can process <2> into arraySpec_
     CHECK(attrArraySpec_.empty());
     attrArraySpec_.splice(attrArraySpec_.cbegin(), arraySpec_);
-    CHECK(arraySpec_.empty());
   }
-}
-
-Bound ArraySpecVisitor::GetBound(const parser::SpecificationExpr &x) {
-  return Bound{EvaluateSubscriptIntExpr(x.v)};
+  if (!coarraySpec_.empty()) {
+    CHECK(attrCoarraySpec_.empty());
+    attrCoarraySpec_.splice(attrCoarraySpec_.cbegin(), coarraySpec_);
+  }
 }
 
 // ScopeHandler implementation
@@ -2554,6 +2529,11 @@ void DeclarationVisitor::Post(const parser::DimensionStmt::Declaration &x) {
   const auto &name{std::get<parser::Name>(x.t)};
   DeclareObjectEntity(name, Attrs{});
 }
+void DeclarationVisitor::Post(const parser::CodimensionDecl &x) {
+  const auto &name{std::get<parser::Name>(x.t)};
+  DeclareObjectEntity(name, Attrs{});
+}
+//TODO: ChangeTeamStmt also uses CodimensionDecl
 
 void DeclarationVisitor::Post(const parser::EntityDecl &x) {
   // TODO: may be under StructureStmt
@@ -2703,7 +2683,7 @@ void DeclarationVisitor::Post(const parser::ObjectDecl &x) {
 // Declare an entity not yet known to be an object or proc.
 Symbol &DeclarationVisitor::DeclareUnknownEntity(
     const parser::Name &name, Attrs attrs) {
-  if (!arraySpec().empty()) {
+  if (!arraySpec().empty() || !coarraySpec().empty()) {
     return DeclareObjectEntity(name, attrs);
   } else {
     Symbol &symbol{DeclareEntity<EntityDetails>(name, attrs)};
@@ -2752,6 +2732,15 @@ Symbol &DeclarationVisitor::DeclareObjectEntity(
       }
       ClearArraySpec();
     }
+    if (!coarraySpec().empty()) {
+      if (details->IsCoarray()) {
+        Say(name,
+            "The codimensions of '%s' have already been declared"_err_en_US);
+      } else {
+        details->set_coshape(coarraySpec());
+      }
+      ClearCoarraySpec();
+    }
     SetBindNameOn(symbol);
   }
   return symbol;
@@ -3110,6 +3099,7 @@ void DeclarationVisitor::Post(const parser::ComponentDecl &x) {
     currScope().symbol()->get<DerivedTypeDetails>().add_component(symbol);
   }
   ClearArraySpec();
+  ClearCoarraySpec();
 }
 bool DeclarationVisitor::Pre(const parser::ProcedureDeclarationStmt &) {
   CHECK(!interfaceName_);
@@ -3414,6 +3404,7 @@ void DeclarationVisitor::Post(const parser::CommonBlockObject &x) {
   const auto &name{std::get<parser::Name>(x.t)};
   auto &symbol{DeclareObjectEntity(name, Attrs{})};
   ClearArraySpec();
+  ClearCoarraySpec();
   auto *details{symbol.detailsIf<ObjectEntityDetails>()};
   if (!details) {
     return;  // error was reported
index b474670..f7cdd27 100644 (file)
@@ -26,6 +26,52 @@ std::ostream &operator<<(std::ostream &os, const parser::CharBlock &name) {
   return os << name.ToString();
 }
 
+template<typename T>
+static void DumpOptional(
+    std::ostream &os, const char *label, const std::optional<T> &x) {
+  if (x) {
+    os << ' ' << label << ':' << *x;
+  }
+}
+
+static void DumpBool(std::ostream &os, const char *label, bool x) {
+  if (x) {
+    os << ' ' << label;
+  }
+}
+
+static void DumpSymbolList(std::ostream &os, const SymbolList &list) {
+  char sep{' '};
+  for (const auto *elem : list) {
+    os << sep << elem->name();
+    sep = ',';
+  }
+}
+
+static void DumpType(std::ostream &os, const Symbol &symbol) {
+  if (const auto *type{symbol.GetType()}) {
+    os << *type << ' ';
+  }
+}
+static void DumpType(std::ostream &os, const DeclTypeSpec *type) {
+  if (type) {
+    os << ' ' << *type;
+  }
+}
+
+template<typename T>
+static void DumpList(
+    std::ostream &os, const char *label, const std::list<T> &list) {
+  if (!list.empty()) {
+    os << ' ' << label << ':';
+    char sep{' '};
+    for (const auto &elem : list) {
+      os << sep << elem;
+      sep = ',';
+    }
+  }
+}
+
 const Scope *ModuleDetails::parent() const {
   return isSubmodule_ && scope_ ? &scope_->parent() : nullptr;
 }
@@ -49,12 +95,8 @@ void ModuleDetails::set_scope(const Scope *scope) {
 }
 
 std::ostream &operator<<(std::ostream &os, const SubprogramDetails &x) {
-  if (x.isInterface_) {
-    os << " isInterface";
-  }
-  if (x.bindName_) {
-    os << " bindName:" << x.bindName_;
-  }
+  DumpBool(os, "isInterface", x.isInterface_);
+  DumpOptional(os, "bindName", x.bindName_);
   if (x.result_) {
     os << " result:" << x.result_->name();
     if (!x.result_->attrs().empty()) {
@@ -86,6 +128,12 @@ void ObjectEntityDetails::set_shape(const ArraySpec &shape) {
     shape_.push_back(shapeSpec);
   }
 }
+void ObjectEntityDetails::set_coshape(const ArraySpec &coshape) {
+  CHECK(coshape_.empty());
+  for (const auto &shapeSpec : coshape) {
+    coshape_.push_back(shapeSpec);
+  }
+}
 
 ProcEntityDetails::ProcEntityDetails(EntityDetails &&d) : EntityDetails(d) {
   if (type()) {
@@ -268,75 +316,43 @@ ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d)
   : EntityDetails(d) {}
 
 std::ostream &operator<<(std::ostream &os, const EntityDetails &x) {
-  if (x.isDummy()) {
-    os << " dummy";
-  }
-  if (x.isFuncResult()) {
-    os << " funcResult";
-  }
+  DumpBool(os, "dummy", x.isDummy());
+  DumpBool(os, "funcResult", x.isFuncResult());
   if (x.type()) {
     os << " type: " << *x.type();
   }
-  if (x.bindName_) {
-    os << " bindName:" << x.bindName_;
-  }
+  DumpOptional(os, "bindName", x.bindName_);
   return os;
 }
 
 std::ostream &operator<<(std::ostream &os, const ObjectEntityDetails &x) {
   os << *static_cast<const EntityDetails *>(&x);
-  if (!x.shape().empty()) {
-    os << " shape:";
-    for (const auto &s : x.shape()) {
-      os << ' ' << s;
-    }
-  }
-  if (x.init_) {
-    os << " init:" << x.init_;
-  }
+  DumpList(os, "shape", x.shape());
+  DumpList(os, "coshape", x.coshape());
+  DumpOptional(os, "init", x.init_);
   return os;
 }
 
 std::ostream &operator<<(std::ostream &os, const AssocEntityDetails &x) {
   os << *static_cast<const EntityDetails *>(&x);
-  if (x.expr().has_value()) {
-    os << ' ' << x.expr();
-  }
+  DumpOptional(os, "expr", x.expr());
   return os;
 }
 
 std::ostream &operator<<(std::ostream &os, const ProcEntityDetails &x) {
   if (auto *symbol{x.interface_.symbol()}) {
     os << ' ' << symbol->name();
-  } else if (auto *type{x.interface_.type()}) {
-    os << ' ' << *type;
-  }
-  if (x.bindName()) {
-    os << " bindName:" << x.bindName();
-  }
-  if (x.passName_) {
-    os << " passName:" << *x.passName_;
+  } else {
+    DumpType(os, x.interface_.type());
   }
+  DumpOptional(os, "bindName", x.bindName());
+  DumpOptional(os, "passName", x.passName());
   return os;
 }
 
 std::ostream &operator<<(std::ostream &os, const DerivedTypeDetails &x) {
-  if (x.sequence_) {
-    os << " sequence";
-  }
-  if (!x.componentNames_.empty()) {
-    os << " components:";
-    for (auto name : x.componentNames_) {
-      os << ' ' << name.ToString();
-    }
-  }
-  return os;
-}
-
-static std::ostream &DumpType(std::ostream &os, const Symbol &symbol) {
-  if (const auto *type{symbol.GetType()}) {
-    os << *type << ' ';
-  }
+  DumpBool(os, "sequence", x.sequence_);
+  DumpList(os, "components", x.componentNames_);
   return os;
 }
 
@@ -371,17 +387,13 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
               os << dummy->name();
             }
             os << ')';
-            if (x.bindName()) {
-              os << " bindName:" << x.bindName();
-            }
+            DumpOptional(os, "bindName", x.bindName());
             if (x.isFunction()) {
               os << " result(";
               DumpType(os, x.result());
               os << x.result().name() << ')';
             }
-            if (x.isInterface()) {
-              os << " interface";
-            }
+            DumpBool(os, "interface", x.isInterface());
           },
           [&](const SubprogramNameDetails &x) {
             os << ' ' << EnumToString(x.kind());
@@ -398,29 +410,19 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
           [](const HostAssocDetails &) {},
           [&](const GenericDetails &x) {
             os << ' ' << EnumToString(x.kind());
-            for (const auto *proc : x.specificProcs()) {
-              os << ' ' << proc->name();
-            }
+            DumpSymbolList(os, x.specificProcs());
           },
           [&](const ProcBindingDetails &x) {
             os << " => " << x.symbol().name();
-            if (x.passName()) {
-              os << " passName:" << *x.passName();
-            }
+            DumpOptional(os, "passName", x.passName());
           },
           [&](const GenericBindingDetails &x) {
             os << " =>";
-            char sep{' '};
-            for (const auto *proc : x.specificProcs()) {
-              os << sep << proc->name();
-              sep = ',';
-            }
+            DumpSymbolList(os, x.specificProcs());
           },
           [&](const NamelistDetails &x) {
             os << ':';
-            for (const auto *object : x.objects()) {
-              os << ' ' << object->name();
-            }
+            DumpSymbolList(os, x.objects());
           },
           [&](const CommonBlockDetails &x) {
             os << ':';
@@ -434,9 +436,7 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
               os << ' ' << *x.type();
             }
             os << ' ' << common::EnumToString(x.attr());
-            if (x.init()) {
-              os << " init:" << x.init();
-            }
+            DumpOptional(os, "init", x.init());
           },
           [&](const MiscDetails &x) {
             os << ' ' << MiscDetails::EnumToString(x.kind());
@@ -512,22 +512,12 @@ std::ostream &DumpForUnparse(
     if (!symbol.attrs().empty()) {
       os << ' ' << symbol.attrs();
     }
-    if (symbol.test(Symbol::Flag::Implicit)) {
-      os << " (implicit)";
-    }
-    if (symbol.test(Symbol::Flag::LocalityLocal)) {
-      os << " (local)";
-    }
-    if (symbol.test(Symbol::Flag::LocalityLocalInit)) {
-      os << " (local_init)";
-    }
-    if (symbol.test(Symbol::Flag::LocalityShared)) {
-      os << " (shared)";
-    }
+    DumpBool(os, "(implicit)", symbol.test(Symbol::Flag::Implicit));
+    DumpBool(os, "(local)", symbol.test(Symbol::Flag::LocalityLocal));
+    DumpBool(os, "(local_init)", symbol.test(Symbol::Flag::LocalityLocalInit));
+    DumpBool(os, "(shared)", symbol.test(Symbol::Flag::LocalityShared));
     os << ' ' << symbol.GetDetailsName();
-    if (const auto *type{symbol.GetType()}) {
-      os << ' ' << *type;
-    }
+    DumpType(os, symbol.GetType());
   }
   return os;
 }
@@ -591,7 +581,16 @@ Symbol &Symbol::Instantiate(
                     foldingContext, std::move(dim.ubound().GetExplicit())));
               }
             }
-            // TODO: fold cobounds too once we can represent them
+            for (ShapeSpec &dim : details.coshape()) {
+              if (dim.lbound().isExplicit()) {
+                dim.lbound().SetExplicit(Fold(
+                    foldingContext, std::move(dim.lbound().GetExplicit())));
+              }
+              if (dim.ubound().isExplicit()) {
+                dim.ubound().SetExplicit(Fold(
+                    foldingContext, std::move(dim.ubound().GetExplicit())));
+              }
+            }
           },
           [&](const ProcBindingDetails &that) { symbol.details_ = that; },
           [&](const GenericBindingDetails &that) { symbol.details_ = that; },
@@ -698,4 +697,5 @@ void TypeParamDetails::set_type(const DeclTypeSpec &type) {
   CHECK(type_ == nullptr);
   type_ = &type;
 }
+
 }
index f94fd9f..6a7f28c 100644 (file)
@@ -150,10 +150,14 @@ public:
   void set_init(MaybeExpr &&expr) { init_ = std::move(expr); }
   ArraySpec &shape() { return shape_; }
   const ArraySpec &shape() const { return shape_; }
-  void set_shape(const ArraySpec &shape);
+  ArraySpec &coshape() { return coshape_; }
+  const ArraySpec &coshape() const { return coshape_; }
+  void set_shape(const ArraySpec &);
+  void set_coshape(const ArraySpec &);
   const Symbol *commonBlock() const { return commonBlock_; }    
   void set_commonBlock(const Symbol &commonBlock) { commonBlock_ = &commonBlock; }
   bool IsArray() const { return !shape_.empty(); }
+  bool IsCoarray() const { return !coshape_.empty(); }
   bool IsAssumedShape() const {
     return isDummy() && IsArray() && shape_.back().ubound().isDeferred() &&
         !shape_.back().lbound().isDeferred();
@@ -174,6 +178,7 @@ public:
 private:
   MaybeExpr init_;
   ArraySpec shape_;
+  ArraySpec coshape_;
   const Symbol *commonBlock_{nullptr};  // common block this object is in
   friend std::ostream &operator<<(std::ostream &, const ObjectEntityDetails &);
 };
@@ -529,11 +534,7 @@ public:
     return std::visit(
         common::visitors{
             [](const SubprogramDetails &sd) {
-              if (sd.isFunction()) {
-                return sd.result().Rank();
-              } else {
-                return 0;
-              }
+              return sd.isFunction() ? sd.result().Rank() : 0;
             },
             [](const GenericDetails &) {
               return 0; /*TODO*/
@@ -548,6 +549,25 @@ public:
         details_);
   }
 
+  int Corank() const {
+    return std::visit(
+        common::visitors{
+            [](const SubprogramDetails &sd) {
+              return sd.isFunction() ? sd.result().Corank() : 0;
+            },
+            [](const GenericDetails &) {
+              return 0; /*TODO*/
+            },
+            [](const UseDetails &x) { return x.symbol().Corank(); },
+            [](const HostAssocDetails &x) { return x.symbol().Corank(); },
+            [](const ObjectEntityDetails &oed) {
+              return static_cast<int>(oed.coshape().size());
+            },
+            [](const auto &) { return 0; },
+        },
+        details_);
+  }
+
   // Clones the Symbol in the context of a parameterized derived type instance
   Symbol &Instantiate(Scope &, SemanticsContext &) const;
 
index 352f4a9..c63b9b1 100644 (file)
@@ -134,6 +134,7 @@ set(MODFILE_TESTS
   modfile21.f90
   modfile22.f90
   modfile23.f90
+  modfile24.f90
 )
 
 set(LABEL_TESTS
diff --git a/flang/test/semantics/modfile24.f90 b/flang/test/semantics/modfile24.f90
new file mode 100644 (file)
index 0000000..bdd814d
--- /dev/null
@@ -0,0 +1,88 @@
+! Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
+!
+! Licensed under the Apache License, Version 2.0 (the "License");
+! you may not use this file except in compliance with the License.
+! You may obtain a copy of the License at
+!
+!     http://www.apache.org/licenses/LICENSE-2.0
+!
+! Unless required by applicable law or agreed to in writing, software
+! distributed under the License is distributed on an "AS IS" BASIS,
+! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+! See the License for the specific language governing permissions and
+! limitations under the License.
+
+! Test declarations with coarray-spec
+
+! Different ways of declaring the same coarray.
+module m1
+  real :: a(1:5)[1:10,1:*]
+  real, dimension(5) :: b[1:10,1:*]
+  real, codimension[1:10,1:*] :: c(5)
+  real, codimension[1:10,1:*], dimension(5) :: d
+  codimension :: e[1:10,1:*]
+  dimension :: e(5)
+  real :: e
+end
+!Expect: m1.mod
+!module m1
+! real(4)::a(1_8:5_8)[1_8:10_8,1_8:*]
+! real(4)::b(1_8:5_8)[1_8:10_8,1_8:*]
+! real(4)::c(1_8:5_8)[1_8:10_8,1_8:*]
+! real(4)::d(1_8:5_8)[1_8:10_8,1_8:*]
+! real(4)::e(1_8:5_8)[1_8:10_8,1_8:*]
+!end
+
+! coarray-spec in codimension and target statements.
+module m2
+  codimension :: a[10,*], b[*]
+  target :: c[10,*], d[*]
+end
+!Expect: m2.mod
+!module m2
+! real(4)::a[1_8:10_8,1_8:*]
+! real(4)::b[1_8:*]
+! real(4),target::c[1_8:10_8,1_8:*]
+! real(4),target::d[1_8:*]
+!end
+
+! coarray-spec in components and with non-constants bounds
+module m3
+  type t
+    real :: c(1:5)[1:10,1:*]
+    complex, codimension[5,*] :: d
+  end type
+  real, allocatable :: e[:,:,:]
+contains
+  subroutine s(a, b, n)
+    integer(8) :: n
+    real :: a[1:n,2:*]
+    real, codimension[1:n,2:*] :: b
+  end
+end
+!Expect: m3.mod
+!module m3
+! type::t
+!  real(4)::c[1_8:10_8,1_8:*]
+!  complex(4)::d[1_8:5_8,1_8:*]
+! end type
+! real(4),allocatable::e[:,:,:]
+!contains
+! subroutine s(a,b,n)
+!  integer(8)::n
+!  real(4)::a[1_8:n,2_8:*]
+!  real(4)::b[1_8:n,2_8:*]
+! end
+!end
+
+! coarray-spec in both attributes and entity-decl
+module m4
+  real, codimension[2:*], dimension(2:5) :: a, b(4,4), c[10,*], d(4,4)[10,*]
+end
+!Expect: m4.mod
+!module m4
+! real(4)::a(2_8:5_8)[2_8:*]
+! real(4)::b(1_8:4_8,1_8:4_8)[2_8:*]
+! real(4)::c(2_8:5_8)[1_8:10_8,1_8:*]
+! real(4)::d(1_8:4_8,1_8:4_8)[1_8:10_8,1_8:*]
+!end
index 96e51de..eb456ef 100644 (file)
@@ -16,18 +16,27 @@ subroutine s1
   integer :: x(2)
   !ERROR: The dimensions of 'x' have already been declared
   allocatable :: x(:)
+  real :: y[1:*]
+  !ERROR: The codimensions of 'y' have already been declared
+  allocatable :: y[:]
 end
 
 subroutine s2
   target :: x(1)
   !ERROR: The dimensions of 'x' have already been declared
   integer :: x(2)
+  target :: y[1:*]
+  !ERROR: The codimensions of 'y' have already been declared
+  integer :: y[2:*]
 end
 
 subroutine s3
-  dimension :: x(4), y(8)
+  dimension :: x(4), x2(8)
   !ERROR: The dimensions of 'x' have already been declared
   allocatable :: x(:)
+  codimension :: y[*], y2[1:2,2:*]
+  !ERROR: The codimensions of 'y' have already been declared
+  allocatable :: y[:]
 end
 
 subroutine s4