From 96b187efdf9af3d966274276d8cf36c3921367d1 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Thu, 2 Aug 2018 16:21:27 -0700 Subject: [PATCH] [flang] Add support for submodules Symbols for submodules have `ModuleDetails` with `isSubmodule` set. Scopes for submodules have `Module` kind and have a parent scope that is also `Module` kind. Scopes for modules now contain a mapping of submodule name to scope so that we can find them without having to search the scope tree or re-read their `.mod` file. The module file for submodule `s` with ancestor module `m` is named `m-s.mod`. The tree structure of scopes means module file writing is now recursive. Similarly, reading the module file for a submodule may require reading the module files of its parent and ancestor. `ResolveNames` now requires the parent scope to be passed in -- it is not always the global scope. `test_modfiles.sh` now handles an argument that is a filename glob so that the test can involve multiple files. This allows `modfile09` to test reading of `.mod` files for modules and submodules. Original-commit: flang-compiler/f18@2e4424dbc8cc5803d561274dbf70005afe7e65dc Reviewed-on: https://github.com/flang-compiler/f18/pull/160 Tree-same-pre-rewrite: false --- flang/lib/semantics/mod-file.cc | 175 ++++++++++++++++++++++++----------- flang/lib/semantics/mod-file.h | 19 ++-- flang/lib/semantics/resolve-names.cc | 119 +++++++++++++++--------- flang/lib/semantics/resolve-names.h | 6 +- flang/lib/semantics/scope.cc | 12 +++ flang/lib/semantics/scope.h | 12 ++- flang/lib/semantics/symbol.cc | 34 ++++++- flang/lib/semantics/symbol.h | 11 ++- flang/test/semantics/CMakeLists.txt | 3 + flang/test/semantics/modfile09-a.f90 | 16 ++++ flang/test/semantics/modfile09-b.f90 | 8 ++ flang/test/semantics/modfile09-c.f90 | 8 ++ flang/test/semantics/modfile09-d.f90 | 8 ++ flang/test/semantics/resolve26.f90 | 24 +++++ flang/test/semantics/resolve27.f90 | 21 +++++ flang/test/semantics/test_modfile.sh | 56 ++++++----- flang/tools/f18/f18.cc | 4 +- 17 files changed, 393 insertions(+), 143 deletions(-) create mode 100644 flang/test/semantics/modfile09-a.f90 create mode 100644 flang/test/semantics/modfile09-b.f90 create mode 100644 flang/test/semantics/modfile09-c.f90 create mode 100644 flang/test/semantics/modfile09-d.f90 create mode 100644 flang/test/semantics/resolve26.f90 create mode 100644 flang/test/semantics/resolve27.f90 diff --git a/flang/lib/semantics/mod-file.cc b/flang/lib/semantics/mod-file.cc index 3bde249..d3766d2 100644 --- a/flang/lib/semantics/mod-file.cc +++ b/flang/lib/semantics/mod-file.cc @@ -34,14 +34,16 @@ using namespace parser::literals; static constexpr auto extension{".mod"}; // The initial characters of a file that identify it as a .mod file. static constexpr auto magic{"!mod$"}; -// Construct the path to a module file. -static std::string ModFilePath(const std::string &, const std::string &); + // Helpers for creating error messages. static parser::Message Error( const SourceName &, parser::MessageFixedText, const std::string &); static parser::Message Error(const SourceName &, parser::MessageFixedText, const std::string &, const std::string &); +static const SourceName *GetSubmoduleParent(const parser::Program &); +static std::string ModFilePath( + const std::string &, const SourceName &, const std::string &); static void PutEntity(std::ostream &, const Symbol &); static void PutObjectEntity(std::ostream &, const Symbol &); static void PutProcEntity(std::ostream &, const Symbol &); @@ -54,41 +56,57 @@ static std::ostream &PutLower(std::ostream &, const std::string &); static std::string CheckSum(const std::string &); bool ModFileWriter::WriteAll() { - for (const auto &scope : Scope::globalScope.children()) { - if (scope.kind() == Scope::Kind::Module) { - auto &symbol{*scope.symbol()}; // symbol must be present for module - if (!symbol.test(Symbol::Flag::ModFile)) { - WriteOne(symbol); - } - } - } + WriteChildren(Scope::globalScope); return errors_.empty(); } -bool ModFileWriter::WriteOne(const Symbol &modSymbol) { - CHECK(modSymbol.has()); - auto name{parser::ToLowerCaseLetters(modSymbol.name().ToString())}; - std::string path{ModFilePath(dir_, name)}; - std::ofstream os{path}; - PutSymbols(*modSymbol.scope()); - std::string all{GetAsString(name)}; - auto header{GetHeader(all)}; - os << header << all; - os.close(); - if (!os) { - errors_.emplace_back( - "Error writing %s: %s"_err_en_US, path.c_str(), std::strerror(errno)); - return false; +void ModFileWriter::WriteChildren(const Scope &scope) { + for (const auto &child : scope.children()) { + WriteOne(child); } - return true; +} + +void ModFileWriter::WriteOne(const Scope &scope) { + auto *symbol{scope.symbol()}; + if (scope.kind() != Scope::Kind::Module) { + return; + } + if (!symbol->test(Symbol::Flag::ModFile)) { + auto *ancestor{symbol->get().ancestor()}; + auto ancestorName{ancestor ? ancestor->name().ToString() : ""s}; + auto path{ModFilePath(dir_, symbol->name(), ancestorName)}; + std::ofstream os{path}; + PutSymbols(scope); + std::string all{GetAsString(*symbol)}; + auto header{GetHeader(all)}; + os << header << all; + os.close(); + if (!os) { + errors_.emplace_back( + "Error writing %s: %s"_err_en_US, path.c_str(), std::strerror(errno)); + return; + } + } + WriteChildren(scope); // write out submodules } // Return the entire body of the module file // and clear saved uses, decls, and contains. -std::string ModFileWriter::GetAsString(const std::string &name) { +std::string ModFileWriter::GetAsString(const Symbol &symbol) { std::stringstream all; - all << "module " << name << '\n'; - all << uses_.str(); + auto &details{symbol.get()}; + if (!details.isSubmodule()) { + PutLower(all << "module ", symbol); + } else { + auto *parent{details.parent()->symbol()}; + auto *ancestor{details.ancestor()->symbol()}; + PutLower(all << "submodule(", *ancestor); + if (parent != ancestor) { + PutLower(all << ':', *parent); + } + PutLower(all << ") ", symbol); + } + all << '\n' << uses_.str(); uses_.str(""s); all << useExtraAttrs_.str(); useExtraAttrs_.str(""s); @@ -348,10 +366,22 @@ std::string CheckSum(const std::string &str) { return result; } -bool ModFileReader::Read(const SourceName &modName) { - auto path{FindModFile(modName)}; +Scope *ModFileReader::Read(const SourceName &name, Scope *ancestor) { + std::string ancestorName; // empty for module + if (ancestor) { + if (auto *scope{ancestor->FindSubmodule(name)}) { + return scope; + } + ancestorName = ancestor->name().ToString(); + } else { + auto it{Scope::globalScope.find(name)}; + if (it != Scope::globalScope.end()) { + return it->second->scope(); + } + } + auto path{FindModFile(name, ancestorName)}; if (!path.has_value()) { - return false; + return nullptr; } // TODO: Construct parsing with an AllSources reference to share provenance parser::Parsing parsing; @@ -363,56 +393,89 @@ bool ModFileReader::Read(const SourceName &modName) { if (!parsing.messages().empty() || !parsing.consumedWholeFile() || !parseTree.has_value()) { errors_.push_back( - Error(modName, "Module file for '%s' is corrupt: %s"_err_en_US, - modName.ToString(), *path)); - return false; + Error(name, "Module file for '%s' is corrupt: %s"_err_en_US, + name.ToString(), *path)); + return nullptr; } - ResolveNames(*parseTree, parsing.cooked(), directories_); - - const auto &it{Scope::globalScope.find(modName)}; - if (it == Scope::globalScope.end()) { - return false; + Scope *parentScope; + if (!ancestor) { + // module: goes into global scope + parentScope = &Scope::globalScope; + } else { + // submodule: goes into parent module/submodule + auto *parent{GetSubmoduleParent(*parseTree)}; + parentScope = parent ? Read(*parent, ancestor) : ancestor; + } + ResolveNames(*parentScope, *parseTree, parsing.cooked(), directories_); + const auto &it{parentScope->find(name)}; + if (it == parentScope->end()) { + return nullptr; } auto &modSymbol{*it->second}; // TODO: Preserve the CookedSource rather than acquiring its string. modSymbol.scope()->set_chars(std::string{parsing.cooked().AcquireData()}); modSymbol.set(Symbol::Flag::ModFile); - return true; + return modSymbol.scope(); } -// Look for the .mod file for this module in the search directories. -// Add to errors_ if not found. std::optional ModFileReader::FindModFile( - const SourceName &modName) { - auto error{Error(modName, "Cannot find module file for '%s'"_err_en_US, - modName.ToString())}; + const SourceName &name, const std::string &ancestor) { + std::vector errors; for (auto &dir : directories_) { - std::string path{ModFilePath(dir, modName.ToString())}; + std::string path{ModFilePath(dir, name, ancestor)}; std::ifstream ifstream{path}; if (!ifstream.good()) { - error.Attach(Error( - modName, "%s: %s"_en_US, path, std::string{std::strerror(errno)})); + errors.push_back( + Error(name, "%s: %s"_en_US, path, std::string{std::strerror(errno)})); } else { std::string line; ifstream >> line; if (std::equal(line.begin(), line.end(), std::string{magic}.begin())) { - // TODO: verify reset of header line: version, checksum, etc. - return path; // success + // TODO: verify rest of header line: version, checksum, etc. + return path; } - error.Attach(Error(modName, "%s: Not a valid module file"_en_US, path)); + errors.push_back(Error(name, "%s: Not a valid module file"_en_US, path)); } } + auto error{Error(name, + ancestor.empty() + ? "Cannot find module file for '%s'"_err_en_US + : "Cannot find module file for submodule '%s' of module '%s'"_err_en_US, + name.ToString(), ancestor)}; + for (auto &e : errors) { + error.Attach(e); + } errors_.push_back(error); return std::nullopt; } -static std::string ModFilePath( - const std::string &dir, const std::string &modName) { - if (dir == "."s) { - return modName + extension; +// program was read from a .mod file for a submodule; return the name of the +// submodule's parent submodule, nullptr if none. +static const SourceName *GetSubmoduleParent(const parser::Program &program) { + CHECK(program.v.size() == 1); + auto &unit{program.v.front()}; + auto &submod{std::get>(unit.u)}; + auto &stmt{std::get>(submod->t)}; + auto &parentId{std::get(stmt.statement.t)}; + if (auto &parent{std::get>(parentId.t)}) { + return &parent->source; } else { - return dir + '/' + modName + extension; + return nullptr; + } +} + +// Construct the path to a module file. ancestorName not empty means submodule. +static std::string ModFilePath(const std::string &dir, const SourceName &name, + const std::string &ancestorName) { + std::stringstream path; + if (dir != "."s) { + path << dir << '/'; + } + if (!ancestorName.empty()) { + PutLower(path, ancestorName) << '-'; } + PutLower(path, name.ToString()) << extension; + return path.str(); } static parser::Message Error(const SourceName &location, diff --git a/flang/lib/semantics/mod-file.h b/flang/lib/semantics/mod-file.h index ec24958..a75d248 100644 --- a/flang/lib/semantics/mod-file.h +++ b/flang/lib/semantics/mod-file.h @@ -49,8 +49,6 @@ public: // Write out all .mod files; if error return false. bool WriteAll(); - // Write out .mod file for one module; if error return false. - bool WriteOne(const Symbol &); private: using symbolSet = std::set; @@ -66,7 +64,9 @@ private: // Any errors encountered during writing: std::vector errors_; - std::string GetAsString(const std::string &); + void WriteChildren(const Scope &); + void WriteOne(const Scope &); + std::string GetAsString(const Symbol &); std::string GetHeader(const std::string &); void PutSymbols(const Scope &); symbolVector SortSymbols(const symbolSet); @@ -84,18 +84,19 @@ public: // directories specifies where to search for module files ModFileReader(const std::vector &directories) : directories_{directories} {} - - // Find and read the module file for modName. - // Return true on success; otherwise errors() reports the problems. - bool Read(const SourceName &modName); + // Find and read the module file for a module or submodule. + // If ancestor is specified, look for a submodule of that module. + // Return the Scope for that module/submodule or nullptr on error. + Scope *Read(const SourceName &, Scope *ancestor = nullptr); + // Errors that occurred when Read returns nullptr. std::vector &errors() { return errors_; } private: std::vector directories_; std::vector errors_; - std::optional FindModFile(const SourceName &); - bool Prescan(const SourceName &, const std::string &); + std::optional FindModFile( + const SourceName &, const std::string &); }; } // namespace Fortran::semantics diff --git a/flang/lib/semantics/resolve-names.cc b/flang/lib/semantics/resolve-names.cc index c43a1fb..24bc364 100644 --- a/flang/lib/semantics/resolve-names.cc +++ b/flang/lib/semantics/resolve-names.cc @@ -292,13 +292,14 @@ private: // Manage a stack of Scopes class ScopeHandler : public virtual ImplicitRulesVisitor { public: - ScopeHandler() { PushScope(Scope::globalScope); } + void set_rootScope(Scope &scope) { PushScope(scope); } Scope &CurrScope() { return *scopes_.top(); } // Return the enclosing scope not corresponding to a derived type: Scope &CurrNonTypeScope(); // Create a new scope and push it on the scope stack. Scope &PushScope(Scope::Kind kind, Symbol *symbol); + void PushScope(Scope &scope); void PopScope(); Symbol *FindSymbol(const SourceName &name); @@ -376,14 +377,14 @@ protected: private: // Stack of containing scopes; memory referenced is owned by parent scopes std::stack> scopes_; - - void PushScope(Scope &scope); }; class ModuleVisitor : public virtual ScopeHandler { public: bool Pre(const parser::Module &); void Post(const parser::Module &); + bool Pre(const parser::Submodule &); + void Post(const parser::Submodule &); bool Pre(const parser::AccessStmt &); bool Pre(const parser::Only &); bool Pre(const parser::Rename::Names &); @@ -406,13 +407,15 @@ private: void SetAccess(const parser::Name &, Attr); void ApplyDefaultAccess(); - const Scope *FindModule(const SourceName &); void AddUse(const parser::Rename::Names &); void AddUse(const parser::Name &); // Record a use from useModuleScope_ of useName as localName. location is // where it occurred (either the module or the rename) for error reporting. void AddUse(const SourceName &location, const SourceName &localName, const SourceName &useName); + Symbol &BeginModule(const SourceName &, bool isSubmodule, + const std::optional &); + Scope *FindModule(const SourceName &, Scope * = nullptr); }; class InterfaceVisitor : public virtual ScopeHandler { @@ -1268,30 +1271,6 @@ void ModuleVisitor::Post(const parser::UseStmt &x) { useModuleScope_ = nullptr; } -// Find the module with this name and return its scope. -// May have to read a .mod file to find it. -// Return nullptr on error, after reporting it. -const Scope *ModuleVisitor::FindModule(const SourceName &name) { - auto it{Scope::globalScope.find(name)}; - if (it == Scope::globalScope.end()) { - ModFileReader reader{searchDirectories_}; - if (!reader.Read(name)) { - for (auto &error : reader.errors()) { - Say(std::move(error)); - } - return nullptr; - } - it = Scope::globalScope.find(name); - CHECK(it != Scope::globalScope.end()); // else would have reported error - } - const auto *details{it->second->detailsIf()}; - if (!details) { - Say(name, "'%s' is not a module"_err_en_US); - return nullptr; - } - return details->scope(); -} - void ModuleVisitor::AddUse(const parser::Rename::Names &names) { const SourceName &useName{std::get<0>(names.t).source}; const SourceName &localName{std::get<1>(names.t).source}; @@ -1336,22 +1315,43 @@ void ModuleVisitor::AddUse(const SourceName &location, } } +bool ModuleVisitor::Pre(const parser::Submodule &x) { + auto &stmt{std::get>(x.t)}; + auto &name{std::get(stmt.statement.t).source}; + auto &subpPart{std::get>(x.t)}; + auto &parentId{std::get(stmt.statement.t)}; + auto &ancestorName{std::get(parentId.t).source}; + auto &parentName{std::get>(parentId.t)}; + Scope *ancestor{FindModule(ancestorName)}; + if (!ancestor) { + return false; + } + Scope *parentScope{ + parentName ? FindModule(parentName->source, ancestor) : ancestor}; + if (!parentScope) { + return false; + } + PushScope(*parentScope); // submodule is hosted in parent + auto &symbol{BeginModule(name, true, subpPart)}; + if (ancestor->AddSubmodule(name, &CurrScope())) { + Say(name, "Module '%s' already has a submodule named '%s'"_err_en_US, + ancestorName, name); + } + MakeSymbol(name, symbol.get()); + return true; +} +void ModuleVisitor::Post(const parser::Submodule &) { + PopScope(); // submodule's scope + PopScope(); // parent's scope +} + bool ModuleVisitor::Pre(const parser::Module &x) { // Make a symbol and push a scope for this module const auto &name{ - std::get>(x.t).statement.v}; - auto &symbol{MakeSymbol(name, ModuleDetails{})}; - ModuleDetails &details{symbol.get()}; - Scope &modScope{PushScope(Scope::Kind::Module, &symbol)}; - details.set_scope(&modScope); - MakeSymbol(name, ModuleDetails{details}); - // collect module subprogram names - if (const auto &subpPart{ - std::get>(x.t)}) { - subpNamesOnly_ = SubprogramKind::Module; - parser::Walk(*subpPart, *static_cast(this)); - subpNamesOnly_ = std::nullopt; - } + std::get>(x.t).statement.v.source}; + auto &subpPart{std::get>(x.t)}; + auto &symbol{BeginModule(name, false, subpPart)}; + MakeSymbol(name, symbol.details()); return true; } @@ -1361,6 +1361,40 @@ void ModuleVisitor::Post(const parser::Module &) { prevAccessStmt_ = nullptr; } +Symbol &ModuleVisitor::BeginModule(const SourceName &name, bool isSubmodule, + const std::optional &subpPart) { + auto &symbol{MakeSymbol(name, ModuleDetails{isSubmodule})}; + auto &details{symbol.get()}; + auto &modScope{PushScope(Scope::Kind::Module, &symbol)}; + details.set_scope(&modScope); + if (subpPart) { + subpNamesOnly_ = SubprogramKind::Module; + parser::Walk(*subpPart, *static_cast(this)); + subpNamesOnly_ = std::nullopt; + } + return symbol; +} + +// Find a module or submodule by name and return its scope. +// If ancestor is present, look for a submodule of that ancestor module. +// May have to read a .mod file to find it. +// If an error occurs, report it and return nullptr. +Scope *ModuleVisitor::FindModule(const SourceName &name, Scope *ancestor) { + ModFileReader reader{searchDirectories_}; + auto *scope{reader.Read(name, ancestor)}; + if (!scope) { + for (auto &error : reader.errors()) { + Say(std::move(error)); + } + return nullptr; + } + if (scope->kind() != Scope::Kind::Module) { + Say(name, "'%s' is not a module"_err_en_US); + return nullptr; + } + return scope; +} + void ModuleVisitor::ApplyDefaultAccess() { for (auto &pair : CurrScope()) { Symbol &symbol = *pair.second; @@ -2443,10 +2477,11 @@ void ResolveNamesVisitor::Post(const parser::Program &) { CHECK(!GetDeclTypeSpec()); } -void ResolveNames(parser::Program &program, +void ResolveNames(Scope &rootScope, parser::Program &program, const parser::CookedSource &cookedSource, const std::vector &searchDirectories) { ResolveNamesVisitor visitor; + visitor.set_rootScope(rootScope); for (auto &dir : searchDirectories) { visitor.add_searchDirectory(dir); } diff --git a/flang/lib/semantics/resolve-names.h b/flang/lib/semantics/resolve-names.h index 8a786a8c..f071360 100644 --- a/flang/lib/semantics/resolve-names.h +++ b/flang/lib/semantics/resolve-names.h @@ -25,8 +25,10 @@ class CookedSource; namespace Fortran::semantics { -void ResolveNames(parser::Program &, const parser::CookedSource &, - const std::vector &); +class Scope; + +void ResolveNames(Scope &rootScope, parser::Program &, + const parser::CookedSource &, const std::vector &); void DumpSymbols(std::ostream &); } // namespace Fortran::semantics diff --git a/flang/lib/semantics/scope.cc b/flang/lib/semantics/scope.cc index 63e6d47..d36d028 100644 --- a/flang/lib/semantics/scope.cc +++ b/flang/lib/semantics/scope.cc @@ -48,6 +48,18 @@ Scope::size_type Scope::erase(const SourceName &name) { return 0; } } +Scope *Scope::FindSubmodule(const SourceName &name) const { + auto it{submodules_.find(name)}; + if (it == submodules_.end()) { + return nullptr; + } else { + return it->second; + } +} +Scope *Scope::AddSubmodule(const SourceName &name, Scope *submodule) { + auto pair{submodules_.emplace(name, submodule)}; + return !pair.second ? pair.first->second : nullptr; +} DerivedTypeSpec &Scope::MakeDerivedTypeSpec(const SourceName &name) { derivedTypeSpecs_.emplace_back(name); return derivedTypeSpecs_.back(); diff --git a/flang/lib/semantics/scope.h b/flang/lib/semantics/scope.h index cdeac43..b9a64be 100644 --- a/flang/lib/semantics/scope.h +++ b/flang/lib/semantics/scope.h @@ -91,7 +91,7 @@ public: std::pair try_emplace( const SourceName &name, Attrs attrs, D &&details) { Symbol &symbol{MakeSymbol(name, attrs, std::move(details))}; - return symbols_.insert(std::make_pair(name, &symbol)); + return symbols_.emplace(name, &symbol); } /// Make a Symbol but don't add it to the scope. @@ -103,13 +103,16 @@ public: std::list &children() { return children_; } const std::list &children() const { return children_; } + // For Module scope, maintain a mapping of all submodule scopes with this + // module as its ancestor module. + Scope *FindSubmodule(const SourceName &) const; + Scope *AddSubmodule(const SourceName &, Scope *); + DerivedTypeSpec &MakeDerivedTypeSpec(const SourceName &); // For modules read from module files, this is the stream of characters // that are referenced by SourceName objects. - void set_chars(std::string &&chars) { - chars_ = std::move(chars); - } + void set_chars(std::string &&chars) { chars_ = std::move(chars); } private: Scope &parent_; @@ -117,6 +120,7 @@ private: Symbol *const symbol_; std::list children_; mapType symbols_; + std::map submodules_; std::list derivedTypeSpecs_; std::string chars_; diff --git a/flang/lib/semantics/symbol.cc b/flang/lib/semantics/symbol.cc index 1fd8cbf..970fb1d 100644 --- a/flang/lib/semantics/symbol.cc +++ b/flang/lib/semantics/symbol.cc @@ -23,6 +23,28 @@ std::ostream &operator<<(std::ostream &os, const parser::CharBlock &name) { return os << name.ToString(); } +const Scope *ModuleDetails::parent() const { + return isSubmodule_ ? &scope_->parent() : nullptr; +} +const Scope *ModuleDetails::ancestor() const { + if (!isSubmodule_) { + return nullptr; + } + for (auto *scope{scope_};;) { + auto *parent{&scope->parent()}; + if (parent->kind() != Scope::Kind::Module) { + return scope; + } + scope = parent; + } +} +void ModuleDetails::set_scope(const Scope *scope) { + CHECK(!scope_); + bool scopeIsSubmodule{scope->parent().kind() == Scope::Kind::Module}; + CHECK(isSubmodule_ == scopeIsSubmodule); + scope_ = scope; +} + void EntityDetails::set_type(const DeclTypeSpec &type) { CHECK(!type_); type_ = type; @@ -261,7 +283,17 @@ std::ostream &operator<<(std::ostream &os, const Details &details) { common::visitors{ [&](const UnknownDetails &x) {}, [&](const MainProgramDetails &x) {}, - [&](const ModuleDetails &x) {}, + [&](const ModuleDetails &x) { + if (x.isSubmodule()) { + auto &ancestor{x.ancestor()->name()}; + auto &parent{x.parent()->name()}; + os << " (" << ancestor.ToString(); + if (parent != ancestor) { + os << ':' << parent.ToString(); + } + os << ")"; + } + }, [&](const SubprogramDetails &x) { os << " ("; int n = 0; diff --git a/flang/lib/semantics/symbol.h b/flang/lib/semantics/symbol.h index 5c4fb9c..cfefe24 100644 --- a/flang/lib/semantics/symbol.h +++ b/flang/lib/semantics/symbol.h @@ -29,15 +29,18 @@ namespace Fortran::semantics { class Scope; class Symbol; +// A module or submodule. class ModuleDetails { public: + ModuleDetails(bool isSubmodule = false) : isSubmodule_{isSubmodule} {} + bool isSubmodule() const { return isSubmodule_; } const Scope *scope() const { return scope_; } - void set_scope(const Scope *scope) { - CHECK(!scope_); - scope_ = scope; - } + const Scope *ancestor() const; // for submodule; nullptr for module + const Scope *parent() const; // for submodule; nullptr for module + void set_scope(const Scope *); private: + bool isSubmodule_; const Scope *scope_{nullptr}; }; diff --git a/flang/test/semantics/CMakeLists.txt b/flang/test/semantics/CMakeLists.txt index a1805e3..6e7fa31 100644 --- a/flang/test/semantics/CMakeLists.txt +++ b/flang/test/semantics/CMakeLists.txt @@ -49,6 +49,8 @@ set(ERROR_TESTS resolve23.f90 resolve24.f90 resolve25.f90 + resolve26.f90 + resolve27.f90 ) # These test files have expected symbols in the source @@ -66,6 +68,7 @@ set(MODFILE_TESTS modfile06.f90 modfile07.f90 modfile08.f90 + modfile09-*.f90 ) foreach(test ${ERROR_TESTS}) diff --git a/flang/test/semantics/modfile09-a.f90 b/flang/test/semantics/modfile09-a.f90 new file mode 100644 index 0000000..1baceec --- /dev/null +++ b/flang/test/semantics/modfile09-a.f90 @@ -0,0 +1,16 @@ +module m + integer :: m1_x + interface + module subroutine s() + end subroutine + end interface +end + +!Expect: m.mod +!module m +!integer::m1_x +!interface +!module subroutine s() +!end +!end interface +!end diff --git a/flang/test/semantics/modfile09-b.f90 b/flang/test/semantics/modfile09-b.f90 new file mode 100644 index 0000000..6fc6703 --- /dev/null +++ b/flang/test/semantics/modfile09-b.f90 @@ -0,0 +1,8 @@ +submodule(m) s1 + integer s1_x +end + +!Expect: m-s1.mod +!submodule(m) s1 +!integer::s1_x +!end diff --git a/flang/test/semantics/modfile09-c.f90 b/flang/test/semantics/modfile09-c.f90 new file mode 100644 index 0000000..d6670e4 --- /dev/null +++ b/flang/test/semantics/modfile09-c.f90 @@ -0,0 +1,8 @@ +submodule(m:s1) s2 + integer s2_x +end + +!Expect: m-s2.mod +!submodule(m:s1) s2 +!integer::s2_x +!end diff --git a/flang/test/semantics/modfile09-d.f90 b/flang/test/semantics/modfile09-d.f90 new file mode 100644 index 0000000..00550b5 --- /dev/null +++ b/flang/test/semantics/modfile09-d.f90 @@ -0,0 +1,8 @@ +submodule(m:s2) s3 + integer s3_x +end + +!Expect: m-s3.mod +!submodule(m:s2) s3 +!integer::s3_x +!end diff --git a/flang/test/semantics/resolve26.f90 b/flang/test/semantics/resolve26.f90 new file mode 100644 index 0000000..a02962f --- /dev/null +++ b/flang/test/semantics/resolve26.f90 @@ -0,0 +1,24 @@ +module m1 + interface + module subroutine s() + end subroutine + end interface +end + +module m2 + interface + module subroutine s() + end subroutine + end interface +end + +submodule(m1) s1 +end + +!ERROR: Cannot find module file for submodule 's1' of module 'm2' +submodule(m2:s1) s2 +end + +!ERROR: Cannot find module file for 'm3' +submodule(m3:s1) s3 +end diff --git a/flang/test/semantics/resolve27.f90 b/flang/test/semantics/resolve27.f90 new file mode 100644 index 0000000..3f04c1a --- /dev/null +++ b/flang/test/semantics/resolve27.f90 @@ -0,0 +1,21 @@ +module m + interface + module subroutine s() + end subroutine + end interface +end + +submodule(m) s1 +end + +submodule(m) s2 +end + +submodule(m:s1) s3 + integer x +end + +!ERROR: Module 'm' already has a submodule named 's3' +submodule(m:s2) s3 + integer y +end diff --git a/flang/test/semantics/test_modfile.sh b/flang/test/semantics/test_modfile.sh index f5e1c20..954cf2f 100755 --- a/flang/test/semantics/test_modfile.sh +++ b/flang/test/semantics/test_modfile.sh @@ -19,47 +19,55 @@ set -e PATH=/usr/bin:/bin srcdir=$(dirname $0) CMD="${F18:-../../../tools/f18/f18} -fdebug-resolve-names -fparse-only" - if [[ $# != 1 ]]; then echo "Usage: $0 " exit 1 fi src=$srcdir/$1 -[[ ! -f $src ]] && echo "File not found: $src" && exit 1 temp=temp-$1 rm -rf $temp mkdir $temp [[ $KEEP ]] || trap "rm -rf $temp" EXIT - -( cd $temp && $CMD $src ) - actual=$temp/actual.mod expect=$temp/expect.mod actual_files=$temp/actual_files +prev_files=$temp/prev_files diffs=$temp/diffs -( cd $temp && ls -1 *.mod ) > $actual_files -expected_files=$(sed -n 's/^!Expect: \(.*\)/\1/p' $src) -extra_files=$(echo "$expected_files" | comm -23 $actual_files -) -if [[ ! -z "$extra_files" ]]; then - echo "Unexpected .mod files produced:" $extra_files - echo FAIL - exit 1 -fi -for mod in $expected_files; do - if [[ ! -f $temp/$mod ]]; then - echo "Compilation did not produce expected mod file: $mod" - echo FAIL - exit 1 - fi - sed '/^!mod\$/d' $temp/$mod > $actual - sed '1,/^!Expect: '"$mod"'/d' $src | sed -e '/^$/,$d' -e 's/^! *//' > $expect - if ! diff -U999999 $actual $expect > $diffs; then - echo "Module file $mod differs from expected:" - sed '1,2d' $diffs +set $src + +touch $actual +for src in "$@"; do + [[ ! -f $src ]] && echo "File not found: $src" && exit 1 + ( + cd $temp + ls -1 *.mod > prev_files + $CMD $src + ls -1 *.mod | comm -13 prev_files - + ) > $actual_files + expected_files=$(sed -n 's/^!Expect: \(.*\)/\1/p' $src) + extra_files=$(echo "$expected_files" | comm -23 $actual_files -) + if [[ ! -z "$extra_files" ]]; then + echo "Unexpected .mod files produced:" $extra_files echo FAIL exit 1 fi + for mod in $expected_files; do + if [[ ! -f $temp/$mod ]]; then + echo "Compilation did not produce expected mod file: $mod" + echo FAIL + exit 1 + fi + sed '/^!mod\$/d' $temp/$mod > $actual + sed '1,/^!Expect: '"$mod"'/d' $src | sed -e '/^$/,$d' -e 's/^! *//' > $expect + if ! diff -U999999 $actual $expect > $diffs; then + echo "Module file $mod differs from expected:" + sed '1,2d' $diffs + echo FAIL + exit 1 + fi + done + rm -f $actual $expect done echo PASS diff --git a/flang/tools/f18/f18.cc b/flang/tools/f18/f18.cc index 0d1c3fe..340b625 100644 --- a/flang/tools/f18/f18.cc +++ b/flang/tools/f18/f18.cc @@ -25,6 +25,7 @@ #include "../../lib/semantics/dump-parse-tree.h" #include "../../lib/semantics/mod-file.h" #include "../../lib/semantics/resolve-names.h" +#include "../../lib/semantics/scope.h" #include "../../lib/semantics/unparse-with-symbols.h" #include #include @@ -212,7 +213,8 @@ std::string CompileFortran( if (driver.moduleDirectory != "."s) { directories.insert(directories.begin(), driver.moduleDirectory); } - Fortran::semantics::ResolveNames(parseTree, parsing.cooked(), directories); + Fortran::semantics::ResolveNames(Fortran::semantics::Scope::globalScope, + parseTree, parsing.cooked(), directories); Fortran::semantics::ModFileWriter writer; writer.set_directory(driver.moduleDirectory); if (!writer.WriteAll()) { -- 2.7.4