xref: /llvm-project/flang/lib/Semantics/mod-file.cpp (revision f5ddb1012de1c7c7c958aa288932caead9607b07)
164ab3302SCarolineConcatto //===-- lib/Semantics/mod-file.cpp ----------------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
964ab3302SCarolineConcatto #include "mod-file.h"
1064ab3302SCarolineConcatto #include "resolve-names.h"
113780d3ebSTim Keith #include "flang/Common/restorer.h"
1264ab3302SCarolineConcatto #include "flang/Evaluate/tools.h"
1364ab3302SCarolineConcatto #include "flang/Parser/message.h"
1464ab3302SCarolineConcatto #include "flang/Parser/parsing.h"
159e7eef99SPeter Klausler #include "flang/Parser/unparse.h"
1664ab3302SCarolineConcatto #include "flang/Semantics/scope.h"
1764ab3302SCarolineConcatto #include "flang/Semantics/semantics.h"
1864ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
1964ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
200855c454SDavid Truby #include "llvm/Support/FileSystem.h"
210855c454SDavid Truby #include "llvm/Support/MemoryBuffer.h"
220855c454SDavid Truby #include "llvm/Support/raw_ostream.h"
2364ab3302SCarolineConcatto #include <algorithm>
2464ab3302SCarolineConcatto #include <fstream>
2564ab3302SCarolineConcatto #include <set>
2664ab3302SCarolineConcatto #include <string_view>
2764ab3302SCarolineConcatto #include <vector>
2864ab3302SCarolineConcatto 
2964ab3302SCarolineConcatto namespace Fortran::semantics {
3064ab3302SCarolineConcatto 
3164ab3302SCarolineConcatto using namespace parser::literals;
3264ab3302SCarolineConcatto 
3364ab3302SCarolineConcatto // The first line of a file that identifies it as a .mod file.
3464ab3302SCarolineConcatto // The first three bytes are a Unicode byte order mark that ensures
3564ab3302SCarolineConcatto // that the module file is decoded as UTF-8 even if source files
3664ab3302SCarolineConcatto // are using another encoding.
3764ab3302SCarolineConcatto struct ModHeader {
3864ab3302SCarolineConcatto   static constexpr const char bom[3 + 1]{"\xef\xbb\xbf"};
3964ab3302SCarolineConcatto   static constexpr int magicLen{13};
4064ab3302SCarolineConcatto   static constexpr int sumLen{16};
4164ab3302SCarolineConcatto   static constexpr const char magic[magicLen + 1]{"!mod$ v1 sum:"};
4264ab3302SCarolineConcatto   static constexpr char terminator{'\n'};
4364ab3302SCarolineConcatto   static constexpr int len{magicLen + 1 + sumLen};
44f7a15e00SPeter Klausler   static constexpr int needLen{7};
45f7a15e00SPeter Klausler   static constexpr const char need[needLen + 1]{"!need$ "};
4664ab3302SCarolineConcatto };
4764ab3302SCarolineConcatto 
4864ab3302SCarolineConcatto static std::optional<SourceName> GetSubmoduleParent(const parser::Program &);
499ce8e63cSPeter Klausler static void CollectSymbols(
509ce8e63cSPeter Klausler     const Scope &, SymbolVector &, SymbolVector &, UnorderedSymbolSet &);
518670e499SCaroline Concatto static void PutPassName(llvm::raw_ostream &, const std::optional<SourceName> &);
529e7eef99SPeter Klausler static void PutInit(llvm::raw_ostream &, const Symbol &, const MaybeExpr &,
539ce8e63cSPeter Klausler     const parser::Expr *);
548670e499SCaroline Concatto static void PutInit(llvm::raw_ostream &, const MaybeIntExpr &);
558670e499SCaroline Concatto static void PutBound(llvm::raw_ostream &, const Bound &);
56c14cf92bSPeter Klausler static void PutShapeSpec(llvm::raw_ostream &, const ShapeSpec &);
57c14cf92bSPeter Klausler static void PutShape(
58c14cf92bSPeter Klausler     llvm::raw_ostream &, const ArraySpec &, char open, char close);
598670e499SCaroline Concatto 
608670e499SCaroline Concatto static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr);
618670e499SCaroline Concatto static llvm::raw_ostream &PutType(llvm::raw_ostream &, const DeclTypeSpec &);
62bcba39a5SPeter Klausler static llvm::raw_ostream &PutLower(llvm::raw_ostream &, std::string_view);
63f7a15e00SPeter Klausler static std::error_code WriteFile(const std::string &, const std::string &,
64f7a15e00SPeter Klausler     ModuleCheckSumType &, bool debug = true);
6564ab3302SCarolineConcatto static bool FileContentsMatch(
6664ab3302SCarolineConcatto     const std::string &, const std::string &, const std::string &);
67f7a15e00SPeter Klausler static ModuleCheckSumType ComputeCheckSum(const std::string_view &);
68f7a15e00SPeter Klausler static std::string CheckSumString(ModuleCheckSumType);
6964ab3302SCarolineConcatto 
7064ab3302SCarolineConcatto // Collect symbols needed for a subprogram interface
7164ab3302SCarolineConcatto class SubprogramSymbolCollector {
7264ab3302SCarolineConcatto public:
73c42f6314Speter klausler   SubprogramSymbolCollector(const Symbol &symbol, const Scope &scope)
74c42f6314Speter klausler       : symbol_{symbol}, scope_{scope} {}
7564ab3302SCarolineConcatto   const SymbolVector &symbols() const { return need_; }
7664ab3302SCarolineConcatto   const std::set<SourceName> &imports() const { return imports_; }
7764ab3302SCarolineConcatto   void Collect();
7864ab3302SCarolineConcatto 
7964ab3302SCarolineConcatto private:
8064ab3302SCarolineConcatto   const Symbol &symbol_;
8164ab3302SCarolineConcatto   const Scope &scope_;
8264ab3302SCarolineConcatto   bool isInterface_{false};
8364ab3302SCarolineConcatto   SymbolVector need_; // symbols that are needed
840d8331c0Speter klausler   UnorderedSymbolSet needSet_; // symbols already in need_
850d8331c0Speter klausler   UnorderedSymbolSet useSet_; // use-associations that might be needed
8664ab3302SCarolineConcatto   std::set<SourceName> imports_; // imports from host that are needed
8764ab3302SCarolineConcatto 
8864ab3302SCarolineConcatto   void DoSymbol(const Symbol &);
8964ab3302SCarolineConcatto   void DoSymbol(const SourceName &, const Symbol &);
9064ab3302SCarolineConcatto   void DoType(const DeclTypeSpec *);
9164ab3302SCarolineConcatto   void DoBound(const Bound &);
9264ab3302SCarolineConcatto   void DoParamValue(const ParamValue &);
9364ab3302SCarolineConcatto   bool NeedImport(const SourceName &, const Symbol &);
9464ab3302SCarolineConcatto 
9564ab3302SCarolineConcatto   template <typename T> void DoExpr(evaluate::Expr<T> expr) {
9664ab3302SCarolineConcatto     for (const Symbol &symbol : evaluate::CollectSymbols(expr)) {
9764ab3302SCarolineConcatto       DoSymbol(symbol);
9864ab3302SCarolineConcatto     }
9964ab3302SCarolineConcatto   }
10064ab3302SCarolineConcatto };
10164ab3302SCarolineConcatto 
10264ab3302SCarolineConcatto bool ModFileWriter::WriteAll() {
1033780d3ebSTim Keith   // this flag affects character literals: force it to be consistent
1043780d3ebSTim Keith   auto restorer{
1053780d3ebSTim Keith       common::ScopedSet(parser::useHexadecimalEscapeSequences, false)};
10664ab3302SCarolineConcatto   WriteAll(context_.globalScope());
10764ab3302SCarolineConcatto   return !context_.AnyFatalError();
10864ab3302SCarolineConcatto }
10964ab3302SCarolineConcatto 
11064ab3302SCarolineConcatto void ModFileWriter::WriteAll(const Scope &scope) {
11164ab3302SCarolineConcatto   for (const auto &child : scope.children()) {
11264ab3302SCarolineConcatto     WriteOne(child);
11364ab3302SCarolineConcatto   }
11464ab3302SCarolineConcatto }
11564ab3302SCarolineConcatto 
11664ab3302SCarolineConcatto void ModFileWriter::WriteOne(const Scope &scope) {
11764ab3302SCarolineConcatto   if (scope.kind() == Scope::Kind::Module) {
11864ab3302SCarolineConcatto     auto *symbol{scope.symbol()};
11964ab3302SCarolineConcatto     if (!symbol->test(Symbol::Flag::ModFile)) {
12064ab3302SCarolineConcatto       Write(*symbol);
12164ab3302SCarolineConcatto     }
12264ab3302SCarolineConcatto     WriteAll(scope); // write out submodules
12364ab3302SCarolineConcatto   }
12464ab3302SCarolineConcatto }
12564ab3302SCarolineConcatto 
12664ab3302SCarolineConcatto // Construct the name of a module file. Non-empty ancestorName means submodule.
12764ab3302SCarolineConcatto static std::string ModFileName(const SourceName &name,
12864ab3302SCarolineConcatto     const std::string &ancestorName, const std::string &suffix) {
12964ab3302SCarolineConcatto   std::string result{name.ToString() + suffix};
13064ab3302SCarolineConcatto   return ancestorName.empty() ? result : ancestorName + '-' + result;
13164ab3302SCarolineConcatto }
13264ab3302SCarolineConcatto 
13364ab3302SCarolineConcatto // Write the module file for symbol, which must be a module or submodule.
13464ab3302SCarolineConcatto void ModFileWriter::Write(const Symbol &symbol) {
135e00a3ccfSPeter Klausler   const auto &module{symbol.get<ModuleDetails>()};
136f7a15e00SPeter Klausler   if (module.moduleFileHash()) {
137f7a15e00SPeter Klausler     return; // already written
138f7a15e00SPeter Klausler   }
139e00a3ccfSPeter Klausler   const auto *ancestor{module.ancestor()};
1404b7428e1SPeter Klausler   isSubmodule_ = ancestor != nullptr;
14164ab3302SCarolineConcatto   auto ancestorName{ancestor ? ancestor->GetName().value().ToString() : ""s};
1429629f2c4SPeter Klausler   std::string path{context_.moduleDirectory() + '/' +
14364ab3302SCarolineConcatto       ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())};
14465987954SPeter Klausler 
14565987954SPeter Klausler   UnorderedSymbolSet hermeticModules;
14665987954SPeter Klausler   hermeticModules.insert(symbol);
14765987954SPeter Klausler   UnorderedSymbolSet additionalModules;
14865987954SPeter Klausler   PutSymbols(DEREF(symbol.scope()),
14965987954SPeter Klausler       hermeticModuleFileOutput_ ? &additionalModules : nullptr);
15065987954SPeter Klausler   auto asStr{GetAsString(symbol)};
15165987954SPeter Klausler   while (!additionalModules.empty()) {
15265987954SPeter Klausler     for (auto ref : UnorderedSymbolSet{std::move(additionalModules)}) {
15365987954SPeter Klausler       if (hermeticModules.insert(*ref).second &&
15465987954SPeter Klausler           !ref->owner().IsIntrinsicModules()) {
15565987954SPeter Klausler         PutSymbols(DEREF(ref->scope()), &additionalModules);
15665987954SPeter Klausler         asStr += GetAsString(*ref);
15765987954SPeter Klausler       }
15865987954SPeter Klausler     }
15965987954SPeter Klausler   }
16065987954SPeter Klausler 
161f7a15e00SPeter Klausler   ModuleCheckSumType checkSum;
16265987954SPeter Klausler   if (std::error_code error{
16365987954SPeter Klausler           WriteFile(path, asStr, checkSum, context_.debugModuleWriter())}) {
1640855c454SDavid Truby     context_.Say(
1650855c454SDavid Truby         symbol.name(), "Error writing %s: %s"_err_en_US, path, error.message());
16664ab3302SCarolineConcatto   }
167f7a15e00SPeter Klausler   const_cast<ModuleDetails &>(module).set_moduleFileHash(checkSum);
16864ab3302SCarolineConcatto }
16964ab3302SCarolineConcatto 
170e00a3ccfSPeter Klausler void ModFileWriter::WriteClosure(llvm::raw_ostream &out, const Symbol &symbol,
171e00a3ccfSPeter Klausler     UnorderedSymbolSet &nonIntrinsicModulesWritten) {
172e00a3ccfSPeter Klausler   if (!symbol.has<ModuleDetails>() || symbol.owner().IsIntrinsicModules() ||
173e00a3ccfSPeter Klausler       !nonIntrinsicModulesWritten.insert(symbol).second) {
174e00a3ccfSPeter Klausler     return;
175e00a3ccfSPeter Klausler   }
17665987954SPeter Klausler   PutSymbols(DEREF(symbol.scope()), /*hermeticModules=*/nullptr);
177e00a3ccfSPeter Klausler   needsBuf_.clear(); // omit module checksums
178e00a3ccfSPeter Klausler   auto str{GetAsString(symbol)};
179e00a3ccfSPeter Klausler   for (auto depRef : std::move(usedNonIntrinsicModules_)) {
180e00a3ccfSPeter Klausler     WriteClosure(out, *depRef, nonIntrinsicModulesWritten);
181e00a3ccfSPeter Klausler   }
182e00a3ccfSPeter Klausler   out << std::move(str);
183e00a3ccfSPeter Klausler }
184e00a3ccfSPeter Klausler 
18564ab3302SCarolineConcatto // Return the entire body of the module file
18664ab3302SCarolineConcatto // and clear saved uses, decls, and contains.
18764ab3302SCarolineConcatto std::string ModFileWriter::GetAsString(const Symbol &symbol) {
1888670e499SCaroline Concatto   std::string buf;
1898670e499SCaroline Concatto   llvm::raw_string_ostream all{buf};
190f7a15e00SPeter Klausler   all << needs_.str();
191f7a15e00SPeter Klausler   needs_.str().clear();
19264ab3302SCarolineConcatto   auto &details{symbol.get<ModuleDetails>()};
19364ab3302SCarolineConcatto   if (!details.isSubmodule()) {
19464ab3302SCarolineConcatto     all << "module " << symbol.name();
19564ab3302SCarolineConcatto   } else {
19664ab3302SCarolineConcatto     auto *parent{details.parent()->symbol()};
19764ab3302SCarolineConcatto     auto *ancestor{details.ancestor()->symbol()};
19864ab3302SCarolineConcatto     all << "submodule(" << ancestor->name();
19964ab3302SCarolineConcatto     if (parent != ancestor) {
20064ab3302SCarolineConcatto       all << ':' << parent->name();
20164ab3302SCarolineConcatto     }
20264ab3302SCarolineConcatto     all << ") " << symbol.name();
20364ab3302SCarolineConcatto   }
20464ab3302SCarolineConcatto   all << '\n' << uses_.str();
2058670e499SCaroline Concatto   uses_.str().clear();
20664ab3302SCarolineConcatto   all << useExtraAttrs_.str();
2078670e499SCaroline Concatto   useExtraAttrs_.str().clear();
20864ab3302SCarolineConcatto   all << decls_.str();
2098670e499SCaroline Concatto   decls_.str().clear();
21064ab3302SCarolineConcatto   auto str{contains_.str()};
2118670e499SCaroline Concatto   contains_.str().clear();
21264ab3302SCarolineConcatto   if (!str.empty()) {
21364ab3302SCarolineConcatto     all << "contains\n" << str;
21464ab3302SCarolineConcatto   }
21564ab3302SCarolineConcatto   all << "end\n";
21664ab3302SCarolineConcatto   return all.str();
21764ab3302SCarolineConcatto }
21864ab3302SCarolineConcatto 
2199ce8e63cSPeter Klausler // Collect symbols from constant and specification expressions that are being
2209ce8e63cSPeter Klausler // referenced directly from other modules; they may require new USE
2219ce8e63cSPeter Klausler // associations.
2229ce8e63cSPeter Klausler static void HarvestSymbolsNeededFromOtherModules(
2239ce8e63cSPeter Klausler     SourceOrderedSymbolSet &, const Scope &);
2249ce8e63cSPeter Klausler static void HarvestSymbolsNeededFromOtherModules(
2259ce8e63cSPeter Klausler     SourceOrderedSymbolSet &set, const Symbol &symbol, const Scope &scope) {
2269ce8e63cSPeter Klausler   auto HarvestBound{[&](const Bound &bound) {
2279ce8e63cSPeter Klausler     if (const auto &expr{bound.GetExplicit()}) {
2289ce8e63cSPeter Klausler       for (SymbolRef ref : evaluate::CollectSymbols(*expr)) {
2299ce8e63cSPeter Klausler         set.emplace(*ref);
2301bea0347SPeter Klausler       }
2319ce8e63cSPeter Klausler     }
2329ce8e63cSPeter Klausler   }};
2339ce8e63cSPeter Klausler   auto HarvestShapeSpec{[&](const ShapeSpec &shapeSpec) {
2349ce8e63cSPeter Klausler     HarvestBound(shapeSpec.lbound());
2359ce8e63cSPeter Klausler     HarvestBound(shapeSpec.ubound());
2369ce8e63cSPeter Klausler   }};
2379ce8e63cSPeter Klausler   auto HarvestArraySpec{[&](const ArraySpec &arraySpec) {
2389ce8e63cSPeter Klausler     for (const auto &shapeSpec : arraySpec) {
2399ce8e63cSPeter Klausler       HarvestShapeSpec(shapeSpec);
2409ce8e63cSPeter Klausler     }
2419ce8e63cSPeter Klausler   }};
2429ce8e63cSPeter Klausler 
2439ce8e63cSPeter Klausler   if (symbol.has<DerivedTypeDetails>()) {
2449ce8e63cSPeter Klausler     if (symbol.scope()) {
2459ce8e63cSPeter Klausler       HarvestSymbolsNeededFromOtherModules(set, *symbol.scope());
2469ce8e63cSPeter Klausler     }
2479ce8e63cSPeter Klausler   } else if (const auto &generic{symbol.detailsIf<GenericDetails>()};
2487f542662SPeter Klausler              generic && generic->derivedType()) {
2497f542662SPeter Klausler     const Symbol &dtSym{*generic->derivedType()};
25037180ed7SPeter Klausler     if (dtSym.has<DerivedTypeDetails>()) {
2517f542662SPeter Klausler       if (dtSym.scope()) {
2529ce8e63cSPeter Klausler         HarvestSymbolsNeededFromOtherModules(set, *dtSym.scope());
2537f542662SPeter Klausler       }
25437180ed7SPeter Klausler     } else {
25571113047SPeter Klausler       CHECK(dtSym.has<UseDetails>() || dtSym.has<UseErrorDetails>());
25637180ed7SPeter Klausler     }
2579ce8e63cSPeter Klausler   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
2589ce8e63cSPeter Klausler     HarvestArraySpec(object->shape());
2599ce8e63cSPeter Klausler     HarvestArraySpec(object->coshape());
2609ce8e63cSPeter Klausler     if (IsNamedConstant(symbol) || scope.IsDerivedType()) {
2611bea0347SPeter Klausler       if (object->init()) {
2621bea0347SPeter Klausler         for (SymbolRef ref : evaluate::CollectSymbols(*object->init())) {
2631bea0347SPeter Klausler           set.emplace(*ref);
2641bea0347SPeter Klausler         }
2651bea0347SPeter Klausler       }
2669ce8e63cSPeter Klausler     }
2679ce8e63cSPeter Klausler   } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
2689ce8e63cSPeter Klausler     if (proc->init() && *proc->init() && scope.IsDerivedType()) {
2691bea0347SPeter Klausler       set.emplace(**proc->init());
2701bea0347SPeter Klausler     }
2719ce8e63cSPeter Klausler   } else if (const auto *subp{symbol.detailsIf<SubprogramDetails>()}) {
2729ce8e63cSPeter Klausler     for (const Symbol *dummy : subp->dummyArgs()) {
2739ce8e63cSPeter Klausler       if (dummy) {
2749ce8e63cSPeter Klausler         HarvestSymbolsNeededFromOtherModules(set, *dummy, scope);
2751bea0347SPeter Klausler       }
2761bea0347SPeter Klausler     }
2779ce8e63cSPeter Klausler     if (subp->isFunction()) {
2789ce8e63cSPeter Klausler       HarvestSymbolsNeededFromOtherModules(set, subp->result(), scope);
2799ce8e63cSPeter Klausler     }
2801bea0347SPeter Klausler   }
2811bea0347SPeter Klausler }
2821bea0347SPeter Klausler 
2839ce8e63cSPeter Klausler static void HarvestSymbolsNeededFromOtherModules(
2849ce8e63cSPeter Klausler     SourceOrderedSymbolSet &set, const Scope &scope) {
2859ce8e63cSPeter Klausler   for (const auto &[_, symbol] : scope) {
2869ce8e63cSPeter Klausler     HarvestSymbolsNeededFromOtherModules(set, *symbol, scope);
2879ce8e63cSPeter Klausler   }
2889ce8e63cSPeter Klausler }
2899ce8e63cSPeter Klausler 
2901bea0347SPeter Klausler void ModFileWriter::PrepareRenamings(const Scope &scope) {
2919ce8e63cSPeter Klausler   // Identify use-associated symbols already in scope under some name
2929ce8e63cSPeter Klausler   std::map<const Symbol *, const Symbol *> useMap;
2939ce8e63cSPeter Klausler   for (const auto &[name, symbolRef] : scope) {
2949ce8e63cSPeter Klausler     const Symbol *symbol{&*symbolRef};
2959ce8e63cSPeter Klausler     while (const auto *hostAssoc{symbol->detailsIf<HostAssocDetails>()}) {
2969ce8e63cSPeter Klausler       symbol = &hostAssoc->symbol();
2979ce8e63cSPeter Klausler     }
2989ce8e63cSPeter Klausler     if (const auto *use{symbol->detailsIf<UseDetails>()}) {
2999ce8e63cSPeter Klausler       useMap.emplace(&use->symbol(), symbol);
3009ce8e63cSPeter Klausler     }
3019ce8e63cSPeter Klausler   }
3029ce8e63cSPeter Klausler   // Collect symbols needed from other modules
3039ce8e63cSPeter Klausler   SourceOrderedSymbolSet symbolsNeeded;
3049ce8e63cSPeter Klausler   HarvestSymbolsNeededFromOtherModules(symbolsNeeded, scope);
3059ce8e63cSPeter Klausler   // Establish any necessary renamings of symbols in other modules
3069ce8e63cSPeter Klausler   // to their names in this scope, creating those new names when needed.
3079ce8e63cSPeter Klausler   auto &renamings{context_.moduleFileOutputRenamings()};
3089ce8e63cSPeter Klausler   for (SymbolRef s : symbolsNeeded) {
30982bd7adbSPeter Klausler     if (s->owner().kind() == Scope::Kind::DerivedType) {
31082bd7adbSPeter Klausler       continue; // component or binding: ok
31182bd7adbSPeter Klausler     }
3121bea0347SPeter Klausler     const Scope *sMod{FindModuleContaining(s->owner())};
3139ce8e63cSPeter Klausler     if (!sMod || sMod == &scope) {
3149ce8e63cSPeter Klausler       continue;
3159ce8e63cSPeter Klausler     }
3169ce8e63cSPeter Klausler     if (auto iter{useMap.find(&*s)}; iter != useMap.end()) {
3179ce8e63cSPeter Klausler       renamings.emplace(&*s, iter->second->name());
3181bea0347SPeter Klausler       continue;
3191bea0347SPeter Klausler     }
3201bea0347SPeter Klausler     SourceName rename{s->name()};
3211bea0347SPeter Klausler     if (const Symbol * found{scope.FindSymbol(s->name())}) {
3221bea0347SPeter Klausler       if (found == &*s) {
3231bea0347SPeter Klausler         continue; // available in scope
3241bea0347SPeter Klausler       }
3251bea0347SPeter Klausler       if (const auto *generic{found->detailsIf<GenericDetails>()}) {
3261bea0347SPeter Klausler         if (generic->derivedType() == &*s || generic->specific() == &*s) {
3271bea0347SPeter Klausler           continue;
3281bea0347SPeter Klausler         }
3291bea0347SPeter Klausler       } else if (found->has<UseDetails>()) {
3301bea0347SPeter Klausler         if (&found->GetUltimate() == &*s) {
3311bea0347SPeter Klausler           continue; // already use-associated with same name
3321bea0347SPeter Klausler         }
3331bea0347SPeter Klausler       }
3341bea0347SPeter Klausler       if (&s->owner() != &found->owner()) { // Symbol needs renaming
3351bea0347SPeter Klausler         rename = scope.context().SaveTempName(
3361bea0347SPeter Klausler             DEREF(sMod->symbol()).name().ToString() + "$" +
3371bea0347SPeter Klausler             s->name().ToString());
3381bea0347SPeter Klausler       }
3391bea0347SPeter Klausler     }
3401bea0347SPeter Klausler     // Symbol is used in this scope but not visible under its name
3411bea0347SPeter Klausler     if (sMod->parent().IsIntrinsicModules()) {
3421bea0347SPeter Klausler       uses_ << "use,intrinsic::";
3431bea0347SPeter Klausler     } else {
3441bea0347SPeter Klausler       uses_ << "use ";
3451bea0347SPeter Klausler     }
3461bea0347SPeter Klausler     uses_ << DEREF(sMod->symbol()).name() << ",only:";
3471bea0347SPeter Klausler     if (rename != s->name()) {
3481bea0347SPeter Klausler       uses_ << rename << "=>";
3499ce8e63cSPeter Klausler       renamings.emplace(&*s, rename);
3501bea0347SPeter Klausler     }
3511bea0347SPeter Klausler     uses_ << s->name() << '\n';
3521bea0347SPeter Klausler     useExtraAttrs_ << "private::" << rename << '\n';
3531bea0347SPeter Klausler   }
3541bea0347SPeter Klausler }
3551bea0347SPeter Klausler 
35664ab3302SCarolineConcatto // Put out the visible symbols from scope.
35765987954SPeter Klausler void ModFileWriter::PutSymbols(
35865987954SPeter Klausler     const Scope &scope, UnorderedSymbolSet *hermeticModules) {
35986f59de1STim Keith   SymbolVector sorted;
36086f59de1STim Keith   SymbolVector uses;
3619ce8e63cSPeter Klausler   auto &renamings{context_.moduleFileOutputRenamings()};
3629ce8e63cSPeter Klausler   auto previousRenamings{std::move(renamings)};
3631bea0347SPeter Klausler   PrepareRenamings(scope);
364f7a15e00SPeter Klausler   UnorderedSymbolSet modules;
3659ce8e63cSPeter Klausler   CollectSymbols(scope, sorted, uses, modules);
366f7a15e00SPeter Klausler   // Write module files for dependencies first so that their
367f7a15e00SPeter Klausler   // hashes are known.
368f7a15e00SPeter Klausler   for (auto ref : modules) {
36965987954SPeter Klausler     if (hermeticModules) {
37065987954SPeter Klausler       hermeticModules->insert(*ref);
37165987954SPeter Klausler     } else {
372f7a15e00SPeter Klausler       Write(*ref);
373f7a15e00SPeter Klausler       needs_ << ModHeader::need
37465987954SPeter Klausler              << CheckSumString(
37565987954SPeter Klausler                     ref->get<ModuleDetails>().moduleFileHash().value())
376f7a15e00SPeter Klausler              << (ref->owner().IsIntrinsicModules() ? " i " : " n ")
377f7a15e00SPeter Klausler              << ref->name().ToString() << '\n';
378f7a15e00SPeter Klausler     }
37965987954SPeter Klausler   }
38086f59de1STim Keith   std::string buf; // stuff after CONTAINS in derived type
38186f59de1STim Keith   llvm::raw_string_ostream typeBindings{buf};
38286f59de1STim Keith   for (const Symbol &symbol : sorted) {
383d60a0220Speter klausler     if (!symbol.test(Symbol::Flag::CompilerCreated)) {
38464ab3302SCarolineConcatto       PutSymbol(typeBindings, symbol);
38564ab3302SCarolineConcatto     }
386d60a0220Speter klausler   }
38786f59de1STim Keith   for (const Symbol &symbol : uses) {
38886f59de1STim Keith     PutUse(symbol);
38986f59de1STim Keith   }
390d60a0220Speter klausler   for (const auto &set : scope.equivalenceSets()) {
391d60a0220Speter klausler     if (!set.empty() &&
392d60a0220Speter klausler         !set.front().symbol.test(Symbol::Flag::CompilerCreated)) {
393d60a0220Speter klausler       char punctuation{'('};
394d60a0220Speter klausler       decls_ << "equivalence";
395d60a0220Speter klausler       for (const auto &object : set) {
396d60a0220Speter klausler         decls_ << punctuation << object.AsFortran();
397d60a0220Speter klausler         punctuation = ',';
398d60a0220Speter klausler       }
399d60a0220Speter klausler       decls_ << ")\n";
400d60a0220Speter klausler     }
401d60a0220Speter klausler   }
402c14cf92bSPeter Klausler   CHECK(typeBindings.str().empty());
4039ce8e63cSPeter Klausler   renamings = std::move(previousRenamings);
404c14cf92bSPeter Klausler }
405c14cf92bSPeter Klausler 
406c14cf92bSPeter Klausler // Emit components in order
407c14cf92bSPeter Klausler bool ModFileWriter::PutComponents(const Symbol &typeSymbol) {
408c14cf92bSPeter Klausler   const auto &scope{DEREF(typeSymbol.scope())};
409c14cf92bSPeter Klausler   std::string buf; // stuff after CONTAINS in derived type
410c14cf92bSPeter Klausler   llvm::raw_string_ostream typeBindings{buf};
411c14cf92bSPeter Klausler   UnorderedSymbolSet emitted;
412c14cf92bSPeter Klausler   SymbolVector symbols{scope.GetSymbols()};
413539a6b50SPeter Klausler   // Emit type parameter declarations first, in order
414539a6b50SPeter Klausler   const auto &details{typeSymbol.get<DerivedTypeDetails>()};
415539a6b50SPeter Klausler   for (const Symbol &symbol : details.paramDeclOrder()) {
416539a6b50SPeter Klausler     CHECK(symbol.has<TypeParamDetails>());
417c14cf92bSPeter Klausler     PutSymbol(typeBindings, symbol);
418c14cf92bSPeter Klausler     emitted.emplace(symbol);
419c14cf92bSPeter Klausler   }
420539a6b50SPeter Klausler   // Emit actual components in component order.
421c14cf92bSPeter Klausler   for (SourceName name : details.componentNames()) {
422c14cf92bSPeter Klausler     auto iter{scope.find(name)};
423c14cf92bSPeter Klausler     if (iter != scope.end()) {
424c14cf92bSPeter Klausler       const Symbol &component{*iter->second};
425c14cf92bSPeter Klausler       if (!component.test(Symbol::Flag::ParentComp)) {
426c14cf92bSPeter Klausler         PutSymbol(typeBindings, component);
427c14cf92bSPeter Klausler       }
428c14cf92bSPeter Klausler       emitted.emplace(component);
429c14cf92bSPeter Klausler     }
430c14cf92bSPeter Klausler   }
431c14cf92bSPeter Klausler   // Emit remaining symbols from the type's scope
432c14cf92bSPeter Klausler   for (const Symbol &symbol : symbols) {
433c14cf92bSPeter Klausler     if (emitted.find(symbol) == emitted.end()) {
434c14cf92bSPeter Klausler       PutSymbol(typeBindings, symbol);
435c14cf92bSPeter Klausler     }
436c14cf92bSPeter Klausler   }
43764ab3302SCarolineConcatto   if (auto str{typeBindings.str()}; !str.empty()) {
43864ab3302SCarolineConcatto     CHECK(scope.IsDerivedType());
43964ab3302SCarolineConcatto     decls_ << "contains\n" << str;
44037b2e2b0Speter klausler     return true;
44137b2e2b0Speter klausler   } else {
44237b2e2b0Speter klausler     return false;
44364ab3302SCarolineConcatto   }
44464ab3302SCarolineConcatto }
44564ab3302SCarolineConcatto 
4461db42fa6SSlava Zakharin // Return the symbol's attributes that should be written
4471db42fa6SSlava Zakharin // into the mod file.
4481db42fa6SSlava Zakharin static Attrs getSymbolAttrsToWrite(const Symbol &symbol) {
4491db42fa6SSlava Zakharin   // Is SAVE attribute is implicit, it should be omitted
4501db42fa6SSlava Zakharin   // to not violate F202x C862 for a common block member.
4511db42fa6SSlava Zakharin   return symbol.attrs() & ~(symbol.implicitAttrs() & Attrs{Attr::SAVE});
4521db42fa6SSlava Zakharin }
4531db42fa6SSlava Zakharin 
4544864d9f7Speter klausler static llvm::raw_ostream &PutGenericName(
4554864d9f7Speter klausler     llvm::raw_ostream &os, const Symbol &symbol) {
4564864d9f7Speter klausler   if (IsGenericDefinedOp(symbol)) {
4574864d9f7Speter klausler     return os << "operator(" << symbol.name() << ')';
4584864d9f7Speter klausler   } else {
4594864d9f7Speter klausler     return os << symbol.name();
4604864d9f7Speter klausler   }
4614864d9f7Speter klausler }
4624864d9f7Speter klausler 
46364ab3302SCarolineConcatto // Emit a symbol to decls_, except for bindings in a derived type (type-bound
46464ab3302SCarolineConcatto // procedures, type-bound generics, final procedures) which go to typeBindings.
46564ab3302SCarolineConcatto void ModFileWriter::PutSymbol(
4668670e499SCaroline Concatto     llvm::raw_ostream &typeBindings, const Symbol &symbol) {
467cd03e96fSPeter Klausler   common::visit(
468cd03e96fSPeter Klausler       common::visitors{
46964ab3302SCarolineConcatto           [&](const ModuleDetails &) { /* should be current module */ },
47064ab3302SCarolineConcatto           [&](const DerivedTypeDetails &) { PutDerivedType(symbol); },
47164ab3302SCarolineConcatto           [&](const SubprogramDetails &) { PutSubprogram(symbol); },
47264ab3302SCarolineConcatto           [&](const GenericDetails &x) {
47364ab3302SCarolineConcatto             if (symbol.owner().IsDerivedType()) {
47464ab3302SCarolineConcatto               // generic binding
47564ab3302SCarolineConcatto               for (const Symbol &proc : x.specificProcs()) {
4764864d9f7Speter klausler                 PutGenericName(typeBindings << "generic::", symbol)
4774864d9f7Speter klausler                     << "=>" << proc.name() << '\n';
47864ab3302SCarolineConcatto               }
47964ab3302SCarolineConcatto             } else {
48064ab3302SCarolineConcatto               PutGeneric(symbol);
48164ab3302SCarolineConcatto             }
48264ab3302SCarolineConcatto           },
48364ab3302SCarolineConcatto           [&](const UseDetails &) { PutUse(symbol); },
48464ab3302SCarolineConcatto           [](const UseErrorDetails &) {},
48564ab3302SCarolineConcatto           [&](const ProcBindingDetails &x) {
48664ab3302SCarolineConcatto             bool deferred{symbol.attrs().test(Attr::DEFERRED)};
48764ab3302SCarolineConcatto             typeBindings << "procedure";
48864ab3302SCarolineConcatto             if (deferred) {
48964ab3302SCarolineConcatto               typeBindings << '(' << x.symbol().name() << ')';
49064ab3302SCarolineConcatto             }
49164ab3302SCarolineConcatto             PutPassName(typeBindings, x.passName());
49264ab3302SCarolineConcatto             auto attrs{symbol.attrs()};
49364ab3302SCarolineConcatto             if (x.passName()) {
49464ab3302SCarolineConcatto               attrs.reset(Attr::PASS);
49564ab3302SCarolineConcatto             }
49664ab3302SCarolineConcatto             PutAttrs(typeBindings, attrs);
49764ab3302SCarolineConcatto             typeBindings << "::" << symbol.name();
49864ab3302SCarolineConcatto             if (!deferred && x.symbol().name() != symbol.name()) {
49964ab3302SCarolineConcatto               typeBindings << "=>" << x.symbol().name();
50064ab3302SCarolineConcatto             }
50164ab3302SCarolineConcatto             typeBindings << '\n';
50264ab3302SCarolineConcatto           },
50364ab3302SCarolineConcatto           [&](const NamelistDetails &x) {
50464ab3302SCarolineConcatto             decls_ << "namelist/" << symbol.name();
50564ab3302SCarolineConcatto             char sep{'/'};
50664ab3302SCarolineConcatto             for (const Symbol &object : x.objects()) {
50764ab3302SCarolineConcatto               decls_ << sep << object.name();
50864ab3302SCarolineConcatto               sep = ',';
50964ab3302SCarolineConcatto             }
51064ab3302SCarolineConcatto             decls_ << '\n';
5114b7428e1SPeter Klausler             if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) {
5124b7428e1SPeter Klausler               decls_ << "private::" << symbol.name() << '\n';
5134b7428e1SPeter Klausler             }
51464ab3302SCarolineConcatto           },
51564ab3302SCarolineConcatto           [&](const CommonBlockDetails &x) {
51664ab3302SCarolineConcatto             decls_ << "common/" << symbol.name();
51764ab3302SCarolineConcatto             char sep = '/';
518d5c05cedSTim Keith             for (const auto &object : x.objects()) {
519d5c05cedSTim Keith               decls_ << sep << object->name();
52064ab3302SCarolineConcatto               sep = ',';
52164ab3302SCarolineConcatto             }
52264ab3302SCarolineConcatto             decls_ << '\n';
52364ab3302SCarolineConcatto             if (symbol.attrs().test(Attr::BIND_C)) {
5241db42fa6SSlava Zakharin               PutAttrs(decls_, getSymbolAttrsToWrite(symbol), x.bindName(),
52569e2665cSPeter Klausler                   x.isExplicitBindName(), ""s);
52664ab3302SCarolineConcatto               decls_ << "::/" << symbol.name() << "/\n";
52764ab3302SCarolineConcatto             }
52864ab3302SCarolineConcatto           },
52964ab3302SCarolineConcatto           [](const HostAssocDetails &) {},
53064ab3302SCarolineConcatto           [](const MiscDetails &) {},
5312cbd4fc4SPeixin-Qiao           [&](const auto &) {
5322cbd4fc4SPeixin-Qiao             PutEntity(decls_, symbol);
53322f63b5cSValentin Clement             PutDirective(decls_, symbol);
5342cbd4fc4SPeixin-Qiao           },
53564ab3302SCarolineConcatto       },
53664ab3302SCarolineConcatto       symbol.details());
53764ab3302SCarolineConcatto }
53864ab3302SCarolineConcatto 
539c14cf92bSPeter Klausler void ModFileWriter::PutDerivedType(
540c14cf92bSPeter Klausler     const Symbol &typeSymbol, const Scope *scope) {
54164ab3302SCarolineConcatto   auto &details{typeSymbol.get<DerivedTypeDetails>()};
542c14cf92bSPeter Klausler   if (details.isDECStructure()) {
543c14cf92bSPeter Klausler     PutDECStructure(typeSymbol, scope);
544c14cf92bSPeter Klausler     return;
545c14cf92bSPeter Klausler   }
54664ab3302SCarolineConcatto   PutAttrs(decls_ << "type", typeSymbol.attrs());
54764ab3302SCarolineConcatto   if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
54864ab3302SCarolineConcatto     decls_ << ",extends(" << extends->name() << ')';
54964ab3302SCarolineConcatto   }
55064ab3302SCarolineConcatto   decls_ << "::" << typeSymbol.name();
551539a6b50SPeter Klausler   if (!details.paramNameOrder().empty()) {
55264ab3302SCarolineConcatto     char sep{'('};
553539a6b50SPeter Klausler     for (const SymbolRef &ref : details.paramNameOrder()) {
554539a6b50SPeter Klausler       decls_ << sep << ref->name();
55564ab3302SCarolineConcatto       sep = ',';
55664ab3302SCarolineConcatto     }
55764ab3302SCarolineConcatto     decls_ << ')';
55864ab3302SCarolineConcatto   }
55964ab3302SCarolineConcatto   decls_ << '\n';
56064ab3302SCarolineConcatto   if (details.sequence()) {
56164ab3302SCarolineConcatto     decls_ << "sequence\n";
56264ab3302SCarolineConcatto   }
563c14cf92bSPeter Klausler   bool contains{PutComponents(typeSymbol)};
56437b2e2b0Speter klausler   if (!details.finals().empty()) {
56537b2e2b0Speter klausler     const char *sep{contains ? "final::" : "contains\nfinal::"};
56637b2e2b0Speter klausler     for (const auto &pair : details.finals()) {
56737b2e2b0Speter klausler       decls_ << sep << pair.second->name();
56837b2e2b0Speter klausler       sep = ",";
56937b2e2b0Speter klausler     }
57037b2e2b0Speter klausler     if (*sep == ',') {
57137b2e2b0Speter klausler       decls_ << '\n';
57237b2e2b0Speter klausler     }
57337b2e2b0Speter klausler   }
57464ab3302SCarolineConcatto   decls_ << "end type\n";
57564ab3302SCarolineConcatto }
57664ab3302SCarolineConcatto 
577c14cf92bSPeter Klausler void ModFileWriter::PutDECStructure(
578c14cf92bSPeter Klausler     const Symbol &typeSymbol, const Scope *scope) {
579c14cf92bSPeter Klausler   if (emittedDECStructures_.find(typeSymbol) != emittedDECStructures_.end()) {
580c14cf92bSPeter Klausler     return;
581c14cf92bSPeter Klausler   }
582c14cf92bSPeter Klausler   if (!scope && context_.IsTempName(typeSymbol.name().ToString())) {
583c14cf92bSPeter Klausler     return; // defer until used
584c14cf92bSPeter Klausler   }
585c14cf92bSPeter Klausler   emittedDECStructures_.insert(typeSymbol);
586c14cf92bSPeter Klausler   decls_ << "structure ";
587c14cf92bSPeter Klausler   if (!context_.IsTempName(typeSymbol.name().ToString())) {
588c14cf92bSPeter Klausler     decls_ << typeSymbol.name();
589c14cf92bSPeter Klausler   }
590c14cf92bSPeter Klausler   if (scope && scope->kind() == Scope::Kind::DerivedType) {
591c14cf92bSPeter Klausler     // Nested STRUCTURE: emit entity declarations right now
592c14cf92bSPeter Klausler     // on the STRUCTURE statement.
593c14cf92bSPeter Klausler     bool any{false};
594c14cf92bSPeter Klausler     for (const auto &ref : scope->GetSymbols()) {
595c14cf92bSPeter Klausler       const auto *object{ref->detailsIf<ObjectEntityDetails>()};
596c14cf92bSPeter Klausler       if (object && object->type() &&
597c14cf92bSPeter Klausler           object->type()->category() == DeclTypeSpec::TypeDerived &&
598c14cf92bSPeter Klausler           &object->type()->derivedTypeSpec().typeSymbol() == &typeSymbol) {
599c14cf92bSPeter Klausler         if (any) {
600c14cf92bSPeter Klausler           decls_ << ',';
601c14cf92bSPeter Klausler         } else {
602c14cf92bSPeter Klausler           any = true;
603c14cf92bSPeter Klausler         }
604c14cf92bSPeter Klausler         decls_ << ref->name();
605c14cf92bSPeter Klausler         PutShape(decls_, object->shape(), '(', ')');
6069ce8e63cSPeter Klausler         PutInit(decls_, *ref, object->init(), nullptr);
607c14cf92bSPeter Klausler         emittedDECFields_.insert(*ref);
608c14cf92bSPeter Klausler       } else if (any) {
609c14cf92bSPeter Klausler         break; // any later use of this structure will use RECORD/str/
610c14cf92bSPeter Klausler       }
611c14cf92bSPeter Klausler     }
612c14cf92bSPeter Klausler   }
613c14cf92bSPeter Klausler   decls_ << '\n';
614c14cf92bSPeter Klausler   PutComponents(typeSymbol);
615c14cf92bSPeter Klausler   decls_ << "end structure\n";
616c14cf92bSPeter Klausler }
617c14cf92bSPeter Klausler 
61864ab3302SCarolineConcatto // Attributes that may be in a subprogram prefix
61964ab3302SCarolineConcatto static const Attrs subprogramPrefixAttrs{Attr::ELEMENTAL, Attr::IMPURE,
62064ab3302SCarolineConcatto     Attr::MODULE, Attr::NON_RECURSIVE, Attr::PURE, Attr::RECURSIVE};
62164ab3302SCarolineConcatto 
622c8ad8024SValentin Clement static void PutOpenACCDeviceTypeRoutineInfo(
623c8ad8024SValentin Clement     llvm::raw_ostream &os, const OpenACCRoutineDeviceTypeInfo &info) {
62422209a67SValentin Clement   if (info.isSeq()) {
62522209a67SValentin Clement     os << " seq";
62622209a67SValentin Clement   }
62722209a67SValentin Clement   if (info.isGang()) {
62822209a67SValentin Clement     os << " gang";
62922209a67SValentin Clement     if (info.gangDim() > 0) {
63022209a67SValentin Clement       os << "(dim: " << info.gangDim() << ")";
63122209a67SValentin Clement     }
63222209a67SValentin Clement   }
63322209a67SValentin Clement   if (info.isVector()) {
63422209a67SValentin Clement     os << " vector";
63522209a67SValentin Clement   }
63622209a67SValentin Clement   if (info.isWorker()) {
63722209a67SValentin Clement     os << " worker";
63822209a67SValentin Clement   }
63922209a67SValentin Clement   if (info.bindName()) {
64022209a67SValentin Clement     os << " bind(" << *info.bindName() << ")";
64122209a67SValentin Clement   }
642c8ad8024SValentin Clement }
643c8ad8024SValentin Clement 
644c8ad8024SValentin Clement static void PutOpenACCRoutineInfo(
645c8ad8024SValentin Clement     llvm::raw_ostream &os, const SubprogramDetails &details) {
646c8ad8024SValentin Clement   for (auto info : details.openACCRoutineInfos()) {
647c8ad8024SValentin Clement     os << "!$acc routine";
648c8ad8024SValentin Clement 
649c8ad8024SValentin Clement     PutOpenACCDeviceTypeRoutineInfo(os, info);
650c8ad8024SValentin Clement 
651c8ad8024SValentin Clement     if (info.isNohost()) {
652c8ad8024SValentin Clement       os << " nohost";
653c8ad8024SValentin Clement     }
654c8ad8024SValentin Clement 
655c8ad8024SValentin Clement     for (auto dtype : info.deviceTypeInfos()) {
656c8ad8024SValentin Clement       os << " device_type(";
657c8ad8024SValentin Clement       if (dtype.dType() == common::OpenACCDeviceType::Star) {
658c8ad8024SValentin Clement         os << "*";
659c8ad8024SValentin Clement       } else {
660c8ad8024SValentin Clement         os << parser::ToLowerCaseLetters(common::EnumToString(dtype.dType()));
661c8ad8024SValentin Clement       }
662c8ad8024SValentin Clement       os << ")";
663c8ad8024SValentin Clement 
664c8ad8024SValentin Clement       PutOpenACCDeviceTypeRoutineInfo(os, dtype);
665c8ad8024SValentin Clement     }
666c8ad8024SValentin Clement 
66722209a67SValentin Clement     os << "\n";
66822209a67SValentin Clement   }
66922209a67SValentin Clement }
67022209a67SValentin Clement 
67164ab3302SCarolineConcatto void ModFileWriter::PutSubprogram(const Symbol &symbol) {
67264ab3302SCarolineConcatto   auto &details{symbol.get<SubprogramDetails>()};
673b67984d3SPeter Klausler   if (const Symbol * interface{details.moduleInterface()}) {
674bb7e31bcSPeter Klausler     const Scope *module{FindModuleContaining(interface->owner())};
675bb7e31bcSPeter Klausler     if (module && module != &symbol.owner()) {
676bb7e31bcSPeter Klausler       // Interface is in ancestor module
677bb7e31bcSPeter Klausler     } else {
678b67984d3SPeter Klausler       PutSubprogram(*interface);
679b67984d3SPeter Klausler     }
680bb7e31bcSPeter Klausler   }
681b67984d3SPeter Klausler   auto attrs{symbol.attrs()};
68264ab3302SCarolineConcatto   Attrs bindAttrs{};
68364ab3302SCarolineConcatto   if (attrs.test(Attr::BIND_C)) {
68464ab3302SCarolineConcatto     // bind(c) is a suffix, not prefix
68564ab3302SCarolineConcatto     bindAttrs.set(Attr::BIND_C, true);
68664ab3302SCarolineConcatto     attrs.set(Attr::BIND_C, false);
68764ab3302SCarolineConcatto   }
688d55627d2STim Keith   bool isAbstract{attrs.test(Attr::ABSTRACT)};
689d55627d2STim Keith   if (isAbstract) {
690d55627d2STim Keith     attrs.set(Attr::ABSTRACT, false);
691d55627d2STim Keith   }
69264ab3302SCarolineConcatto   Attrs prefixAttrs{subprogramPrefixAttrs & attrs};
69364ab3302SCarolineConcatto   // emit any non-prefix attributes in an attribute statement
69464ab3302SCarolineConcatto   attrs &= ~subprogramPrefixAttrs;
6958670e499SCaroline Concatto   std::string ssBuf;
6968670e499SCaroline Concatto   llvm::raw_string_ostream ss{ssBuf};
69764ab3302SCarolineConcatto   PutAttrs(ss, attrs);
69864ab3302SCarolineConcatto   if (!ss.str().empty()) {
69964ab3302SCarolineConcatto     decls_ << ss.str().substr(1) << "::" << symbol.name() << '\n';
70064ab3302SCarolineConcatto   }
70164ab3302SCarolineConcatto   bool isInterface{details.isInterface()};
7028670e499SCaroline Concatto   llvm::raw_ostream &os{isInterface ? decls_ : contains_};
70364ab3302SCarolineConcatto   if (isInterface) {
704d55627d2STim Keith     os << (isAbstract ? "abstract " : "") << "interface\n";
70564ab3302SCarolineConcatto   }
70669e2665cSPeter Klausler   PutAttrs(os, prefixAttrs, nullptr, false, ""s, " "s);
70727f71807SPeter Klausler   if (auto attrs{details.cudaSubprogramAttrs()}) {
70827f71807SPeter Klausler     if (*attrs == common::CUDASubprogramAttrs::HostDevice) {
70927f71807SPeter Klausler       os << "attributes(host,device) ";
71027f71807SPeter Klausler     } else {
71127f71807SPeter Klausler       PutLower(os << "attributes(", common::EnumToString(*attrs)) << ") ";
71227f71807SPeter Klausler     }
71327f71807SPeter Klausler     if (!details.cudaLaunchBounds().empty()) {
71427f71807SPeter Klausler       os << "launch_bounds";
71527f71807SPeter Klausler       char sep{'('};
71627f71807SPeter Klausler       for (auto x : details.cudaLaunchBounds()) {
71727f71807SPeter Klausler         os << sep << x;
71827f71807SPeter Klausler         sep = ',';
71927f71807SPeter Klausler       }
72027f71807SPeter Klausler       os << ") ";
72127f71807SPeter Klausler     }
72227f71807SPeter Klausler     if (!details.cudaClusterDims().empty()) {
72327f71807SPeter Klausler       os << "cluster_dims";
72427f71807SPeter Klausler       char sep{'('};
72527f71807SPeter Klausler       for (auto x : details.cudaClusterDims()) {
72627f71807SPeter Klausler         os << sep << x;
72727f71807SPeter Klausler         sep = ',';
72827f71807SPeter Klausler       }
72927f71807SPeter Klausler       os << ") ";
73027f71807SPeter Klausler     }
73127f71807SPeter Klausler   }
73264ab3302SCarolineConcatto   os << (details.isFunction() ? "function " : "subroutine ");
73364ab3302SCarolineConcatto   os << symbol.name() << '(';
73464ab3302SCarolineConcatto   int n = 0;
73564ab3302SCarolineConcatto   for (const auto &dummy : details.dummyArgs()) {
73664ab3302SCarolineConcatto     if (n++ > 0) {
73764ab3302SCarolineConcatto       os << ',';
73864ab3302SCarolineConcatto     }
7393ed2909fSPete Steinfeld     if (dummy) {
74064ab3302SCarolineConcatto       os << dummy->name();
7413ed2909fSPete Steinfeld     } else {
7423ed2909fSPete Steinfeld       os << "*";
7433ed2909fSPete Steinfeld     }
74464ab3302SCarolineConcatto   }
74564ab3302SCarolineConcatto   os << ')';
74669e2665cSPeter Klausler   PutAttrs(os, bindAttrs, details.bindName(), details.isExplicitBindName(),
74769e2665cSPeter Klausler       " "s, ""s);
74864ab3302SCarolineConcatto   if (details.isFunction()) {
74964ab3302SCarolineConcatto     const Symbol &result{details.result()};
75064ab3302SCarolineConcatto     if (result.name() != symbol.name()) {
75164ab3302SCarolineConcatto       os << " result(" << result.name() << ')';
75264ab3302SCarolineConcatto     }
75364ab3302SCarolineConcatto   }
75464ab3302SCarolineConcatto   os << '\n';
755c42f6314Speter klausler   // walk symbols, collect ones needed for interface
756c42f6314Speter klausler   const Scope &scope{
757c42f6314Speter klausler       details.entryScope() ? *details.entryScope() : DEREF(symbol.scope())};
758c42f6314Speter klausler   SubprogramSymbolCollector collector{symbol, scope};
759c42f6314Speter klausler   collector.Collect();
7608670e499SCaroline Concatto   std::string typeBindingsBuf;
7618670e499SCaroline Concatto   llvm::raw_string_ostream typeBindings{typeBindingsBuf};
762c42f6314Speter klausler   ModFileWriter writer{context_};
76364ab3302SCarolineConcatto   for (const Symbol &need : collector.symbols()) {
76464ab3302SCarolineConcatto     writer.PutSymbol(typeBindings, need);
76564ab3302SCarolineConcatto   }
76664ab3302SCarolineConcatto   CHECK(typeBindings.str().empty());
76764ab3302SCarolineConcatto   os << writer.uses_.str();
76864ab3302SCarolineConcatto   for (const SourceName &import : collector.imports()) {
76964ab3302SCarolineConcatto     decls_ << "import::" << import << "\n";
77064ab3302SCarolineConcatto   }
77164ab3302SCarolineConcatto   os << writer.decls_.str();
77222209a67SValentin Clement   PutOpenACCRoutineInfo(os, details);
77364ab3302SCarolineConcatto   os << "end\n";
77464ab3302SCarolineConcatto   if (isInterface) {
77564ab3302SCarolineConcatto     os << "end interface\n";
77664ab3302SCarolineConcatto   }
77764ab3302SCarolineConcatto }
77864ab3302SCarolineConcatto 
77964ab3302SCarolineConcatto static bool IsIntrinsicOp(const Symbol &symbol) {
78064ab3302SCarolineConcatto   if (const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()}) {
78164ab3302SCarolineConcatto     return details->kind().IsIntrinsicOperator();
78264ab3302SCarolineConcatto   } else {
78364ab3302SCarolineConcatto     return false;
78464ab3302SCarolineConcatto   }
78564ab3302SCarolineConcatto }
78664ab3302SCarolineConcatto 
78764ab3302SCarolineConcatto void ModFileWriter::PutGeneric(const Symbol &symbol) {
78886f59de1STim Keith   const auto &genericOwner{symbol.owner()};
78964ab3302SCarolineConcatto   auto &details{symbol.get<GenericDetails>()};
79064ab3302SCarolineConcatto   PutGenericName(decls_ << "interface ", symbol) << '\n';
79164ab3302SCarolineConcatto   for (const Symbol &specific : details.specificProcs()) {
79286f59de1STim Keith     if (specific.owner() == genericOwner) {
79364ab3302SCarolineConcatto       decls_ << "procedure::" << specific.name() << '\n';
79464ab3302SCarolineConcatto     }
79586f59de1STim Keith   }
79664ab3302SCarolineConcatto   decls_ << "end interface\n";
7974b7428e1SPeter Klausler   if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) {
79864ab3302SCarolineConcatto     PutGenericName(decls_ << "private::", symbol) << '\n';
79964ab3302SCarolineConcatto   }
80064ab3302SCarolineConcatto }
80164ab3302SCarolineConcatto 
80264ab3302SCarolineConcatto void ModFileWriter::PutUse(const Symbol &symbol) {
80364ab3302SCarolineConcatto   auto &details{symbol.get<UseDetails>()};
80464ab3302SCarolineConcatto   auto &use{details.symbol()};
80515faac90SPeter Klausler   const Symbol &module{GetUsedModule(details)};
80615faac90SPeter Klausler   if (use.owner().parent().IsIntrinsicModules()) {
80715faac90SPeter Klausler     uses_ << "use,intrinsic::";
80815faac90SPeter Klausler   } else {
80915faac90SPeter Klausler     uses_ << "use ";
810e00a3ccfSPeter Klausler     usedNonIntrinsicModules_.insert(module);
81115faac90SPeter Klausler   }
81215faac90SPeter Klausler   uses_ << module.name() << ",only:";
81315faac90SPeter Klausler   PutGenericName(uses_, symbol);
81464ab3302SCarolineConcatto   // Can have intrinsic op with different local-name and use-name
81564ab3302SCarolineConcatto   // (e.g. `operator(<)` and `operator(.lt.)`) but rename is not allowed
81664ab3302SCarolineConcatto   if (!IsIntrinsicOp(symbol) && use.name() != symbol.name()) {
81764ab3302SCarolineConcatto     PutGenericName(uses_ << "=>", use);
81864ab3302SCarolineConcatto   }
81964ab3302SCarolineConcatto   uses_ << '\n';
82064ab3302SCarolineConcatto   PutUseExtraAttr(Attr::VOLATILE, symbol, use);
82164ab3302SCarolineConcatto   PutUseExtraAttr(Attr::ASYNCHRONOUS, symbol, use);
8224b7428e1SPeter Klausler   if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) {
8234864d9f7Speter klausler     PutGenericName(useExtraAttrs_ << "private::", symbol) << '\n';
8244864d9f7Speter klausler   }
82564ab3302SCarolineConcatto }
82664ab3302SCarolineConcatto 
82764ab3302SCarolineConcatto // We have "USE local => use" in this module. If attr was added locally
82864ab3302SCarolineConcatto // (i.e. on local but not on use), also write it out in the mod file.
82964ab3302SCarolineConcatto void ModFileWriter::PutUseExtraAttr(
83064ab3302SCarolineConcatto     Attr attr, const Symbol &local, const Symbol &use) {
83164ab3302SCarolineConcatto   if (local.attrs().test(attr) && !use.attrs().test(attr)) {
83264ab3302SCarolineConcatto     PutAttr(useExtraAttrs_, attr) << "::";
83364ab3302SCarolineConcatto     useExtraAttrs_ << local.name() << '\n';
83464ab3302SCarolineConcatto   }
83564ab3302SCarolineConcatto }
83664ab3302SCarolineConcatto 
8374864d9f7Speter klausler static inline SourceName NameInModuleFile(const Symbol &symbol) {
838a3e9d3c2SPeter Klausler   if (const auto *use{symbol.detailsIf<UseDetails>()}) {
8394864d9f7Speter klausler     if (use->symbol().attrs().test(Attr::PRIVATE)) {
8404864d9f7Speter klausler       // Avoid the use in sorting of names created to access private
8414864d9f7Speter klausler       // specific procedures as a result of generic resolution;
8424864d9f7Speter klausler       // they're not in the cooked source.
8434864d9f7Speter klausler       return use->symbol().name();
8444864d9f7Speter klausler     }
8454864d9f7Speter klausler   }
8464864d9f7Speter klausler   return symbol.name();
8474864d9f7Speter klausler }
8484864d9f7Speter klausler 
84964ab3302SCarolineConcatto // Collect the symbols of this scope sorted by their original order, not name.
850a3e9d3c2SPeter Klausler // Generics and namelists are exceptions: they are sorted after other symbols.
8511bea0347SPeter Klausler void CollectSymbols(const Scope &scope, SymbolVector &sorted,
8529ce8e63cSPeter Klausler     SymbolVector &uses, UnorderedSymbolSet &modules) {
853a3e9d3c2SPeter Klausler   SymbolVector namelist, generics;
854c353ebbfSTim Keith   auto symbols{scope.GetSymbols()};
8551bea0347SPeter Klausler   std::size_t commonSize{scope.commonBlocks().size()};
856c353ebbfSTim Keith   sorted.reserve(symbols.size() + commonSize);
857c353ebbfSTim Keith   for (SymbolRef symbol : symbols) {
858f7a15e00SPeter Klausler     const auto *generic{symbol->detailsIf<GenericDetails>()};
859f7a15e00SPeter Klausler     if (generic) {
860f7a15e00SPeter Klausler       uses.insert(uses.end(), generic->uses().begin(), generic->uses().end());
861f7a15e00SPeter Klausler       for (auto ref : generic->uses()) {
862f7a15e00SPeter Klausler         modules.insert(GetUsedModule(ref->get<UseDetails>()));
863f7a15e00SPeter Klausler       }
864f7a15e00SPeter Klausler     } else if (const auto *use{symbol->detailsIf<UseDetails>()}) {
865f7a15e00SPeter Klausler       modules.insert(GetUsedModule(*use));
866f7a15e00SPeter Klausler     }
8671bea0347SPeter Klausler     if (symbol->test(Symbol::Flag::ParentComp)) {
8681bea0347SPeter Klausler     } else if (symbol->has<NamelistDetails>()) {
86964ab3302SCarolineConcatto       namelist.push_back(symbol);
870f7a15e00SPeter Klausler     } else if (generic) {
871a3e9d3c2SPeter Klausler       if (generic->specific() &&
872a3e9d3c2SPeter Klausler           &generic->specific()->owner() == &symbol->owner()) {
873a3e9d3c2SPeter Klausler         sorted.push_back(*generic->specific());
874a3e9d3c2SPeter Klausler       } else if (generic->derivedType() &&
875a3e9d3c2SPeter Klausler           &generic->derivedType()->owner() == &symbol->owner()) {
876a3e9d3c2SPeter Klausler         sorted.push_back(*generic->derivedType());
877a3e9d3c2SPeter Klausler       }
878a3e9d3c2SPeter Klausler       generics.push_back(symbol);
87964ab3302SCarolineConcatto     } else {
88064ab3302SCarolineConcatto       sorted.push_back(symbol);
88164ab3302SCarolineConcatto     }
88264ab3302SCarolineConcatto   }
883d55627d2STim Keith   // Sort most symbols by name: use of Symbol::ReplaceName ensures the source
884d55627d2STim Keith   // location of a symbol's name is the first "real" use.
885a3e9d3c2SPeter Klausler   auto sorter{[](SymbolRef x, SymbolRef y) {
886a3e9d3c2SPeter Klausler     return NameInModuleFile(*x).begin() < NameInModuleFile(*y).begin();
887a3e9d3c2SPeter Klausler   }};
888a3e9d3c2SPeter Klausler   std::sort(sorted.begin(), sorted.end(), sorter);
889a3e9d3c2SPeter Klausler   std::sort(generics.begin(), generics.end(), sorter);
890a3e9d3c2SPeter Klausler   sorted.insert(sorted.end(), generics.begin(), generics.end());
891c353ebbfSTim Keith   sorted.insert(sorted.end(), namelist.begin(), namelist.end());
89264ab3302SCarolineConcatto   for (const auto &pair : scope.commonBlocks()) {
893c353ebbfSTim Keith     sorted.push_back(*pair.second);
89464ab3302SCarolineConcatto   }
8950d8331c0Speter klausler   std::sort(
8960d8331c0Speter klausler       sorted.end() - commonSize, sorted.end(), SymbolSourcePositionCompare{});
89764ab3302SCarolineConcatto }
89864ab3302SCarolineConcatto 
899c14cf92bSPeter Klausler void ModFileWriter::PutEntity(llvm::raw_ostream &os, const Symbol &symbol) {
900cd03e96fSPeter Klausler   common::visit(
90164ab3302SCarolineConcatto       common::visitors{
90264ab3302SCarolineConcatto           [&](const ObjectEntityDetails &) { PutObjectEntity(os, symbol); },
90364ab3302SCarolineConcatto           [&](const ProcEntityDetails &) { PutProcEntity(os, symbol); },
90464ab3302SCarolineConcatto           [&](const TypeParamDetails &) { PutTypeParam(os, symbol); },
90564ab3302SCarolineConcatto           [&](const auto &) {
90664ab3302SCarolineConcatto             common::die("PutEntity: unexpected details: %s",
90764ab3302SCarolineConcatto                 DetailsToString(symbol.details()).c_str());
90864ab3302SCarolineConcatto           },
90964ab3302SCarolineConcatto       },
91064ab3302SCarolineConcatto       symbol.details());
91164ab3302SCarolineConcatto }
91264ab3302SCarolineConcatto 
9138670e499SCaroline Concatto void PutShapeSpec(llvm::raw_ostream &os, const ShapeSpec &x) {
91444bc97c8SPeter Klausler   if (x.lbound().isStar()) {
91544bc97c8SPeter Klausler     CHECK(x.ubound().isStar());
91644bc97c8SPeter Klausler     os << ".."; // assumed rank
91764ab3302SCarolineConcatto   } else {
91844bc97c8SPeter Klausler     if (!x.lbound().isColon()) {
91964ab3302SCarolineConcatto       PutBound(os, x.lbound());
92064ab3302SCarolineConcatto     }
92164ab3302SCarolineConcatto     os << ':';
92244bc97c8SPeter Klausler     if (!x.ubound().isColon()) {
92364ab3302SCarolineConcatto       PutBound(os, x.ubound());
92464ab3302SCarolineConcatto     }
92564ab3302SCarolineConcatto   }
92664ab3302SCarolineConcatto }
9278670e499SCaroline Concatto void PutShape(
9288670e499SCaroline Concatto     llvm::raw_ostream &os, const ArraySpec &shape, char open, char close) {
92964ab3302SCarolineConcatto   if (!shape.empty()) {
93064ab3302SCarolineConcatto     os << open;
93164ab3302SCarolineConcatto     bool first{true};
93264ab3302SCarolineConcatto     for (const auto &shapeSpec : shape) {
93364ab3302SCarolineConcatto       if (first) {
93464ab3302SCarolineConcatto         first = false;
93564ab3302SCarolineConcatto       } else {
93664ab3302SCarolineConcatto         os << ',';
93764ab3302SCarolineConcatto       }
93864ab3302SCarolineConcatto       PutShapeSpec(os, shapeSpec);
93964ab3302SCarolineConcatto     }
94064ab3302SCarolineConcatto     os << close;
94164ab3302SCarolineConcatto   }
94264ab3302SCarolineConcatto }
94364ab3302SCarolineConcatto 
944c14cf92bSPeter Klausler void ModFileWriter::PutObjectEntity(
945c14cf92bSPeter Klausler     llvm::raw_ostream &os, const Symbol &symbol) {
94664ab3302SCarolineConcatto   auto &details{symbol.get<ObjectEntityDetails>()};
947c14cf92bSPeter Klausler   if (details.type() &&
948c14cf92bSPeter Klausler       details.type()->category() == DeclTypeSpec::TypeDerived) {
949c14cf92bSPeter Klausler     const Symbol &typeSymbol{details.type()->derivedTypeSpec().typeSymbol()};
950c14cf92bSPeter Klausler     if (typeSymbol.get<DerivedTypeDetails>().isDECStructure()) {
951c14cf92bSPeter Klausler       PutDerivedType(typeSymbol, &symbol.owner());
952c14cf92bSPeter Klausler       if (emittedDECFields_.find(symbol) != emittedDECFields_.end()) {
953c14cf92bSPeter Klausler         return; // symbol was emitted on STRUCTURE statement
954c14cf92bSPeter Klausler       }
955c14cf92bSPeter Klausler     }
956c14cf92bSPeter Klausler   }
9571f879005STim Keith   PutEntity(
9581f879005STim Keith       os, symbol, [&]() { PutType(os, DEREF(symbol.GetType())); },
9591db42fa6SSlava Zakharin       getSymbolAttrsToWrite(symbol));
96064ab3302SCarolineConcatto   PutShape(os, details.shape(), '(', ')');
96164ab3302SCarolineConcatto   PutShape(os, details.coshape(), '[', ']');
9629ce8e63cSPeter Klausler   PutInit(os, symbol, details.init(), details.unanalyzedPDTComponentInit());
96364ab3302SCarolineConcatto   os << '\n';
964864cb2aaSPeter Klausler   if (auto tkr{GetIgnoreTKR(symbol)}; !tkr.empty()) {
965864cb2aaSPeter Klausler     os << "!dir$ ignore_tkr(";
966864cb2aaSPeter Klausler     tkr.IterateOverMembers([&](common::IgnoreTKR tkr) {
967864cb2aaSPeter Klausler       switch (tkr) {
968864cb2aaSPeter Klausler         SWITCH_COVERS_ALL_CASES
969864cb2aaSPeter Klausler       case common::IgnoreTKR::Type:
970864cb2aaSPeter Klausler         os << 't';
971864cb2aaSPeter Klausler         break;
972864cb2aaSPeter Klausler       case common::IgnoreTKR::Kind:
973864cb2aaSPeter Klausler         os << 'k';
974864cb2aaSPeter Klausler         break;
975864cb2aaSPeter Klausler       case common::IgnoreTKR::Rank:
976864cb2aaSPeter Klausler         os << 'r';
977864cb2aaSPeter Klausler         break;
978864cb2aaSPeter Klausler       case common::IgnoreTKR::Device:
979864cb2aaSPeter Klausler         os << 'd';
980864cb2aaSPeter Klausler         break;
981864cb2aaSPeter Klausler       case common::IgnoreTKR::Managed:
982864cb2aaSPeter Klausler         os << 'm';
983864cb2aaSPeter Klausler         break;
984864cb2aaSPeter Klausler       case common::IgnoreTKR::Contiguous:
985864cb2aaSPeter Klausler         os << 'c';
986864cb2aaSPeter Klausler         break;
987864cb2aaSPeter Klausler       }
988864cb2aaSPeter Klausler     });
989864cb2aaSPeter Klausler     os << ") " << symbol.name() << '\n';
990864cb2aaSPeter Klausler   }
99127f71807SPeter Klausler   if (auto attr{details.cudaDataAttr()}) {
99227f71807SPeter Klausler     PutLower(os << "attributes(", common::EnumToString(*attr))
99327f71807SPeter Klausler         << ") " << symbol.name() << '\n';
99427f71807SPeter Klausler   }
995602e5098Skkwli   if (symbol.test(Fortran::semantics::Symbol::Flag::CrayPointer)) {
996602e5098Skkwli     if (!symbol.owner().crayPointers().empty()) {
997602e5098Skkwli       for (const auto &[pointee, pointer] : symbol.owner().crayPointers()) {
998602e5098Skkwli         if (pointer == symbol) {
999602e5098Skkwli           os << "pointer(" << symbol.name() << "," << pointee << ")\n";
1000602e5098Skkwli         }
1001602e5098Skkwli       }
1002602e5098Skkwli     }
1003602e5098Skkwli   }
100464ab3302SCarolineConcatto }
100564ab3302SCarolineConcatto 
1006c14cf92bSPeter Klausler void ModFileWriter::PutProcEntity(llvm::raw_ostream &os, const Symbol &symbol) {
100764ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::INTRINSIC)) {
100864ab3302SCarolineConcatto     os << "intrinsic::" << symbol.name() << '\n';
10094b7428e1SPeter Klausler     if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) {
10108f16101cSpeter klausler       os << "private::" << symbol.name() << '\n';
10118f16101cSpeter klausler     }
101264ab3302SCarolineConcatto     return;
101364ab3302SCarolineConcatto   }
101464ab3302SCarolineConcatto   const auto &details{symbol.get<ProcEntityDetails>()};
101564ab3302SCarolineConcatto   Attrs attrs{symbol.attrs()};
101664ab3302SCarolineConcatto   if (details.passName()) {
101764ab3302SCarolineConcatto     attrs.reset(Attr::PASS);
101864ab3302SCarolineConcatto   }
10191f879005STim Keith   PutEntity(
10201f879005STim Keith       os, symbol,
102164ab3302SCarolineConcatto       [&]() {
102264ab3302SCarolineConcatto         os << "procedure(";
102383ca78deSPeter Klausler         if (details.rawProcInterface()) {
102483ca78deSPeter Klausler           os << details.rawProcInterface()->name();
1025635656f4SPeter Klausler         } else if (details.type()) {
1026635656f4SPeter Klausler           PutType(os, *details.type());
102764ab3302SCarolineConcatto         }
102864ab3302SCarolineConcatto         os << ')';
102964ab3302SCarolineConcatto         PutPassName(os, details.passName());
103064ab3302SCarolineConcatto       },
103164ab3302SCarolineConcatto       attrs);
103264ab3302SCarolineConcatto   os << '\n';
103364ab3302SCarolineConcatto }
103464ab3302SCarolineConcatto 
10358670e499SCaroline Concatto void PutPassName(
10368670e499SCaroline Concatto     llvm::raw_ostream &os, const std::optional<SourceName> &passName) {
103764ab3302SCarolineConcatto   if (passName) {
103864ab3302SCarolineConcatto     os << ",pass(" << *passName << ')';
103964ab3302SCarolineConcatto   }
104064ab3302SCarolineConcatto }
1041c14cf92bSPeter Klausler 
1042c14cf92bSPeter Klausler void ModFileWriter::PutTypeParam(llvm::raw_ostream &os, const Symbol &symbol) {
104364ab3302SCarolineConcatto   auto &details{symbol.get<TypeParamDetails>()};
10441f879005STim Keith   PutEntity(
10451f879005STim Keith       os, symbol,
104664ab3302SCarolineConcatto       [&]() {
104764ab3302SCarolineConcatto         PutType(os, DEREF(symbol.GetType()));
1048539a6b50SPeter Klausler         PutLower(os << ',', common::EnumToString(details.attr().value()));
104964ab3302SCarolineConcatto       },
105064ab3302SCarolineConcatto       symbol.attrs());
105164ab3302SCarolineConcatto   PutInit(os, details.init());
105264ab3302SCarolineConcatto   os << '\n';
105364ab3302SCarolineConcatto }
105464ab3302SCarolineConcatto 
10559e7eef99SPeter Klausler void PutInit(llvm::raw_ostream &os, const Symbol &symbol, const MaybeExpr &init,
10569ce8e63cSPeter Klausler     const parser::Expr *unanalyzed) {
10571bea0347SPeter Klausler   if (IsNamedConstant(symbol) || symbol.owner().IsDerivedType()) {
10589e7eef99SPeter Klausler     const char *assign{symbol.attrs().test(Attr::POINTER) ? "=>" : "="};
10599e7eef99SPeter Klausler     if (unanalyzed) {
10609e7eef99SPeter Klausler       parser::Unparse(os << assign, *unanalyzed);
10619e7eef99SPeter Klausler     } else if (init) {
10629e7eef99SPeter Klausler       init->AsFortran(os << assign);
106364ab3302SCarolineConcatto     }
106464ab3302SCarolineConcatto   }
106564ab3302SCarolineConcatto }
106664ab3302SCarolineConcatto 
10678670e499SCaroline Concatto void PutInit(llvm::raw_ostream &os, const MaybeIntExpr &init) {
106864ab3302SCarolineConcatto   if (init) {
106964ab3302SCarolineConcatto     init->AsFortran(os << '=');
107064ab3302SCarolineConcatto   }
107164ab3302SCarolineConcatto }
107264ab3302SCarolineConcatto 
10738670e499SCaroline Concatto void PutBound(llvm::raw_ostream &os, const Bound &x) {
107444bc97c8SPeter Klausler   if (x.isStar()) {
107564ab3302SCarolineConcatto     os << '*';
107644bc97c8SPeter Klausler   } else if (x.isColon()) {
107764ab3302SCarolineConcatto     os << ':';
107864ab3302SCarolineConcatto   } else {
107964ab3302SCarolineConcatto     x.GetExplicit()->AsFortran(os);
108064ab3302SCarolineConcatto   }
108164ab3302SCarolineConcatto }
108264ab3302SCarolineConcatto 
108364ab3302SCarolineConcatto // Write an entity (object or procedure) declaration.
108464ab3302SCarolineConcatto // writeType is called to write out the type.
1085c14cf92bSPeter Klausler void ModFileWriter::PutEntity(llvm::raw_ostream &os, const Symbol &symbol,
108664ab3302SCarolineConcatto     std::function<void()> writeType, Attrs attrs) {
108764ab3302SCarolineConcatto   writeType();
108869e2665cSPeter Klausler   PutAttrs(os, attrs, symbol.GetBindName(), symbol.GetIsExplicitBindName());
1089c14cf92bSPeter Klausler   if (symbol.owner().kind() == Scope::Kind::DerivedType &&
1090c14cf92bSPeter Klausler       context_.IsTempName(symbol.name().ToString())) {
1091c14cf92bSPeter Klausler     os << "::%FILL";
1092c14cf92bSPeter Klausler   } else {
109364ab3302SCarolineConcatto     os << "::" << symbol.name();
109464ab3302SCarolineConcatto   }
1095c14cf92bSPeter Klausler }
109664ab3302SCarolineConcatto 
109764ab3302SCarolineConcatto // Put out each attribute to os, surrounded by `before` and `after` and
109864ab3302SCarolineConcatto // mapped to lower case.
10994b7428e1SPeter Klausler llvm::raw_ostream &ModFileWriter::PutAttrs(llvm::raw_ostream &os, Attrs attrs,
110069e2665cSPeter Klausler     const std::string *bindName, bool isExplicitBindName, std::string before,
110169e2665cSPeter Klausler     std::string after) const {
110264ab3302SCarolineConcatto   attrs.set(Attr::PUBLIC, false); // no need to write PUBLIC
110364ab3302SCarolineConcatto   attrs.set(Attr::EXTERNAL, false); // no need to write EXTERNAL
11044b7428e1SPeter Klausler   if (isSubmodule_) {
11054b7428e1SPeter Klausler     attrs.set(Attr::PRIVATE, false);
11064b7428e1SPeter Klausler   }
110769e2665cSPeter Klausler   if (bindName || isExplicitBindName) {
110869e2665cSPeter Klausler     os << before << "bind(c";
110969e2665cSPeter Klausler     if (isExplicitBindName) {
111069e2665cSPeter Klausler       os << ",name=\"" << (bindName ? *bindName : ""s) << '"';
111169e2665cSPeter Klausler     }
111269e2665cSPeter Klausler     os << ')' << after;
111364ab3302SCarolineConcatto     attrs.set(Attr::BIND_C, false);
111464ab3302SCarolineConcatto   }
111564ab3302SCarolineConcatto   for (std::size_t i{0}; i < Attr_enumSize; ++i) {
111664ab3302SCarolineConcatto     Attr attr{static_cast<Attr>(i)};
111764ab3302SCarolineConcatto     if (attrs.test(attr)) {
111864ab3302SCarolineConcatto       PutAttr(os << before, attr) << after;
111964ab3302SCarolineConcatto     }
112064ab3302SCarolineConcatto   }
112164ab3302SCarolineConcatto   return os;
112264ab3302SCarolineConcatto }
112364ab3302SCarolineConcatto 
11248670e499SCaroline Concatto llvm::raw_ostream &PutAttr(llvm::raw_ostream &os, Attr attr) {
112564ab3302SCarolineConcatto   return PutLower(os, AttrToString(attr));
112664ab3302SCarolineConcatto }
112764ab3302SCarolineConcatto 
11288670e499SCaroline Concatto llvm::raw_ostream &PutType(llvm::raw_ostream &os, const DeclTypeSpec &type) {
112964ab3302SCarolineConcatto   return PutLower(os, type.AsFortran());
113064ab3302SCarolineConcatto }
113164ab3302SCarolineConcatto 
1132bcba39a5SPeter Klausler llvm::raw_ostream &PutLower(llvm::raw_ostream &os, std::string_view str) {
113364ab3302SCarolineConcatto   for (char c : str) {
113464ab3302SCarolineConcatto     os << parser::ToLowerCaseLetter(c);
113564ab3302SCarolineConcatto   }
113664ab3302SCarolineConcatto   return os;
113764ab3302SCarolineConcatto }
113864ab3302SCarolineConcatto 
113968f36106SValentin Clement void PutOpenACCDirective(llvm::raw_ostream &os, const Symbol &symbol) {
114022f63b5cSValentin Clement   if (symbol.test(Symbol::Flag::AccDeclare)) {
114122f63b5cSValentin Clement     os << "!$acc declare ";
114222f63b5cSValentin Clement     if (symbol.test(Symbol::Flag::AccCopy)) {
114322f63b5cSValentin Clement       os << "copy";
1144a749b32aSValentin Clement     } else if (symbol.test(Symbol::Flag::AccCopyIn) ||
1145a749b32aSValentin Clement         symbol.test(Symbol::Flag::AccCopyInReadOnly)) {
114622f63b5cSValentin Clement       os << "copyin";
114722f63b5cSValentin Clement     } else if (symbol.test(Symbol::Flag::AccCopyOut)) {
114822f63b5cSValentin Clement       os << "copyout";
114922f63b5cSValentin Clement     } else if (symbol.test(Symbol::Flag::AccCreate)) {
115022f63b5cSValentin Clement       os << "create";
115122f63b5cSValentin Clement     } else if (symbol.test(Symbol::Flag::AccPresent)) {
115222f63b5cSValentin Clement       os << "present";
115322f63b5cSValentin Clement     } else if (symbol.test(Symbol::Flag::AccDevicePtr)) {
115422f63b5cSValentin Clement       os << "deviceptr";
115522f63b5cSValentin Clement     } else if (symbol.test(Symbol::Flag::AccDeviceResident)) {
115622f63b5cSValentin Clement       os << "device_resident";
115722f63b5cSValentin Clement     } else if (symbol.test(Symbol::Flag::AccLink)) {
115822f63b5cSValentin Clement       os << "link";
115922f63b5cSValentin Clement     }
1160a749b32aSValentin Clement     os << "(";
1161a749b32aSValentin Clement     if (symbol.test(Symbol::Flag::AccCopyInReadOnly)) {
1162a749b32aSValentin Clement       os << "readonly: ";
1163a749b32aSValentin Clement     }
1164a749b32aSValentin Clement     os << symbol.name() << ")\n";
116522f63b5cSValentin Clement   }
116622f63b5cSValentin Clement }
116722f63b5cSValentin Clement 
116868f36106SValentin Clement void PutOpenMPDirective(llvm::raw_ostream &os, const Symbol &symbol) {
116968f36106SValentin Clement   if (symbol.test(Symbol::Flag::OmpThreadprivate)) {
117068f36106SValentin Clement     os << "!$omp threadprivate(" << symbol.name() << ")\n";
117168f36106SValentin Clement   }
117268f36106SValentin Clement }
117368f36106SValentin Clement 
117468f36106SValentin Clement void ModFileWriter::PutDirective(llvm::raw_ostream &os, const Symbol &symbol) {
117568f36106SValentin Clement   PutOpenACCDirective(os, symbol);
117668f36106SValentin Clement   PutOpenMPDirective(os, symbol);
117768f36106SValentin Clement }
117868f36106SValentin Clement 
117964ab3302SCarolineConcatto struct Temp {
118061106305SSteve Scalpone   Temp(int fd, std::string path) : fd{fd}, path{path} {}
11810855c454SDavid Truby   Temp(Temp &&t) : fd{std::exchange(t.fd, -1)}, path{std::move(t.path)} {}
118264ab3302SCarolineConcatto   ~Temp() {
11830855c454SDavid Truby     if (fd >= 0) {
118461106305SSteve Scalpone       llvm::sys::fs::file_t native{llvm::sys::fs::convertFDToNativeFile(fd)};
118561106305SSteve Scalpone       llvm::sys::fs::closeFile(native);
11860855c454SDavid Truby       llvm::sys::fs::remove(path.c_str());
118764ab3302SCarolineConcatto     }
11880855c454SDavid Truby   }
118961106305SSteve Scalpone   int fd;
119064ab3302SCarolineConcatto   std::string path;
119164ab3302SCarolineConcatto };
119264ab3302SCarolineConcatto 
119364ab3302SCarolineConcatto // Create a temp file in the same directory and with the same suffix as path.
119464ab3302SCarolineConcatto // Return an open file descriptor and its path.
11950855c454SDavid Truby static llvm::ErrorOr<Temp> MkTemp(const std::string &path) {
119664ab3302SCarolineConcatto   auto length{path.length()};
119764ab3302SCarolineConcatto   auto dot{path.find_last_of("./")};
11980855c454SDavid Truby   std::string suffix{
11990855c454SDavid Truby       dot < length && path[dot] == '.' ? path.substr(dot + 1) : ""};
120064ab3302SCarolineConcatto   CHECK(length > suffix.length() &&
120164ab3302SCarolineConcatto       path.substr(length - suffix.length()) == suffix);
12020855c454SDavid Truby   auto prefix{path.substr(0, length - suffix.length())};
120361106305SSteve Scalpone   int fd;
12040855c454SDavid Truby   llvm::SmallString<16> tempPath;
12050855c454SDavid Truby   if (std::error_code err{llvm::sys::fs::createUniqueFile(
12060855c454SDavid Truby           prefix + "%%%%%%" + suffix, fd, tempPath)}) {
12070855c454SDavid Truby     return err;
12080855c454SDavid Truby   }
12090855c454SDavid Truby   return Temp{fd, tempPath.c_str()};
121064ab3302SCarolineConcatto }
121164ab3302SCarolineConcatto 
121264ab3302SCarolineConcatto // Write the module file at path, prepending header. If an error occurs,
121364ab3302SCarolineConcatto // return errno, otherwise 0.
1214f7a15e00SPeter Klausler static std::error_code WriteFile(const std::string &path,
1215f7a15e00SPeter Klausler     const std::string &contents, ModuleCheckSumType &checkSum, bool debug) {
1216f7a15e00SPeter Klausler   checkSum = ComputeCheckSum(contents);
121764ab3302SCarolineConcatto   auto header{std::string{ModHeader::bom} + ModHeader::magic +
1218f7a15e00SPeter Klausler       CheckSumString(checkSum) + ModHeader::terminator};
12190855c454SDavid Truby   if (debug) {
12200855c454SDavid Truby     llvm::dbgs() << "Processing module " << path << ": ";
12210855c454SDavid Truby   }
122264ab3302SCarolineConcatto   if (FileContentsMatch(path, header, contents)) {
12230855c454SDavid Truby     if (debug) {
12240855c454SDavid Truby       llvm::dbgs() << "module unchanged, not writing\n";
122564ab3302SCarolineConcatto     }
12260855c454SDavid Truby     return {};
122764ab3302SCarolineConcatto   }
12280855c454SDavid Truby   llvm::ErrorOr<Temp> temp{MkTemp(path)};
12290855c454SDavid Truby   if (!temp) {
12300855c454SDavid Truby     return temp.getError();
123164ab3302SCarolineConcatto   }
12320855c454SDavid Truby   llvm::raw_fd_ostream writer(temp->fd, /*shouldClose=*/false);
12330855c454SDavid Truby   writer << header;
12340855c454SDavid Truby   writer << contents;
12350855c454SDavid Truby   writer.flush();
12360855c454SDavid Truby   if (writer.has_error()) {
12370855c454SDavid Truby     return writer.error();
123864ab3302SCarolineConcatto   }
12390855c454SDavid Truby   if (debug) {
12400855c454SDavid Truby     llvm::dbgs() << "module written\n";
12410855c454SDavid Truby   }
12420855c454SDavid Truby   return llvm::sys::fs::rename(temp->path, path);
124364ab3302SCarolineConcatto }
124464ab3302SCarolineConcatto 
124564ab3302SCarolineConcatto // Return true if the stream matches what we would write for the mod file.
124664ab3302SCarolineConcatto static bool FileContentsMatch(const std::string &path,
124764ab3302SCarolineConcatto     const std::string &header, const std::string &contents) {
124864ab3302SCarolineConcatto   std::size_t hsize{header.size()};
124964ab3302SCarolineConcatto   std::size_t csize{contents.size()};
12500855c454SDavid Truby   auto buf_or{llvm::MemoryBuffer::getFile(path)};
12510855c454SDavid Truby   if (!buf_or) {
125264ab3302SCarolineConcatto     return false;
125364ab3302SCarolineConcatto   }
12540855c454SDavid Truby   auto buf = std::move(buf_or.get());
12550855c454SDavid Truby   if (buf->getBufferSize() != hsize + csize) {
125664ab3302SCarolineConcatto     return false;
125764ab3302SCarolineConcatto   }
12580855c454SDavid Truby   if (!std::equal(header.begin(), header.end(), buf->getBufferStart(),
12590855c454SDavid Truby           buf->getBufferStart() + hsize)) {
126064ab3302SCarolineConcatto     return false;
126164ab3302SCarolineConcatto   }
12620855c454SDavid Truby 
12630855c454SDavid Truby   return std::equal(contents.begin(), contents.end(),
12640855c454SDavid Truby       buf->getBufferStart() + hsize, buf->getBufferEnd());
126564ab3302SCarolineConcatto }
126664ab3302SCarolineConcatto 
126764ab3302SCarolineConcatto // Compute a simple hash of the contents of a module file and
126864ab3302SCarolineConcatto // return it as a string of hex digits.
126964ab3302SCarolineConcatto // This uses the Fowler-Noll-Vo hash function.
1270f7a15e00SPeter Klausler static ModuleCheckSumType ComputeCheckSum(const std::string_view &contents) {
1271f7a15e00SPeter Klausler   ModuleCheckSumType hash{0xcbf29ce484222325ull};
127264ab3302SCarolineConcatto   for (char c : contents) {
127364ab3302SCarolineConcatto     hash ^= c & 0xff;
127464ab3302SCarolineConcatto     hash *= 0x100000001b3;
127564ab3302SCarolineConcatto   }
1276f7a15e00SPeter Klausler   return hash;
1277f7a15e00SPeter Klausler }
1278f7a15e00SPeter Klausler 
1279f7a15e00SPeter Klausler static std::string CheckSumString(ModuleCheckSumType hash) {
128064ab3302SCarolineConcatto   static const char *digits = "0123456789abcdef";
128164ab3302SCarolineConcatto   std::string result(ModHeader::sumLen, '0');
128264ab3302SCarolineConcatto   for (size_t i{ModHeader::sumLen}; hash != 0; hash >>= 4) {
128364ab3302SCarolineConcatto     result[--i] = digits[hash & 0xf];
128464ab3302SCarolineConcatto   }
128564ab3302SCarolineConcatto   return result;
128664ab3302SCarolineConcatto }
128764ab3302SCarolineConcatto 
1288f7a15e00SPeter Klausler std::optional<ModuleCheckSumType> ExtractCheckSum(const std::string_view &str) {
1289f7a15e00SPeter Klausler   if (str.size() == ModHeader::sumLen) {
1290f7a15e00SPeter Klausler     ModuleCheckSumType hash{0};
1291f7a15e00SPeter Klausler     for (size_t j{0}; j < ModHeader::sumLen; ++j) {
1292f7a15e00SPeter Klausler       hash <<= 4;
1293f7a15e00SPeter Klausler       char ch{str.at(j)};
1294f7a15e00SPeter Klausler       if (ch >= '0' && ch <= '9') {
1295f7a15e00SPeter Klausler         hash += ch - '0';
1296f7a15e00SPeter Klausler       } else if (ch >= 'a' && ch <= 'f') {
1297f7a15e00SPeter Klausler         hash += ch - 'a' + 10;
1298f7a15e00SPeter Klausler       } else {
1299f7a15e00SPeter Klausler         return std::nullopt;
130064ab3302SCarolineConcatto       }
1301f7a15e00SPeter Klausler     }
1302f7a15e00SPeter Klausler     return hash;
1303f7a15e00SPeter Klausler   }
1304f7a15e00SPeter Klausler   return std::nullopt;
130564ab3302SCarolineConcatto }
130664ab3302SCarolineConcatto 
1307f7a15e00SPeter Klausler static std::optional<ModuleCheckSumType> VerifyHeader(
1308f7a15e00SPeter Klausler     llvm::ArrayRef<char> content) {
1309f7a15e00SPeter Klausler   std::string_view sv{content.data(), content.size()};
1310f7a15e00SPeter Klausler   if (sv.substr(0, ModHeader::magicLen) != ModHeader::magic) {
1311f7a15e00SPeter Klausler     return std::nullopt;
1312f7a15e00SPeter Klausler   }
1313f7a15e00SPeter Klausler   ModuleCheckSumType checkSum{ComputeCheckSum(sv.substr(ModHeader::len))};
1314f7a15e00SPeter Klausler   std::string_view expectSum{sv.substr(ModHeader::magicLen, ModHeader::sumLen)};
1315f7a15e00SPeter Klausler   if (auto extracted{ExtractCheckSum(expectSum)};
1316f7a15e00SPeter Klausler       extracted && *extracted == checkSum) {
1317f7a15e00SPeter Klausler     return checkSum;
1318f7a15e00SPeter Klausler   } else {
1319f7a15e00SPeter Klausler     return std::nullopt;
1320f7a15e00SPeter Klausler   }
1321f7a15e00SPeter Klausler }
1322f7a15e00SPeter Klausler 
1323f7a15e00SPeter Klausler static void GetModuleDependences(
1324f7a15e00SPeter Klausler     ModuleDependences &dependences, llvm::ArrayRef<char> content) {
1325f7a15e00SPeter Klausler   std::size_t limit{content.size()};
1326f7a15e00SPeter Klausler   std::string_view str{content.data(), limit};
1327f7a15e00SPeter Klausler   for (std::size_t j{ModHeader::len};
13285661188cSPeter Klausler        str.substr(j, ModHeader::needLen) == ModHeader::need; ++j) {
1329f7a15e00SPeter Klausler     j += 7;
1330f7a15e00SPeter Klausler     auto checkSum{ExtractCheckSum(str.substr(j, ModHeader::sumLen))};
1331f7a15e00SPeter Klausler     if (!checkSum) {
1332f7a15e00SPeter Klausler       break;
1333f7a15e00SPeter Klausler     }
1334f7a15e00SPeter Klausler     j += ModHeader::sumLen;
1335f7a15e00SPeter Klausler     bool intrinsic{false};
1336f7a15e00SPeter Klausler     if (str.substr(j, 3) == " i ") {
1337f7a15e00SPeter Klausler       intrinsic = true;
1338f7a15e00SPeter Klausler     } else if (str.substr(j, 3) != " n ") {
1339f7a15e00SPeter Klausler       break;
1340f7a15e00SPeter Klausler     }
1341f7a15e00SPeter Klausler     j += 3;
1342f7a15e00SPeter Klausler     std::size_t start{j};
1343f7a15e00SPeter Klausler     for (; j < limit && str.at(j) != '\n'; ++j) {
1344f7a15e00SPeter Klausler     }
1345f7a15e00SPeter Klausler     if (j > start && j < limit && str.at(j) == '\n') {
13465661188cSPeter Klausler       std::string depModName{str.substr(start, j - start)};
13475661188cSPeter Klausler       dependences.AddDependence(std::move(depModName), intrinsic, *checkSum);
1348f7a15e00SPeter Klausler     } else {
1349f7a15e00SPeter Klausler       break;
1350f7a15e00SPeter Klausler     }
1351f7a15e00SPeter Klausler   }
1352f7a15e00SPeter Klausler }
1353f7a15e00SPeter Klausler 
1354f7a15e00SPeter Klausler Scope *ModFileReader::Read(SourceName name, std::optional<bool> isIntrinsic,
1355f7a15e00SPeter Klausler     Scope *ancestor, bool silent) {
135664ab3302SCarolineConcatto   std::string ancestorName; // empty for module
13575661188cSPeter Klausler   const Symbol *notAModule{nullptr};
13589f33dd73SPeter Klausler   bool fatalError{false};
135964ab3302SCarolineConcatto   if (ancestor) {
136064ab3302SCarolineConcatto     if (auto *scope{ancestor->FindSubmodule(name)}) {
136164ab3302SCarolineConcatto       return scope;
136264ab3302SCarolineConcatto     }
136364ab3302SCarolineConcatto     ancestorName = ancestor->GetName().value().ToString();
1364dcbfabbeSPeter Klausler   }
1365f7a15e00SPeter Klausler   auto requiredHash{context_.moduleDependences().GetRequiredHash(
1366f7a15e00SPeter Klausler       name.ToString(), isIntrinsic.value_or(false))};
1367dcbfabbeSPeter Klausler   if (!isIntrinsic.value_or(false) && !ancestor) {
1368dcbfabbeSPeter Klausler     // Already present in the symbol table as a usable non-intrinsic module?
1369038b42baSPeter Klausler     if (Scope * hermeticScope{context_.currentHermeticModuleFileScope()}) {
1370038b42baSPeter Klausler       auto it{hermeticScope->find(name)};
1371038b42baSPeter Klausler       if (it != hermeticScope->end()) {
1372038b42baSPeter Klausler         return it->second->scope();
1373038b42baSPeter Klausler       }
1374038b42baSPeter Klausler     }
137564ab3302SCarolineConcatto     auto it{context_.globalScope().find(name)};
137664ab3302SCarolineConcatto     if (it != context_.globalScope().end()) {
13779f33dd73SPeter Klausler       Scope *scope{it->second->scope()};
13789f33dd73SPeter Klausler       if (scope->kind() == Scope::Kind::Module) {
13795661188cSPeter Klausler         for (const Symbol *found{scope->symbol()}; found;) {
13805661188cSPeter Klausler           if (const auto *module{found->detailsIf<ModuleDetails>()}) {
13815661188cSPeter Klausler             if (!requiredHash ||
13825661188cSPeter Klausler                 *requiredHash ==
13835661188cSPeter Klausler                     module->moduleFileHash().value_or(*requiredHash)) {
13845661188cSPeter Klausler               return const_cast<Scope *>(found->scope());
13855661188cSPeter Klausler             }
13865661188cSPeter Klausler             found = module->previous(); // same name, distinct hash
13875661188cSPeter Klausler           } else {
13885661188cSPeter Klausler             notAModule = found;
13895661188cSPeter Klausler             break;
1390f7a15e00SPeter Klausler           }
1391f7a15e00SPeter Klausler         }
13929f33dd73SPeter Klausler       } else {
13939f33dd73SPeter Klausler         notAModule = scope->symbol();
13945661188cSPeter Klausler       }
13955661188cSPeter Klausler     }
13965661188cSPeter Klausler   }
13975661188cSPeter Klausler   if (notAModule) {
13989f33dd73SPeter Klausler     // USE, NON_INTRINSIC global name isn't a module?
13999f33dd73SPeter Klausler     fatalError = isIntrinsic.has_value();
14009f33dd73SPeter Klausler   }
14019629f2c4SPeter Klausler   std::string path{
14029629f2c4SPeter Klausler       ModFileName(name, ancestorName, context_.moduleFileSuffix())};
140392a54197Speter klausler   parser::Parsing parsing{context_.allCookedSources()};
140464ab3302SCarolineConcatto   parser::Options options;
140564ab3302SCarolineConcatto   options.isModuleFile = true;
140664ab3302SCarolineConcatto   options.features.Enable(common::LanguageFeature::BackslashEscapes);
14072cbd4fc4SPeixin-Qiao   options.features.Enable(common::LanguageFeature::OpenMP);
140827f71807SPeter Klausler   options.features.Enable(common::LanguageFeature::CUDA);
14099f33dd73SPeter Klausler   if (!isIntrinsic.value_or(false) && !notAModule) {
1410dcbfabbeSPeter Klausler     // The search for this module file will scan non-intrinsic module
1411dcbfabbeSPeter Klausler     // directories.  If a directory is in both the intrinsic and non-intrinsic
1412dcbfabbeSPeter Klausler     // directory lists, the intrinsic module directory takes precedence.
141364ab3302SCarolineConcatto     options.searchDirectories = context_.searchDirectories();
141452a1346bSPeter Klausler     for (const auto &dir : context_.intrinsicModuleDirectories()) {
1415f95bdff1SShao-Ce SUN       options.searchDirectories.erase(
141652a1346bSPeter Klausler           std::remove(options.searchDirectories.begin(),
1417f95bdff1SShao-Ce SUN               options.searchDirectories.end(), dir),
1418f95bdff1SShao-Ce SUN           options.searchDirectories.end());
141952a1346bSPeter Klausler     }
142015faac90SPeter Klausler     options.searchDirectories.insert(options.searchDirectories.begin(), "."s);
142152a1346bSPeter Klausler   }
1422dcbfabbeSPeter Klausler   bool foundNonIntrinsicModuleFile{false};
1423dcbfabbeSPeter Klausler   if (!isIntrinsic) {
1424dcbfabbeSPeter Klausler     std::list<std::string> searchDirs;
1425dcbfabbeSPeter Klausler     for (const auto &d : options.searchDirectories) {
1426dcbfabbeSPeter Klausler       searchDirs.push_back(d);
1427dcbfabbeSPeter Klausler     }
1428dcbfabbeSPeter Klausler     foundNonIntrinsicModuleFile =
1429dcbfabbeSPeter Klausler         parser::LocateSourceFile(path, searchDirs).has_value();
1430dcbfabbeSPeter Klausler   }
1431dcbfabbeSPeter Klausler   if (isIntrinsic.value_or(!foundNonIntrinsicModuleFile)) {
1432dcbfabbeSPeter Klausler     // Explicitly intrinsic, or not specified and not found in the search
1433dcbfabbeSPeter Klausler     // path; see whether it's already in the symbol table as an intrinsic
1434dcbfabbeSPeter Klausler     // module.
1435dcbfabbeSPeter Klausler     auto it{context_.intrinsicModulesScope().find(name)};
1436dcbfabbeSPeter Klausler     if (it != context_.intrinsicModulesScope().end()) {
1437dcbfabbeSPeter Klausler       return it->second->scope();
1438dcbfabbeSPeter Klausler     }
1439dcbfabbeSPeter Klausler   }
1440dcbfabbeSPeter Klausler   // We don't have this module in the symbol table yet.
1441dcbfabbeSPeter Klausler   // Find its module file and parse it.  Define or extend the search
1442dcbfabbeSPeter Klausler   // path with intrinsic module directories, if appropriate.
144352a1346bSPeter Klausler   if (isIntrinsic.value_or(true)) {
144452a1346bSPeter Klausler     for (const auto &dir : context_.intrinsicModuleDirectories()) {
144552a1346bSPeter Klausler       options.searchDirectories.push_back(dir);
144652a1346bSPeter Klausler     }
1447f7a15e00SPeter Klausler     if (!requiredHash) {
1448f7a15e00SPeter Klausler       requiredHash =
1449f7a15e00SPeter Klausler           context_.moduleDependences().GetRequiredHash(name.ToString(), true);
145052a1346bSPeter Klausler     }
1451f7a15e00SPeter Klausler   }
1452f7a15e00SPeter Klausler 
1453f7a15e00SPeter Klausler   // Look for the right module file if its hash is known
1454f7a15e00SPeter Klausler   if (requiredHash && !fatalError) {
1455f7a15e00SPeter Klausler     for (const std::string &maybe :
1456f7a15e00SPeter Klausler         parser::LocateSourceFileAll(path, options.searchDirectories)) {
1457f7a15e00SPeter Klausler       if (const auto *srcFile{context_.allCookedSources().allSources().OpenPath(
1458f7a15e00SPeter Klausler               maybe, llvm::errs())}) {
14595661188cSPeter Klausler         if (auto checkSum{VerifyHeader(srcFile->content())};
14605661188cSPeter Klausler             checkSum && *checkSum == *requiredHash) {
1461f7a15e00SPeter Klausler           path = maybe;
1462f7a15e00SPeter Klausler           break;
1463f7a15e00SPeter Klausler         }
1464f7a15e00SPeter Klausler       }
1465f7a15e00SPeter Klausler     }
1466f7a15e00SPeter Klausler   }
14679f33dd73SPeter Klausler   const auto *sourceFile{fatalError ? nullptr : parsing.Prescan(path, options)};
14689f33dd73SPeter Klausler   if (fatalError || parsing.messages().AnyFatalError()) {
146952711fb8Speter klausler     if (!silent) {
14709f33dd73SPeter Klausler       if (notAModule) {
14719f33dd73SPeter Klausler         // Module is not explicitly INTRINSIC, and there's already a global
14729f33dd73SPeter Klausler         // symbol of the same name that is not a module.
14739f33dd73SPeter Klausler         context_.SayWithDecl(
14749f33dd73SPeter Klausler             *notAModule, name, "'%s' is not a module"_err_en_US, name);
14759f33dd73SPeter Klausler       } else {
147664ab3302SCarolineConcatto         for (auto &msg : parsing.messages().messages()) {
147764ab3302SCarolineConcatto           std::string str{msg.ToString()};
14789629f2c4SPeter Klausler           Say("parse", name, ancestorName,
14792895771fSPeter Klausler               parser::MessageFixedText{str.c_str(), str.size(), msg.severity()},
14802895771fSPeter Klausler               path);
148152711fb8Speter klausler         }
148264ab3302SCarolineConcatto       }
14839f33dd73SPeter Klausler     }
148464ab3302SCarolineConcatto     return nullptr;
148564ab3302SCarolineConcatto   }
148664ab3302SCarolineConcatto   CHECK(sourceFile);
1487f7a15e00SPeter Klausler   std::optional<ModuleCheckSumType> checkSum{
1488f7a15e00SPeter Klausler       VerifyHeader(sourceFile->content())};
1489f7a15e00SPeter Klausler   if (!checkSum) {
14909629f2c4SPeter Klausler     Say("use", name, ancestorName, "File has invalid checksum: %s"_err_en_US,
14910e907c17SIñaki Amatria Barral         sourceFile->path());
149264ab3302SCarolineConcatto     return nullptr;
1493f7a15e00SPeter Klausler   } else if (requiredHash && *requiredHash != *checkSum) {
14949629f2c4SPeter Klausler     Say("use", name, ancestorName,
14950e907c17SIñaki Amatria Barral         "File is not the right module file for %s"_err_en_US,
14960e907c17SIñaki Amatria Barral         "'"s + name.ToString() + "': "s + sourceFile->path());
1497f7a15e00SPeter Klausler     return nullptr;
149864ab3302SCarolineConcatto   }
14998670e499SCaroline Concatto   llvm::raw_null_ostream NullStream;
15008670e499SCaroline Concatto   parsing.Parse(NullStream);
1501f4bb211aSPeter Klausler   std::optional<parser::Program> &parsedProgram{parsing.parseTree()};
150264ab3302SCarolineConcatto   if (!parsing.messages().empty() || !parsing.consumedWholeFile() ||
1503f4bb211aSPeter Klausler       !parsedProgram) {
15049629f2c4SPeter Klausler     Say("parse", name, ancestorName, "Module file is corrupt: %s"_err_en_US,
150564ab3302SCarolineConcatto         sourceFile->path());
150664ab3302SCarolineConcatto     return nullptr;
150764ab3302SCarolineConcatto   }
1508f4bb211aSPeter Klausler   parser::Program &parseTree{context_.SaveParseTree(std::move(*parsedProgram))};
150964ab3302SCarolineConcatto   Scope *parentScope; // the scope this module/submodule goes into
151052a1346bSPeter Klausler   if (!isIntrinsic.has_value()) {
151152a1346bSPeter Klausler     for (const auto &dir : context_.intrinsicModuleDirectories()) {
151252a1346bSPeter Klausler       if (sourceFile->path().size() > dir.size() &&
151352a1346bSPeter Klausler           sourceFile->path().find(dir) == 0) {
151452a1346bSPeter Klausler         isIntrinsic = true;
151552a1346bSPeter Klausler         break;
151652a1346bSPeter Klausler       }
151752a1346bSPeter Klausler     }
151852a1346bSPeter Klausler   }
151952a1346bSPeter Klausler   Scope &topScope{isIntrinsic.value_or(false) ? context_.intrinsicModulesScope()
152052a1346bSPeter Klausler                                               : context_.globalScope()};
1521e200b0e4SPeter Klausler   Symbol *moduleSymbol{nullptr};
15225661188cSPeter Klausler   const Symbol *previousModuleSymbol{nullptr};
1523e200b0e4SPeter Klausler   if (!ancestor) { // module, not submodule
152452a1346bSPeter Klausler     parentScope = &topScope;
15254864d9f7Speter klausler     auto pair{parentScope->try_emplace(name, UnknownDetails{})};
15264864d9f7Speter klausler     if (!pair.second) {
15275661188cSPeter Klausler       // There is already a global symbol or intrinsic module of the same name.
15285661188cSPeter Klausler       previousModuleSymbol = &*pair.first->second;
15295661188cSPeter Klausler       if (const auto *details{
15305661188cSPeter Klausler               previousModuleSymbol->detailsIf<ModuleDetails>()}) {
15315661188cSPeter Klausler         if (!details->moduleFileHash().has_value()) {
153264ab3302SCarolineConcatto           return nullptr;
153364ab3302SCarolineConcatto         }
15345661188cSPeter Klausler       } else {
15355661188cSPeter Klausler         return nullptr;
15365661188cSPeter Klausler       }
15375661188cSPeter Klausler       CHECK(parentScope->erase(name) != 0);
15385661188cSPeter Klausler       pair = parentScope->try_emplace(name, UnknownDetails{});
15395661188cSPeter Klausler       CHECK(pair.second);
15405661188cSPeter Klausler     }
1541e200b0e4SPeter Klausler     moduleSymbol = &*pair.first->second;
1542e200b0e4SPeter Klausler     moduleSymbol->set(Symbol::Flag::ModFile);
1543e200b0e4SPeter Klausler   } else if (std::optional<SourceName> parent{GetSubmoduleParent(parseTree)}) {
1544e200b0e4SPeter Klausler     // submodule with submodule parent
1545e200b0e4SPeter Klausler     parentScope = Read(*parent, false /*not intrinsic*/, ancestor, silent);
1546e200b0e4SPeter Klausler   } else {
1547e200b0e4SPeter Klausler     // submodule with module parent
1548e200b0e4SPeter Klausler     parentScope = ancestor;
1549e200b0e4SPeter Klausler   }
155073506256SPeter Klausler   // Process declarations from the module file
1551e1ad2735SPeter Klausler   auto wasModuleFileName{context_.foldingContext().moduleFileName()};
1552e1ad2735SPeter Klausler   context_.foldingContext().set_moduleFileName(name);
1553038b42baSPeter Klausler   // Are there multiple modules in the module file due to it having been
1554038b42baSPeter Klausler   // created under -fhermetic-module-files?  If so, process them first in
1555038b42baSPeter Klausler   // their own nested scope that will be visible only to USE statements
1556038b42baSPeter Klausler   // within the module file.
1557038b42baSPeter Klausler   if (parseTree.v.size() > 1) {
1558038b42baSPeter Klausler     parser::Program hermeticModules{std::move(parseTree.v)};
1559038b42baSPeter Klausler     parseTree.v.emplace_back(std::move(hermeticModules.v.front()));
1560038b42baSPeter Klausler     hermeticModules.v.pop_front();
1561038b42baSPeter Klausler     Scope &hermeticScope{topScope.MakeScope(Scope::Kind::Global)};
1562038b42baSPeter Klausler     context_.set_currentHermeticModuleFileScope(&hermeticScope);
1563038b42baSPeter Klausler     ResolveNames(context_, hermeticModules, hermeticScope);
1564038b42baSPeter Klausler   }
1565f7a15e00SPeter Klausler   GetModuleDependences(context_.moduleDependences(), sourceFile->content());
1566f4bb211aSPeter Klausler   ResolveNames(context_, parseTree, topScope);
1567e1ad2735SPeter Klausler   context_.foldingContext().set_moduleFileName(wasModuleFileName);
1568038b42baSPeter Klausler   context_.set_currentHermeticModuleFileScope(nullptr);
1569e200b0e4SPeter Klausler   if (!moduleSymbol) {
1570e200b0e4SPeter Klausler     // Submodule symbols' storage are owned by their parents' scopes,
1571e200b0e4SPeter Klausler     // but their names are not in their parents' dictionaries -- we
1572e200b0e4SPeter Klausler     // don't want to report bogus errors about clashes between submodule
1573e200b0e4SPeter Klausler     // names and other objects in the parent scopes.
1574e200b0e4SPeter Klausler     if (Scope * submoduleScope{ancestor->FindSubmodule(name)}) {
1575e200b0e4SPeter Klausler       moduleSymbol = submoduleScope->symbol();
1576e200b0e4SPeter Klausler       if (moduleSymbol) {
1577e200b0e4SPeter Klausler         moduleSymbol->set(Symbol::Flag::ModFile);
157852a1346bSPeter Klausler       }
1579e200b0e4SPeter Klausler     }
1580e200b0e4SPeter Klausler   }
1581e200b0e4SPeter Klausler   if (moduleSymbol) {
1582e200b0e4SPeter Klausler     CHECK(moduleSymbol->test(Symbol::Flag::ModFile));
15835661188cSPeter Klausler     auto &details{moduleSymbol->get<ModuleDetails>()};
15845661188cSPeter Klausler     details.set_moduleFileHash(checkSum.value());
15855661188cSPeter Klausler     details.set_previous(previousModuleSymbol);
1586e200b0e4SPeter Klausler     if (isIntrinsic.value_or(false)) {
1587e200b0e4SPeter Klausler       moduleSymbol->attrs().set(Attr::INTRINSIC);
1588e200b0e4SPeter Klausler     }
1589e200b0e4SPeter Klausler     return moduleSymbol->scope();
1590e200b0e4SPeter Klausler   } else {
1591e200b0e4SPeter Klausler     return nullptr;
1592e200b0e4SPeter Klausler   }
159364ab3302SCarolineConcatto }
159464ab3302SCarolineConcatto 
15959629f2c4SPeter Klausler parser::Message &ModFileReader::Say(const char *verb, SourceName name,
159664ab3302SCarolineConcatto     const std::string &ancestor, parser::MessageFixedText &&msg,
159764ab3302SCarolineConcatto     const std::string &arg) {
15989629f2c4SPeter Klausler   return context_.Say(name, "Cannot %s module file for %s: %s"_err_en_US, verb,
15990bfa4ac6Speter klausler       parser::MessageFormattedText{ancestor.empty()
16000bfa4ac6Speter klausler               ? "module '%s'"_en_US
16010bfa4ac6Speter klausler               : "submodule '%s' of module '%s'"_en_US,
16020bfa4ac6Speter klausler           name, ancestor}
16030bfa4ac6Speter klausler           .MoveString(),
16040bfa4ac6Speter klausler       parser::MessageFormattedText{std::move(msg), arg}.MoveString());
160564ab3302SCarolineConcatto }
160664ab3302SCarolineConcatto 
160764ab3302SCarolineConcatto // program was read from a .mod file for a submodule; return the name of the
160864ab3302SCarolineConcatto // submodule's parent submodule, nullptr if none.
160964ab3302SCarolineConcatto static std::optional<SourceName> GetSubmoduleParent(
161064ab3302SCarolineConcatto     const parser::Program &program) {
161164ab3302SCarolineConcatto   CHECK(program.v.size() == 1);
161264ab3302SCarolineConcatto   auto &unit{program.v.front()};
161364ab3302SCarolineConcatto   auto &submod{std::get<common::Indirection<parser::Submodule>>(unit.u)};
161464ab3302SCarolineConcatto   auto &stmt{
161564ab3302SCarolineConcatto       std::get<parser::Statement<parser::SubmoduleStmt>>(submod.value().t)};
161664ab3302SCarolineConcatto   auto &parentId{std::get<parser::ParentIdentifier>(stmt.statement.t)};
161764ab3302SCarolineConcatto   if (auto &parent{std::get<std::optional<parser::Name>>(parentId.t)}) {
161864ab3302SCarolineConcatto     return parent->source;
161964ab3302SCarolineConcatto   } else {
162064ab3302SCarolineConcatto     return std::nullopt;
162164ab3302SCarolineConcatto   }
162264ab3302SCarolineConcatto }
162364ab3302SCarolineConcatto 
162464ab3302SCarolineConcatto void SubprogramSymbolCollector::Collect() {
162564ab3302SCarolineConcatto   const auto &details{symbol_.get<SubprogramDetails>()};
162664ab3302SCarolineConcatto   isInterface_ = details.isInterface();
162764ab3302SCarolineConcatto   for (const Symbol *dummyArg : details.dummyArgs()) {
16283ed2909fSPete Steinfeld     if (dummyArg) {
16293ed2909fSPete Steinfeld       DoSymbol(*dummyArg);
16303ed2909fSPete Steinfeld     }
163164ab3302SCarolineConcatto   }
163264ab3302SCarolineConcatto   if (details.isFunction()) {
163364ab3302SCarolineConcatto     DoSymbol(details.result());
163464ab3302SCarolineConcatto   }
163564ab3302SCarolineConcatto   for (const auto &pair : scope_) {
163664ab3302SCarolineConcatto     const Symbol &symbol{*pair.second};
163764ab3302SCarolineConcatto     if (const auto *useDetails{symbol.detailsIf<UseDetails>()}) {
1638665d4159SPeter Klausler       const Symbol &ultimate{useDetails->symbol().GetUltimate()};
1639665d4159SPeter Klausler       bool needed{useSet_.count(ultimate) > 0};
1640665d4159SPeter Klausler       if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
1641665d4159SPeter Klausler         // The generic may not be needed itself, but the specific procedure
1642665d4159SPeter Klausler         // &/or derived type that it shadows may be needed.
1643665d4159SPeter Klausler         const Symbol *spec{generic->specific()};
1644665d4159SPeter Klausler         const Symbol *dt{generic->derivedType()};
16458be575e4SPeter Klausler         needed = needed || (spec && useSet_.count(spec->GetUltimate()) > 0) ||
16468be575e4SPeter Klausler             (dt && useSet_.count(dt->GetUltimate()) > 0);
1647b67984d3SPeter Klausler       } else if (const auto *subp{ultimate.detailsIf<SubprogramDetails>()}) {
1648b67984d3SPeter Klausler         const Symbol *interface { subp->moduleInterface() };
1649b67984d3SPeter Klausler         needed = needed || (interface && useSet_.count(*interface) > 0);
1650665d4159SPeter Klausler       }
1651665d4159SPeter Klausler       if (needed) {
165264ab3302SCarolineConcatto         need_.push_back(symbol);
165364ab3302SCarolineConcatto       }
1654b85922cdSEmil Kieri     } else if (symbol.has<SubprogramDetails>()) {
1655b85922cdSEmil Kieri       // An internal subprogram is needed if it is used as interface
1656b85922cdSEmil Kieri       // for a dummy or return value procedure.
1657b85922cdSEmil Kieri       bool needed{false};
1658b85922cdSEmil Kieri       const auto hasInterface{[&symbol](const Symbol *s) -> bool {
1659b85922cdSEmil Kieri         // Is 's' a procedure with interface 'symbol'?
1660b85922cdSEmil Kieri         if (s) {
1661b85922cdSEmil Kieri           if (const auto *sDetails{s->detailsIf<ProcEntityDetails>()}) {
1662635656f4SPeter Klausler             if (sDetails->procInterface() == &symbol) {
1663b85922cdSEmil Kieri               return true;
1664b85922cdSEmil Kieri             }
1665b85922cdSEmil Kieri           }
1666b85922cdSEmil Kieri         }
1667b85922cdSEmil Kieri         return false;
1668b85922cdSEmil Kieri       }};
1669b85922cdSEmil Kieri       for (const Symbol *dummyArg : details.dummyArgs()) {
1670b85922cdSEmil Kieri         needed = needed || hasInterface(dummyArg);
1671b85922cdSEmil Kieri       }
1672b85922cdSEmil Kieri       needed =
1673b85922cdSEmil Kieri           needed || (details.isFunction() && hasInterface(&details.result()));
1674b85922cdSEmil Kieri       if (needed && needSet_.insert(symbol).second) {
1675b85922cdSEmil Kieri         need_.push_back(symbol);
1676b85922cdSEmil Kieri       }
167764ab3302SCarolineConcatto     }
167864ab3302SCarolineConcatto   }
167964ab3302SCarolineConcatto }
168064ab3302SCarolineConcatto 
168164ab3302SCarolineConcatto void SubprogramSymbolCollector::DoSymbol(const Symbol &symbol) {
168264ab3302SCarolineConcatto   DoSymbol(symbol.name(), symbol);
168364ab3302SCarolineConcatto }
168464ab3302SCarolineConcatto 
168564ab3302SCarolineConcatto // Do symbols this one depends on; then add to need_
168664ab3302SCarolineConcatto void SubprogramSymbolCollector::DoSymbol(
168764ab3302SCarolineConcatto     const SourceName &name, const Symbol &symbol) {
168864ab3302SCarolineConcatto   const auto &scope{symbol.owner()};
168964ab3302SCarolineConcatto   if (scope != scope_ && !scope.IsDerivedType()) {
169064ab3302SCarolineConcatto     if (scope != scope_.parent()) {
169164ab3302SCarolineConcatto       useSet_.insert(symbol);
169264ab3302SCarolineConcatto     }
169364ab3302SCarolineConcatto     if (NeedImport(name, symbol)) {
169464ab3302SCarolineConcatto       imports_.insert(name);
169564ab3302SCarolineConcatto     }
169664ab3302SCarolineConcatto     return;
169764ab3302SCarolineConcatto   }
169864ab3302SCarolineConcatto   if (!needSet_.insert(symbol).second) {
169964ab3302SCarolineConcatto     return; // already done
170064ab3302SCarolineConcatto   }
1701cd03e96fSPeter Klausler   common::visit(common::visitors{
170264ab3302SCarolineConcatto                     [this](const ObjectEntityDetails &details) {
170364ab3302SCarolineConcatto                       for (const ShapeSpec &spec : details.shape()) {
170464ab3302SCarolineConcatto                         DoBound(spec.lbound());
170564ab3302SCarolineConcatto                         DoBound(spec.ubound());
170664ab3302SCarolineConcatto                       }
170764ab3302SCarolineConcatto                       for (const ShapeSpec &spec : details.coshape()) {
170864ab3302SCarolineConcatto                         DoBound(spec.lbound());
170964ab3302SCarolineConcatto                         DoBound(spec.ubound());
171064ab3302SCarolineConcatto                       }
171164ab3302SCarolineConcatto                       if (const Symbol * commonBlock{details.commonBlock()}) {
171264ab3302SCarolineConcatto                         DoSymbol(*commonBlock);
171364ab3302SCarolineConcatto                       }
171464ab3302SCarolineConcatto                     },
171564ab3302SCarolineConcatto                     [this](const CommonBlockDetails &details) {
1716d5c05cedSTim Keith                       for (const auto &object : details.objects()) {
1717d5c05cedSTim Keith                         DoSymbol(*object);
171864ab3302SCarolineConcatto                       }
171964ab3302SCarolineConcatto                     },
1720df3e5f18SPeter Klausler                     [this](const ProcEntityDetails &details) {
172183ca78deSPeter Klausler                       if (details.rawProcInterface()) {
172283ca78deSPeter Klausler                         DoSymbol(*details.rawProcInterface());
1723635656f4SPeter Klausler                       } else {
1724635656f4SPeter Klausler                         DoType(details.type());
1725df3e5f18SPeter Klausler                       }
1726df3e5f18SPeter Klausler                     },
1727d0708e62SPeter Klausler                     [this](const ProcBindingDetails &details) {
1728d0708e62SPeter Klausler                       DoSymbol(details.symbol());
1729d0708e62SPeter Klausler                     },
173064ab3302SCarolineConcatto                     [](const auto &) {},
173164ab3302SCarolineConcatto                 },
173264ab3302SCarolineConcatto       symbol.details());
173364ab3302SCarolineConcatto   if (!symbol.has<UseDetails>()) {
173464ab3302SCarolineConcatto     DoType(symbol.GetType());
173564ab3302SCarolineConcatto   }
173664ab3302SCarolineConcatto   if (!scope.IsDerivedType()) {
173764ab3302SCarolineConcatto     need_.push_back(symbol);
173864ab3302SCarolineConcatto   }
173964ab3302SCarolineConcatto }
174064ab3302SCarolineConcatto 
174164ab3302SCarolineConcatto void SubprogramSymbolCollector::DoType(const DeclTypeSpec *type) {
174264ab3302SCarolineConcatto   if (!type) {
174364ab3302SCarolineConcatto     return;
174464ab3302SCarolineConcatto   }
174564ab3302SCarolineConcatto   switch (type->category()) {
174664ab3302SCarolineConcatto   case DeclTypeSpec::Numeric:
17471f879005STim Keith   case DeclTypeSpec::Logical:
17481f879005STim Keith     break; // nothing to do
174964ab3302SCarolineConcatto   case DeclTypeSpec::Character:
175064ab3302SCarolineConcatto     DoParamValue(type->characterTypeSpec().length());
175164ab3302SCarolineConcatto     break;
175264ab3302SCarolineConcatto   default:
175364ab3302SCarolineConcatto     if (const DerivedTypeSpec * derived{type->AsDerived()}) {
175464ab3302SCarolineConcatto       const auto &typeSymbol{derived->typeSymbol()};
175564ab3302SCarolineConcatto       for (const auto &pair : derived->parameters()) {
175664ab3302SCarolineConcatto         DoParamValue(pair.second);
175764ab3302SCarolineConcatto       }
1758d0708e62SPeter Klausler       // The components of the type (including its parent component, if
1759d0708e62SPeter Klausler       // any) matter to IMPORT symbol collection only for derived types
1760d0708e62SPeter Klausler       // defined in the subprogram.
1761d0708e62SPeter Klausler       if (typeSymbol.owner() == scope_) {
1762d0708e62SPeter Klausler         if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
1763d0708e62SPeter Klausler           DoSymbol(extends->name(), extends->typeSymbol());
176464ab3302SCarolineConcatto         }
1765d0708e62SPeter Klausler         for (const auto &pair : *typeSymbol.scope()) {
1766d0708e62SPeter Klausler           DoSymbol(*pair.second);
1767d0708e62SPeter Klausler         }
1768d0708e62SPeter Klausler       }
1769d0708e62SPeter Klausler       DoSymbol(derived->name(), typeSymbol);
177064ab3302SCarolineConcatto     }
177164ab3302SCarolineConcatto   }
177264ab3302SCarolineConcatto }
177364ab3302SCarolineConcatto 
177464ab3302SCarolineConcatto void SubprogramSymbolCollector::DoBound(const Bound &bound) {
177564ab3302SCarolineConcatto   if (const MaybeSubscriptIntExpr & expr{bound.GetExplicit()}) {
177664ab3302SCarolineConcatto     DoExpr(*expr);
177764ab3302SCarolineConcatto   }
177864ab3302SCarolineConcatto }
177964ab3302SCarolineConcatto void SubprogramSymbolCollector::DoParamValue(const ParamValue &paramValue) {
178064ab3302SCarolineConcatto   if (const auto &expr{paramValue.GetExplicit()}) {
178164ab3302SCarolineConcatto     DoExpr(*expr);
178264ab3302SCarolineConcatto   }
178364ab3302SCarolineConcatto }
178464ab3302SCarolineConcatto 
178564ab3302SCarolineConcatto // Do we need a IMPORT of this symbol into an interface block?
178664ab3302SCarolineConcatto bool SubprogramSymbolCollector::NeedImport(
178764ab3302SCarolineConcatto     const SourceName &name, const Symbol &symbol) {
178864ab3302SCarolineConcatto   if (!isInterface_) {
178964ab3302SCarolineConcatto     return false;
17909e855a6cSPeter Klausler   } else if (IsSeparateModuleProcedureInterface(&symbol_)) {
17919e855a6cSPeter Klausler     return false; // IMPORT needed only for external and dummy procedure
17929e855a6cSPeter Klausler                   // interfaces
1793df3e5f18SPeter Klausler   } else if (&symbol == scope_.symbol()) {
1794df3e5f18SPeter Klausler     return false;
17954864d9f7Speter klausler   } else if (symbol.owner().Contains(scope_)) {
179664ab3302SCarolineConcatto     return true;
17974864d9f7Speter klausler   } else if (const Symbol *found{scope_.FindSymbol(name)}) {
17984864d9f7Speter klausler     // detect import from ancestor of use-associated symbol
17994864d9f7Speter klausler     return found->has<UseDetails>() && found->owner() != scope_;
18004864d9f7Speter klausler   } else {
1801d0708e62SPeter Klausler     // "found" can be null in the case of a use-associated derived type's
1802*f5ddb101SPeter Klausler     // parent type, and also in the case of an object (like a dummy argument)
1803*f5ddb101SPeter Klausler     // used to define a length or bound of a nested interface.
18044864d9f7Speter klausler     return false;
180564ab3302SCarolineConcatto   }
180664ab3302SCarolineConcatto }
180764ab3302SCarolineConcatto 
18081f879005STim Keith } // namespace Fortran::semantics
1809