[flang] Add statement functions to symbol table
authorTim Keith <tkeith@nvidia.com>
Tue, 17 Apr 2018 21:16:42 +0000 (14:16 -0700)
committerTim Keith <tkeith@nvidia.com>
Tue, 17 Apr 2018 21:16:42 +0000 (14:16 -0700)
SubprogramDetails: Store dummy args and result as symbols, not names.

Symbol: Save list of occurrences (i.e. SourceNames that map to the same
symbol). This is needed to map Names in the parse tree back to symbols,
and will probably be useful when reporting errors.
Improve dumping of symbols.

resolve-names.cc: Recognize statement functions. They are treated like
function subprograms but the result type and type of dummy arguments come
from the enclosing scope. The implicit rules from the enclosing scope need
to be copied in.

Original-commit: flang-compiler/f18@44e4fb4b6cc107fad299e80046673804c15a2ac2
Reviewed-on: https://github.com/flang-compiler/f18/pull/58
Tree-same-pre-rewrite: false

flang/lib/semantics/resolve-names.cc
flang/lib/semantics/symbol.cc
flang/lib/semantics/symbol.h

index fa379a5..9097c8d 100644 (file)
@@ -180,7 +180,7 @@ public:
   }
 
 protected:
-  void PushScope();
+  void PushScope(bool copyImplicitRules);
   void PopScope();
 
 private:
@@ -209,15 +209,9 @@ public:
     return !arraySpec_.empty() ? arraySpec_ : attrArraySpec_;
   }
 
-  void BeginArraySpec() {
-    CHECK(attrArraySpec_.empty());
-  }
-  void EndArraySpec() {
-    attrArraySpec_.clear();
-  }
-  void ClearArraySpec() {
-    arraySpec_.clear();
-  }
+  void BeginArraySpec() { CHECK(attrArraySpec_.empty()); }
+  void EndArraySpec() { attrArraySpec_.clear(); }
+  void ClearArraySpec() { arraySpec_.clear(); }
 
   bool Pre(const parser::ArraySpec &x) {
     CHECK(arraySpec_.empty());
@@ -282,10 +276,10 @@ private:
 class ResolveNamesVisitor : public ImplicitRulesVisitor,
                             public ArraySpecVisitor {
 public:
-  using ImplicitRulesVisitor::Pre;
-  using ImplicitRulesVisitor::Post;
-  using ArraySpecVisitor::Pre;
   using ArraySpecVisitor::Post;
+  using ArraySpecVisitor::Pre;
+  using ImplicitRulesVisitor::Post;
+  using ImplicitRulesVisitor::Pre;
 
   ResolveNamesVisitor(parser::Messages &messages)
     : ImplicitRulesVisitor(messages) {
@@ -293,9 +287,9 @@ public:
   }
 
   Scope &CurrScope() { return *scopes_.top(); }
-  void PushScope(Scope &scope) {
+  void PushScope(Scope &scope, bool copyImplicitRules = false) {
     scopes_.push(&scope);
-    ImplicitRulesVisitor::PushScope();
+    ImplicitRulesVisitor::PushScope(copyImplicitRules);
   }
   void PopScope() {
     scopes_.pop();
@@ -320,13 +314,15 @@ public:
   bool Pre(const parser::ValueStmt &);
   bool Pre(const parser::VolatileStmt &);
   void Post(const parser::SpecificationPart &);
-  void Post(const parser::EndSubroutineStmt &);
-  void Post(const parser::EndFunctionStmt &);
   bool Pre(const parser::Suffix &);
+  bool Pre(const parser::StmtFunctionStmt &);
+  void Post(const parser::StmtFunctionStmt &);
   bool Pre(const parser::SubroutineStmt &);
   void Post(const parser::SubroutineStmt &);
+  void Post(const parser::EndSubroutineStmt &);
   bool Pre(const parser::FunctionStmt &);
   void Post(const parser::FunctionStmt &);
+  void Post(const parser::EndFunctionStmt &);
   bool Pre(const parser::MainProgram &);
   void Post(const parser::EndProgramStmt &);
   void Post(const parser::Program &);
@@ -335,16 +331,12 @@ public:
     objectDeclAttr_ = Attr::ALLOCATABLE;
     return true;
   }
-  void Post(const parser::AllocatableStmt &) {
-    objectDeclAttr_ = std::nullopt;
-  }
+  void Post(const parser::AllocatableStmt &) { objectDeclAttr_ = std::nullopt; }
   bool Pre(const parser::TargetStmt &x) {
     objectDeclAttr_ = Attr::TARGET;
     return true;
   }
-  void Post(const parser::TargetStmt &) {
-    objectDeclAttr_ = std::nullopt;
-  }
+  void Post(const parser::TargetStmt &) { objectDeclAttr_ = std::nullopt; }
   void Post(const parser::DimensionStmt::Declaration &);
 
   const parser::Name *GetVariableName(const parser::DataRef &x) {
@@ -378,9 +370,7 @@ public:
     }
   }
 
-  void Post(const parser::Expr &x) {
-    CheckImplicitSymbol(GetVariableName(x));
-  }
+  void Post(const parser::Expr &x) { CheckImplicitSymbol(GetVariableName(x)); }
   void Post(const parser::Variable &x) {
     CheckImplicitSymbol(GetVariableName(x));
   }
@@ -409,7 +399,8 @@ private:
   std::optional<Attr> objectDeclAttr_;
 
   // Create a subprogram symbol in the current scope and push a new scope.
-  void PushSubprogramScope(const parser::Name &, SubprogramDetails &);
+  Symbol &PushSubprogramScope(
+      const parser::Name &, bool copyImplicitRules = false);
 
   // On leaving a scope, add implicit types if appropriate.
   void ApplyImplicitRules();
@@ -421,20 +412,27 @@ private:
   template<typename D>
   Symbol &MakeSymbol(
       const parser::Name &name, const Attrs &attrs, D &&details) {
-    const auto pair = CurrScope().try_emplace(name.source, attrs, details);
-    Symbol &symbol = pair.first->second;
-    if (!pair.second) {
+    const auto &it = CurrScope().find(name.source);
+    auto &symbol = it->second;
+    if (it == CurrScope().end()) {
+      const auto pair = CurrScope().try_emplace(name.source, attrs, details);
+      CHECK(pair.second);  // name was not found, so must be able to add
+      return pair.first->second;
+    } else if (symbol.has<UnknownDetails>()) {
+      // update the existing symbol
       symbol.attrs() |= attrs;
-      if (!std::is_same<UnknownDetails, D>::value) {
-        if (symbol.has<UnknownDetails>()) {
-          symbol.set_details(details);
-        } else {
-          Say(name, "'%s' is already declared in this scoping unit"_err_en_US);
-          Say(symbol.name(), "Previous declaration of '%s'"_en_US);
-        }
-      }
+      symbol.set_details(details);
+      return symbol;
+    } else if (std::is_same<UnknownDetails, D>::value) {
+      symbol.attrs() |= attrs;
+      return symbol;
+    } else {
+      Say(name, "'%s' is already declared in this scoping unit"_err_en_US);
+      Say(symbol.name(), "Previous declaration of '%s'"_en_US);
+      // replace the old symbols with a new one with correct details
+      CurrScope().erase(symbol.name());
+      return MakeSymbol(name, attrs, details);
     }
-    return symbol;
   }
   template<typename D>
   Symbol &MakeSymbol(const parser::Name &name, D &&details) {
@@ -723,8 +721,9 @@ void ImplicitRulesVisitor::Post(const parser::ImplicitSpec &) {
   EndDeclTypeSpec();
 }
 
-void ImplicitRulesVisitor::PushScope() {
-  implicitRules_.push(ImplicitRules(*this));
+void ImplicitRulesVisitor::PushScope(bool copyImplicitRules) {
+  implicitRules_.push(copyImplicitRules ? ImplicitRules(implicitRules_.top())
+                                        : ImplicitRules(*this));
   prevImplicit_ = nullptr;
   prevImplicitNone_ = nullptr;
   prevImplicitNoneType_ = nullptr;
@@ -950,6 +949,60 @@ bool ResolveNamesVisitor::Pre(const parser::Suffix &suffix) {
   return true;
 }
 
+bool ResolveNamesVisitor::Pre(const parser::StmtFunctionStmt &x) {
+  const auto &name = std::get<parser::Name>(x.t);
+  std::optional<SourceName> occurrence;
+  std::optional<DeclTypeSpec> resultType;
+  // Look up name: provides return type or tells us if it's an array
+  auto it = CurrScope().find(name.source);
+  if (it != CurrScope().end()) {
+    Symbol &symbol{it->second};
+    if (auto *details = symbol.detailsIf<EntityDetails>()) {
+      if (details->isArray()) {
+        // not a stmt-func at all but an array; do nothing
+        symbol.add_occurrence(name.source);
+        return true;
+      }
+      // TODO: check that attrs are compatible with stmt func
+      resultType = details->type();
+      occurrence = symbol.name();
+      CurrScope().erase(symbol.name());
+    }
+  }
+  BeginAttrs();  // no attrs to collect, but PushSubprogramScope expects this
+  auto &symbol = PushSubprogramScope(name, /*copyImplicitRules=*/true);
+  if (occurrence) {
+    symbol.add_occurrence(*occurrence);
+  }
+  auto &details = symbol.details<SubprogramDetails>();
+  for (const auto &dummyName : std::get<std::list<parser::Name>>(x.t)) {
+    EntityDetails dummyDetails{true};
+    auto it = CurrScope().parent().find(dummyName.source);
+    if (it != CurrScope().parent().end()) {
+      if (auto *d = it->second.detailsIf<EntityDetails>()) {
+        if (d->type()) {
+          dummyDetails.set_type(*d->type());
+        }
+      }
+    }
+    details.add_dummyArg(MakeSymbol(dummyName, std::move(dummyDetails)));
+  }
+  CurrScope().erase(name.source);  // added by PushSubprogramScope
+  EntityDetails resultDetails;
+  if (resultType) {
+    resultDetails.set_type(*resultType);
+  }
+  details.set_result(MakeSymbol(name, resultDetails));
+  return true;
+}
+
+void ResolveNamesVisitor::Post(const parser::StmtFunctionStmt &x) {
+  ApplyImplicitRules();
+  std::cout << "End of stmt func scope\n";
+  std::cout << CurrScope();
+  PopScope();
+}
+
 bool ResolveNamesVisitor::Pre(const parser::SubroutineStmt &stmt) {
   BeginAttrs();
   return true;
@@ -963,25 +1016,23 @@ bool ResolveNamesVisitor::Pre(const parser::FunctionStmt &stmt) {
 
 void ResolveNamesVisitor::Post(const parser::SubroutineStmt &stmt) {
   const auto &subrName = std::get<parser::Name>(stmt.t);
-  SubprogramDetails details;
-  PushSubprogramScope(subrName, details);
+  auto &symbol = PushSubprogramScope(subrName);
+  auto &details = symbol.details<SubprogramDetails>();
   for (const auto &dummyArg : std::get<std::list<parser::DummyArg>>(stmt.t)) {
     const parser::Name *dummyName = std::get_if<parser::Name>(&dummyArg.u);
     CHECK(dummyName != nullptr && "TODO: alternate return indicator");
-    MakeSymbol(*dummyName, EntityDetails(true));
-    details.AddDummyName(dummyName->source);
+    Symbol &dummy{MakeSymbol(*dummyName, EntityDetails(true))};
+    details.add_dummyArg(dummy);
   }
 }
 
 void ResolveNamesVisitor::Post(const parser::FunctionStmt &stmt) {
   const auto &funcName = std::get<parser::Name>(stmt.t);
-  const auto &funcResultName = funcResultName_ ? *funcResultName_ : funcName;
-  funcResultName_ = nullptr;
-  SubprogramDetails details(funcResultName.source);
-  PushSubprogramScope(funcName, details);
+  auto &symbol = PushSubprogramScope(funcName);
+  auto &details = symbol.details<SubprogramDetails>();
   for (const auto &dummyName : std::get<std::list<parser::Name>>(stmt.t)) {
-    MakeSymbol(dummyName, EntityDetails(true));
-    details.AddDummyName(dummyName.source);
+    Symbol &dummy{MakeSymbol(dummyName, EntityDetails(true))};
+    details.add_dummyArg(dummy);
   }
   // add function result to function scope
   EntityDetails funcResultDetails;
@@ -989,20 +1040,27 @@ void ResolveNamesVisitor::Post(const parser::FunctionStmt &stmt) {
     funcResultDetails.set_type(*declTypeSpec_);
   }
   EndDeclTypeSpec();
-  if (funcResultName.source != funcName.source) {
-    MakeSymbol(funcResultName, funcResultDetails);
+
+  const parser::Name *funcResultName;
+  if (funcResultName_ && funcResultName_->source != funcName.source) {
+    funcResultName = funcResultName_;
+    funcResultName_ = nullptr;
   } else {
     CurrScope().erase(funcName.source);  // was added by PushSubprogramScope
-    MakeSymbol(funcName, funcResultDetails);
+    funcResultName = &funcName;
   }
+  details.set_result(MakeSymbol(*funcResultName, funcResultDetails));
 }
 
-void ResolveNamesVisitor::PushSubprogramScope(
-    const parser::Name &name, SubprogramDetails &details) {
-  MakeSymbol(name, EndAttrs(), details);
+Symbol &ResolveNamesVisitor::PushSubprogramScope(
+    const parser::Name &name, bool copyImplicitRules) {
+  auto &symbol = MakeSymbol(name, EndAttrs(), SubprogramDetails());
   Scope &subpScope = CurrScope().MakeScope(Scope::Kind::Subprogram);
-  PushScope(subpScope);
-  MakeSymbol(name, details);  // can't reused this name inside subprogram
+  PushScope(subpScope, copyImplicitRules);
+  auto &details = symbol.details<SubprogramDetails>();
+  // can't reused this name inside subprogram:
+  MakeSymbol(name, SubprogramDetails(details));
+  return symbol;
 }
 
 bool ResolveNamesVisitor::Pre(const parser::MainProgram &x) {
index afea00b..8670b9c 100644 (file)
@@ -17,6 +17,20 @@ void EntityDetails::set_shape(const ArraySpec &shape) {
   }
 }
 
+// The name of the kind of details for this symbol.
+// This is primarily for debugging.
+const std::string Symbol::GetDetailsName() const {
+  return std::visit(
+      parser::visitors{
+          [&](const UnknownDetails &x) { return "Unknown"; },
+          [&](const MainProgramDetails &x) { return "MainProgram"; },
+          [&](const ModuleDetails &x) { return "Module"; },
+          [&](const SubprogramDetails &x) { return "Subprogram"; },
+          [&](const EntityDetails &x) { return "Entity"; },
+      },
+      details_);
+}
+
 std::ostream &operator<<(std::ostream &os, const EntityDetails &x) {
   os << "Entity";
   if (x.type()) {
@@ -31,8 +45,17 @@ std::ostream &operator<<(std::ostream &os, const EntityDetails &x) {
   return os;
 }
 
+static std::ostream &DumpType(std::ostream &os, const Symbol &symbol) {
+  if (const auto *details = symbol.detailsIf<EntityDetails>()) {
+    if (details->type()) {
+      os << *details->type() << ' ';
+    }
+  }
+  return os;
+}
+
 std::ostream &operator<<(std::ostream &os, const Symbol &sym) {
-  os << sym.name_.ToString();
+  os << sym.name().ToString();
   if (!sym.attrs().empty()) {
     os << ", " << sym.attrs();
   }
@@ -45,13 +68,16 @@ std::ostream &operator<<(std::ostream &os, const Symbol &sym) {
           [&](const SubprogramDetails &x) {
             os << " Subprogram (";
             int n = 0;
-            for (const auto &dummy : x.dummyNames()) {
+            for (const auto &dummy : x.dummyArgs()) {
               if (n++ > 0) os << ", ";
-              os << dummy.ToString();
+              DumpType(os, *dummy);
+              os << dummy->name().ToString();
             }
             os << ')';
-            if (x.resultName()) {
-              os << " result(" << x.resultName()->ToString() << ')';
+            if (x.isFunction()) {
+              os << " result(";
+              DumpType(os, x.result());
+              os << x.result().name().ToString() << ')';
             }
           },
           [&](const EntityDetails &x) { os << ' ' << x; },
index 668bbca..79927f8 100644 (file)
@@ -16,6 +16,7 @@ using SourceName = parser::CharBlock;
 /// *Details classes.
 
 class Scope;
+class Symbol;
 
 class ModuleDetails {
 public:
@@ -29,19 +30,22 @@ private:
 
 class SubprogramDetails {
 public:
-  // Subroutine:
   SubprogramDetails() {}
-  // Function:
-  SubprogramDetails(const SourceName &resultName) : resultName_{resultName} {}
-
-  bool isFunction() const { return resultName_.has_value(); }
-  const std::list<SourceName> &dummyNames() const { return dummyNames_; }
-  const std::optional<SourceName> &resultName() const { return resultName_; }
-  void AddDummyName(const SourceName &name) { dummyNames_.push_back(name); }
+  SubprogramDetails(const SubprogramDetails &that)
+    : dummyArgs_{that.dummyArgs_}, result_{that.result_} {}
+
+  bool isFunction() const { return result_.has_value(); }
+  const Symbol &result() const { CHECK(isFunction()); return **result_; }
+  void set_result(Symbol &result) {
+    CHECK(!result_.has_value());
+    result_ = &result;
+  }
+  const std::list<Symbol *> &dummyArgs() const { return dummyArgs_; }
+  void add_dummyArg(Symbol &symbol) { dummyArgs_.push_back(&symbol); }
 
 private:
-  std::list<SourceName> dummyNames_;
-  std::optional<SourceName> resultName_;
+  std::list<Symbol *> dummyArgs_;
+  std::optional<Symbol *> result_;
   friend std::ostream &operator<<(std::ostream &, const SubprogramDetails &);
 };
 
@@ -53,6 +57,7 @@ public:
   const ArraySpec &shape() const { return shape_; }
   void set_shape(const ArraySpec &shape);
   bool isDummy() const { return isDummy_; }
+  bool isArray() const { return !shape_.empty(); }
 
 private:
   bool isDummy_;
@@ -71,10 +76,11 @@ public:
 
   Symbol(const Scope &owner, const SourceName &name, const Attrs &attrs,
       Details &&details)
-    : owner_{owner}, name_{name}, attrs_{attrs},
-      details_{std::move(details)} {}
+    : owner_{owner}, attrs_{attrs}, details_{std::move(details)} {
+      add_occurrence(name);
+    }
   const Scope &owner() const { return owner_; }
-  const SourceName &name() const { return name_; }
+  const SourceName &name() const { return occurrences_.front(); }
   Attrs &attrs() { return attrs_; }
   const Attrs &attrs() const { return attrs_; }
 
@@ -91,14 +97,15 @@ public:
 
   // Return a reference to the details which must be of type D.
   template<typename D> D &details() {
-    auto p = detailsIf<D>();
-    CHECK(p && "unexpected type");
-    return *p;
+    return const_cast<D &>(static_cast<const Symbol *>(this)->details<D>());
   }
   template<typename D> const D &details() const {
-    const auto p = detailsIf<D>();
-    CHECK(p && "unexpected type");
-    return *p;
+    if (const auto p = detailsIf<D>()) {
+      return *p;
+    } else {
+      Fortran::parser::die("unexpected %s details at %s(%d)",
+          GetDetailsName().c_str(), __FILE__, __LINE__);
+    }
   }
 
   // Assign the details of the symbol from one of the variants.
@@ -108,11 +115,20 @@ public:
     details_.swap(details);
   }
 
+  const std::list<SourceName> &occurrences() const {
+    return occurrences_;
+  }
+  void add_occurrence(const SourceName &name) {
+    occurrences_.push_back(name);
+  }
+
 private:
   const Scope &owner_;
-  const SourceName name_;
+  std::list<SourceName> occurrences_;
   Attrs attrs_;
   Details details_;
+
+  const std::string GetDetailsName() const;
   friend std::ostream &operator<<(std::ostream &, const Symbol &);
 };