1*64ab3302SCarolineConcatto //===-- lib/Semantics/mod-file.cpp ----------------------------------------===//
2*64ab3302SCarolineConcatto //
3*64ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*64ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
5*64ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*64ab3302SCarolineConcatto //
7*64ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
8*64ab3302SCarolineConcatto 
9*64ab3302SCarolineConcatto #include "mod-file.h"
10*64ab3302SCarolineConcatto #include "resolve-names.h"
11*64ab3302SCarolineConcatto #include "flang/Evaluate/tools.h"
12*64ab3302SCarolineConcatto #include "flang/Parser/message.h"
13*64ab3302SCarolineConcatto #include "flang/Parser/parsing.h"
14*64ab3302SCarolineConcatto #include "flang/Semantics/scope.h"
15*64ab3302SCarolineConcatto #include "flang/Semantics/semantics.h"
16*64ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
17*64ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
18*64ab3302SCarolineConcatto #include <algorithm>
19*64ab3302SCarolineConcatto #include <cerrno>
20*64ab3302SCarolineConcatto #include <fstream>
21*64ab3302SCarolineConcatto #include <ostream>
22*64ab3302SCarolineConcatto #include <set>
23*64ab3302SCarolineConcatto #include <string_view>
24*64ab3302SCarolineConcatto #include <sys/file.h>
25*64ab3302SCarolineConcatto #include <sys/stat.h>
26*64ab3302SCarolineConcatto #include <sys/types.h>
27*64ab3302SCarolineConcatto #include <unistd.h>
28*64ab3302SCarolineConcatto #include <vector>
29*64ab3302SCarolineConcatto 
30*64ab3302SCarolineConcatto namespace Fortran::semantics {
31*64ab3302SCarolineConcatto 
32*64ab3302SCarolineConcatto using namespace parser::literals;
33*64ab3302SCarolineConcatto 
34*64ab3302SCarolineConcatto // The first line of a file that identifies it as a .mod file.
35*64ab3302SCarolineConcatto // The first three bytes are a Unicode byte order mark that ensures
36*64ab3302SCarolineConcatto // that the module file is decoded as UTF-8 even if source files
37*64ab3302SCarolineConcatto // are using another encoding.
38*64ab3302SCarolineConcatto struct ModHeader {
39*64ab3302SCarolineConcatto   static constexpr const char bom[3 + 1]{"\xef\xbb\xbf"};
40*64ab3302SCarolineConcatto   static constexpr int magicLen{13};
41*64ab3302SCarolineConcatto   static constexpr int sumLen{16};
42*64ab3302SCarolineConcatto   static constexpr const char magic[magicLen + 1]{"!mod$ v1 sum:"};
43*64ab3302SCarolineConcatto   static constexpr char terminator{'\n'};
44*64ab3302SCarolineConcatto   static constexpr int len{magicLen + 1 + sumLen};
45*64ab3302SCarolineConcatto };
46*64ab3302SCarolineConcatto 
47*64ab3302SCarolineConcatto static std::optional<SourceName> GetSubmoduleParent(const parser::Program &);
48*64ab3302SCarolineConcatto static SymbolVector CollectSymbols(const Scope &);
49*64ab3302SCarolineConcatto static void PutEntity(std::ostream &, const Symbol &);
50*64ab3302SCarolineConcatto static void PutObjectEntity(std::ostream &, const Symbol &);
51*64ab3302SCarolineConcatto static void PutProcEntity(std::ostream &, const Symbol &);
52*64ab3302SCarolineConcatto static void PutPassName(std::ostream &, const std::optional<SourceName> &);
53*64ab3302SCarolineConcatto static void PutTypeParam(std::ostream &, const Symbol &);
54*64ab3302SCarolineConcatto static void PutEntity(
55*64ab3302SCarolineConcatto     std::ostream &, const Symbol &, std::function<void()>, Attrs);
56*64ab3302SCarolineConcatto static void PutInit(std::ostream &, const Symbol &, const MaybeExpr &);
57*64ab3302SCarolineConcatto static void PutInit(std::ostream &, const MaybeIntExpr &);
58*64ab3302SCarolineConcatto static void PutBound(std::ostream &, const Bound &);
59*64ab3302SCarolineConcatto static std::ostream &PutAttrs(std::ostream &, Attrs,
60*64ab3302SCarolineConcatto     const MaybeExpr & = std::nullopt, std::string before = ","s,
61*64ab3302SCarolineConcatto     std::string after = ""s);
62*64ab3302SCarolineConcatto static std::ostream &PutAttr(std::ostream &, Attr);
63*64ab3302SCarolineConcatto static std::ostream &PutType(std::ostream &, const DeclTypeSpec &);
64*64ab3302SCarolineConcatto static std::ostream &PutLower(std::ostream &, const std::string &);
65*64ab3302SCarolineConcatto static int WriteFile(const std::string &, const std::string &);
66*64ab3302SCarolineConcatto static bool FileContentsMatch(
67*64ab3302SCarolineConcatto     const std::string &, const std::string &, const std::string &);
68*64ab3302SCarolineConcatto static std::size_t GetFileSize(const std::string &);
69*64ab3302SCarolineConcatto static std::string CheckSum(const std::string_view &);
70*64ab3302SCarolineConcatto 
71*64ab3302SCarolineConcatto // Collect symbols needed for a subprogram interface
72*64ab3302SCarolineConcatto class SubprogramSymbolCollector {
73*64ab3302SCarolineConcatto public:
74*64ab3302SCarolineConcatto   SubprogramSymbolCollector(const Symbol &symbol)
75*64ab3302SCarolineConcatto     : symbol_{symbol}, scope_{DEREF(symbol.scope())} {}
76*64ab3302SCarolineConcatto   const SymbolVector &symbols() const { return need_; }
77*64ab3302SCarolineConcatto   const std::set<SourceName> &imports() const { return imports_; }
78*64ab3302SCarolineConcatto   void Collect();
79*64ab3302SCarolineConcatto 
80*64ab3302SCarolineConcatto private:
81*64ab3302SCarolineConcatto   const Symbol &symbol_;
82*64ab3302SCarolineConcatto   const Scope &scope_;
83*64ab3302SCarolineConcatto   bool isInterface_{false};
84*64ab3302SCarolineConcatto   SymbolVector need_;  // symbols that are needed
85*64ab3302SCarolineConcatto   SymbolSet needSet_;  // symbols already in need_
86*64ab3302SCarolineConcatto   SymbolSet useSet_;  // use-associations that might be needed
87*64ab3302SCarolineConcatto   std::set<SourceName> imports_;  // imports from host that are needed
88*64ab3302SCarolineConcatto 
89*64ab3302SCarolineConcatto   void DoSymbol(const Symbol &);
90*64ab3302SCarolineConcatto   void DoSymbol(const SourceName &, const Symbol &);
91*64ab3302SCarolineConcatto   void DoType(const DeclTypeSpec *);
92*64ab3302SCarolineConcatto   void DoBound(const Bound &);
93*64ab3302SCarolineConcatto   void DoParamValue(const ParamValue &);
94*64ab3302SCarolineConcatto   bool NeedImport(const SourceName &, const Symbol &);
95*64ab3302SCarolineConcatto 
96*64ab3302SCarolineConcatto   template<typename T> void DoExpr(evaluate::Expr<T> expr) {
97*64ab3302SCarolineConcatto     for (const Symbol &symbol : evaluate::CollectSymbols(expr)) {
98*64ab3302SCarolineConcatto       DoSymbol(symbol);
99*64ab3302SCarolineConcatto     }
100*64ab3302SCarolineConcatto   }
101*64ab3302SCarolineConcatto };
102*64ab3302SCarolineConcatto 
103*64ab3302SCarolineConcatto bool ModFileWriter::WriteAll() {
104*64ab3302SCarolineConcatto   WriteAll(context_.globalScope());
105*64ab3302SCarolineConcatto   return !context_.AnyFatalError();
106*64ab3302SCarolineConcatto }
107*64ab3302SCarolineConcatto 
108*64ab3302SCarolineConcatto void ModFileWriter::WriteAll(const Scope &scope) {
109*64ab3302SCarolineConcatto   for (const auto &child : scope.children()) {
110*64ab3302SCarolineConcatto     WriteOne(child);
111*64ab3302SCarolineConcatto   }
112*64ab3302SCarolineConcatto }
113*64ab3302SCarolineConcatto 
114*64ab3302SCarolineConcatto void ModFileWriter::WriteOne(const Scope &scope) {
115*64ab3302SCarolineConcatto   if (scope.kind() == Scope::Kind::Module) {
116*64ab3302SCarolineConcatto     auto *symbol{scope.symbol()};
117*64ab3302SCarolineConcatto     if (!symbol->test(Symbol::Flag::ModFile)) {
118*64ab3302SCarolineConcatto       Write(*symbol);
119*64ab3302SCarolineConcatto     }
120*64ab3302SCarolineConcatto     WriteAll(scope);  // write out submodules
121*64ab3302SCarolineConcatto   }
122*64ab3302SCarolineConcatto }
123*64ab3302SCarolineConcatto 
124*64ab3302SCarolineConcatto // Construct the name of a module file. Non-empty ancestorName means submodule.
125*64ab3302SCarolineConcatto static std::string ModFileName(const SourceName &name,
126*64ab3302SCarolineConcatto     const std::string &ancestorName, const std::string &suffix) {
127*64ab3302SCarolineConcatto   std::string result{name.ToString() + suffix};
128*64ab3302SCarolineConcatto   return ancestorName.empty() ? result : ancestorName + '-' + result;
129*64ab3302SCarolineConcatto }
130*64ab3302SCarolineConcatto 
131*64ab3302SCarolineConcatto // Write the module file for symbol, which must be a module or submodule.
132*64ab3302SCarolineConcatto void ModFileWriter::Write(const Symbol &symbol) {
133*64ab3302SCarolineConcatto   auto *ancestor{symbol.get<ModuleDetails>().ancestor()};
134*64ab3302SCarolineConcatto   auto ancestorName{ancestor ? ancestor->GetName().value().ToString() : ""s};
135*64ab3302SCarolineConcatto   auto path{context_.moduleDirectory() + '/' +
136*64ab3302SCarolineConcatto       ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())};
137*64ab3302SCarolineConcatto   PutSymbols(DEREF(symbol.scope()));
138*64ab3302SCarolineConcatto   if (int error{WriteFile(path, GetAsString(symbol))}) {
139*64ab3302SCarolineConcatto     context_.Say(symbol.name(), "Error writing %s: %s"_err_en_US, path,
140*64ab3302SCarolineConcatto         std::strerror(error));
141*64ab3302SCarolineConcatto   }
142*64ab3302SCarolineConcatto }
143*64ab3302SCarolineConcatto 
144*64ab3302SCarolineConcatto // Return the entire body of the module file
145*64ab3302SCarolineConcatto // and clear saved uses, decls, and contains.
146*64ab3302SCarolineConcatto std::string ModFileWriter::GetAsString(const Symbol &symbol) {
147*64ab3302SCarolineConcatto   std::stringstream all;
148*64ab3302SCarolineConcatto   auto &details{symbol.get<ModuleDetails>()};
149*64ab3302SCarolineConcatto   if (!details.isSubmodule()) {
150*64ab3302SCarolineConcatto     all << "module " << symbol.name();
151*64ab3302SCarolineConcatto   } else {
152*64ab3302SCarolineConcatto     auto *parent{details.parent()->symbol()};
153*64ab3302SCarolineConcatto     auto *ancestor{details.ancestor()->symbol()};
154*64ab3302SCarolineConcatto     all << "submodule(" << ancestor->name();
155*64ab3302SCarolineConcatto     if (parent != ancestor) {
156*64ab3302SCarolineConcatto       all << ':' << parent->name();
157*64ab3302SCarolineConcatto     }
158*64ab3302SCarolineConcatto     all << ") " << symbol.name();
159*64ab3302SCarolineConcatto   }
160*64ab3302SCarolineConcatto   all << '\n' << uses_.str();
161*64ab3302SCarolineConcatto   uses_.str(""s);
162*64ab3302SCarolineConcatto   all << useExtraAttrs_.str();
163*64ab3302SCarolineConcatto   useExtraAttrs_.str(""s);
164*64ab3302SCarolineConcatto   all << decls_.str();
165*64ab3302SCarolineConcatto   decls_.str(""s);
166*64ab3302SCarolineConcatto   auto str{contains_.str()};
167*64ab3302SCarolineConcatto   contains_.str(""s);
168*64ab3302SCarolineConcatto   if (!str.empty()) {
169*64ab3302SCarolineConcatto     all << "contains\n" << str;
170*64ab3302SCarolineConcatto   }
171*64ab3302SCarolineConcatto   all << "end\n";
172*64ab3302SCarolineConcatto   return all.str();
173*64ab3302SCarolineConcatto }
174*64ab3302SCarolineConcatto 
175*64ab3302SCarolineConcatto // Put out the visible symbols from scope.
176*64ab3302SCarolineConcatto void ModFileWriter::PutSymbols(const Scope &scope) {
177*64ab3302SCarolineConcatto   std::stringstream typeBindings;  // stuff after CONTAINS in derived type
178*64ab3302SCarolineConcatto   for (const Symbol &symbol : CollectSymbols(scope)) {
179*64ab3302SCarolineConcatto     PutSymbol(typeBindings, symbol);
180*64ab3302SCarolineConcatto   }
181*64ab3302SCarolineConcatto   if (auto str{typeBindings.str()}; !str.empty()) {
182*64ab3302SCarolineConcatto     CHECK(scope.IsDerivedType());
183*64ab3302SCarolineConcatto     decls_ << "contains\n" << str;
184*64ab3302SCarolineConcatto   }
185*64ab3302SCarolineConcatto }
186*64ab3302SCarolineConcatto 
187*64ab3302SCarolineConcatto // Emit a symbol to decls_, except for bindings in a derived type (type-bound
188*64ab3302SCarolineConcatto // procedures, type-bound generics, final procedures) which go to typeBindings.
189*64ab3302SCarolineConcatto void ModFileWriter::PutSymbol(
190*64ab3302SCarolineConcatto     std::stringstream &typeBindings, const Symbol &symbol) {
191*64ab3302SCarolineConcatto   std::visit(
192*64ab3302SCarolineConcatto       common::visitors{
193*64ab3302SCarolineConcatto           [&](const ModuleDetails &) { /* should be current module */ },
194*64ab3302SCarolineConcatto           [&](const DerivedTypeDetails &) { PutDerivedType(symbol); },
195*64ab3302SCarolineConcatto           [&](const SubprogramDetails &) { PutSubprogram(symbol); },
196*64ab3302SCarolineConcatto           [&](const GenericDetails &x) {
197*64ab3302SCarolineConcatto             if (symbol.owner().IsDerivedType()) {
198*64ab3302SCarolineConcatto               // generic binding
199*64ab3302SCarolineConcatto               for (const Symbol &proc : x.specificProcs()) {
200*64ab3302SCarolineConcatto                 typeBindings << "generic::" << symbol.name() << "=>"
201*64ab3302SCarolineConcatto                              << proc.name() << '\n';
202*64ab3302SCarolineConcatto               }
203*64ab3302SCarolineConcatto             } else {
204*64ab3302SCarolineConcatto               PutGeneric(symbol);
205*64ab3302SCarolineConcatto               if (x.specific()) {
206*64ab3302SCarolineConcatto                 PutSymbol(typeBindings, *x.specific());
207*64ab3302SCarolineConcatto               }
208*64ab3302SCarolineConcatto               if (x.derivedType()) {
209*64ab3302SCarolineConcatto                 PutSymbol(typeBindings, *x.derivedType());
210*64ab3302SCarolineConcatto               }
211*64ab3302SCarolineConcatto             }
212*64ab3302SCarolineConcatto           },
213*64ab3302SCarolineConcatto           [&](const UseDetails &) { PutUse(symbol); },
214*64ab3302SCarolineConcatto           [](const UseErrorDetails &) {},
215*64ab3302SCarolineConcatto           [&](const ProcBindingDetails &x) {
216*64ab3302SCarolineConcatto             bool deferred{symbol.attrs().test(Attr::DEFERRED)};
217*64ab3302SCarolineConcatto             typeBindings << "procedure";
218*64ab3302SCarolineConcatto             if (deferred) {
219*64ab3302SCarolineConcatto               typeBindings << '(' << x.symbol().name() << ')';
220*64ab3302SCarolineConcatto             }
221*64ab3302SCarolineConcatto             PutPassName(typeBindings, x.passName());
222*64ab3302SCarolineConcatto             auto attrs{symbol.attrs()};
223*64ab3302SCarolineConcatto             if (x.passName()) {
224*64ab3302SCarolineConcatto               attrs.reset(Attr::PASS);
225*64ab3302SCarolineConcatto             }
226*64ab3302SCarolineConcatto             PutAttrs(typeBindings, attrs);
227*64ab3302SCarolineConcatto             typeBindings << "::" << symbol.name();
228*64ab3302SCarolineConcatto             if (!deferred && x.symbol().name() != symbol.name()) {
229*64ab3302SCarolineConcatto               typeBindings << "=>" << x.symbol().name();
230*64ab3302SCarolineConcatto             }
231*64ab3302SCarolineConcatto             typeBindings << '\n';
232*64ab3302SCarolineConcatto           },
233*64ab3302SCarolineConcatto           [&](const NamelistDetails &x) {
234*64ab3302SCarolineConcatto             decls_ << "namelist/" << symbol.name();
235*64ab3302SCarolineConcatto             char sep{'/'};
236*64ab3302SCarolineConcatto             for (const Symbol &object : x.objects()) {
237*64ab3302SCarolineConcatto               decls_ << sep << object.name();
238*64ab3302SCarolineConcatto               sep = ',';
239*64ab3302SCarolineConcatto             }
240*64ab3302SCarolineConcatto             decls_ << '\n';
241*64ab3302SCarolineConcatto           },
242*64ab3302SCarolineConcatto           [&](const CommonBlockDetails &x) {
243*64ab3302SCarolineConcatto             decls_ << "common/" << symbol.name();
244*64ab3302SCarolineConcatto             char sep = '/';
245*64ab3302SCarolineConcatto             for (const Symbol &object : x.objects()) {
246*64ab3302SCarolineConcatto               decls_ << sep << object.name();
247*64ab3302SCarolineConcatto               sep = ',';
248*64ab3302SCarolineConcatto             }
249*64ab3302SCarolineConcatto             decls_ << '\n';
250*64ab3302SCarolineConcatto             if (symbol.attrs().test(Attr::BIND_C)) {
251*64ab3302SCarolineConcatto               PutAttrs(decls_, symbol.attrs(), x.bindName(), ""s);
252*64ab3302SCarolineConcatto               decls_ << "::/" << symbol.name() << "/\n";
253*64ab3302SCarolineConcatto             }
254*64ab3302SCarolineConcatto           },
255*64ab3302SCarolineConcatto           [&](const FinalProcDetails &) {
256*64ab3302SCarolineConcatto             typeBindings << "final::" << symbol.name() << '\n';
257*64ab3302SCarolineConcatto           },
258*64ab3302SCarolineConcatto           [](const HostAssocDetails &) {},
259*64ab3302SCarolineConcatto           [](const MiscDetails &) {},
260*64ab3302SCarolineConcatto           [&](const auto &) { PutEntity(decls_, symbol); },
261*64ab3302SCarolineConcatto       },
262*64ab3302SCarolineConcatto       symbol.details());
263*64ab3302SCarolineConcatto }
264*64ab3302SCarolineConcatto 
265*64ab3302SCarolineConcatto void ModFileWriter::PutDerivedType(const Symbol &typeSymbol) {
266*64ab3302SCarolineConcatto   auto &details{typeSymbol.get<DerivedTypeDetails>()};
267*64ab3302SCarolineConcatto   PutAttrs(decls_ << "type", typeSymbol.attrs());
268*64ab3302SCarolineConcatto   if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
269*64ab3302SCarolineConcatto     decls_ << ",extends(" << extends->name() << ')';
270*64ab3302SCarolineConcatto   }
271*64ab3302SCarolineConcatto   decls_ << "::" << typeSymbol.name();
272*64ab3302SCarolineConcatto   auto &typeScope{*typeSymbol.scope()};
273*64ab3302SCarolineConcatto   if (!details.paramNames().empty()) {
274*64ab3302SCarolineConcatto     char sep{'('};
275*64ab3302SCarolineConcatto     for (const auto &name : details.paramNames()) {
276*64ab3302SCarolineConcatto       decls_ << sep << name;
277*64ab3302SCarolineConcatto       sep = ',';
278*64ab3302SCarolineConcatto     }
279*64ab3302SCarolineConcatto     decls_ << ')';
280*64ab3302SCarolineConcatto   }
281*64ab3302SCarolineConcatto   decls_ << '\n';
282*64ab3302SCarolineConcatto   if (details.sequence()) {
283*64ab3302SCarolineConcatto     decls_ << "sequence\n";
284*64ab3302SCarolineConcatto   }
285*64ab3302SCarolineConcatto   PutSymbols(typeScope);
286*64ab3302SCarolineConcatto   decls_ << "end type\n";
287*64ab3302SCarolineConcatto }
288*64ab3302SCarolineConcatto 
289*64ab3302SCarolineConcatto // Attributes that may be in a subprogram prefix
290*64ab3302SCarolineConcatto static const Attrs subprogramPrefixAttrs{Attr::ELEMENTAL, Attr::IMPURE,
291*64ab3302SCarolineConcatto     Attr::MODULE, Attr::NON_RECURSIVE, Attr::PURE, Attr::RECURSIVE};
292*64ab3302SCarolineConcatto 
293*64ab3302SCarolineConcatto void ModFileWriter::PutSubprogram(const Symbol &symbol) {
294*64ab3302SCarolineConcatto   auto attrs{symbol.attrs()};
295*64ab3302SCarolineConcatto   auto &details{symbol.get<SubprogramDetails>()};
296*64ab3302SCarolineConcatto   Attrs bindAttrs{};
297*64ab3302SCarolineConcatto   if (attrs.test(Attr::BIND_C)) {
298*64ab3302SCarolineConcatto     // bind(c) is a suffix, not prefix
299*64ab3302SCarolineConcatto     bindAttrs.set(Attr::BIND_C, true);
300*64ab3302SCarolineConcatto     attrs.set(Attr::BIND_C, false);
301*64ab3302SCarolineConcatto   }
302*64ab3302SCarolineConcatto   Attrs prefixAttrs{subprogramPrefixAttrs & attrs};
303*64ab3302SCarolineConcatto   // emit any non-prefix attributes in an attribute statement
304*64ab3302SCarolineConcatto   attrs &= ~subprogramPrefixAttrs;
305*64ab3302SCarolineConcatto   std::stringstream ss;
306*64ab3302SCarolineConcatto   PutAttrs(ss, attrs);
307*64ab3302SCarolineConcatto   if (!ss.str().empty()) {
308*64ab3302SCarolineConcatto     decls_ << ss.str().substr(1) << "::" << symbol.name() << '\n';
309*64ab3302SCarolineConcatto   }
310*64ab3302SCarolineConcatto   bool isInterface{details.isInterface()};
311*64ab3302SCarolineConcatto   std::ostream &os{isInterface ? decls_ : contains_};
312*64ab3302SCarolineConcatto   if (isInterface) {
313*64ab3302SCarolineConcatto     os << "interface\n";
314*64ab3302SCarolineConcatto   }
315*64ab3302SCarolineConcatto   PutAttrs(os, prefixAttrs, std::nullopt, ""s, " "s);
316*64ab3302SCarolineConcatto   os << (details.isFunction() ? "function " : "subroutine ");
317*64ab3302SCarolineConcatto   os << symbol.name() << '(';
318*64ab3302SCarolineConcatto   int n = 0;
319*64ab3302SCarolineConcatto   for (const auto &dummy : details.dummyArgs()) {
320*64ab3302SCarolineConcatto     if (n++ > 0) {
321*64ab3302SCarolineConcatto       os << ',';
322*64ab3302SCarolineConcatto     }
323*64ab3302SCarolineConcatto     os << dummy->name();
324*64ab3302SCarolineConcatto   }
325*64ab3302SCarolineConcatto   os << ')';
326*64ab3302SCarolineConcatto   PutAttrs(os, bindAttrs, details.bindName(), " "s, ""s);
327*64ab3302SCarolineConcatto   if (details.isFunction()) {
328*64ab3302SCarolineConcatto     const Symbol &result{details.result()};
329*64ab3302SCarolineConcatto     if (result.name() != symbol.name()) {
330*64ab3302SCarolineConcatto       os << " result(" << result.name() << ')';
331*64ab3302SCarolineConcatto     }
332*64ab3302SCarolineConcatto   }
333*64ab3302SCarolineConcatto   os << '\n';
334*64ab3302SCarolineConcatto 
335*64ab3302SCarolineConcatto   // walk symbols, collect ones needed
336*64ab3302SCarolineConcatto   ModFileWriter writer{context_};
337*64ab3302SCarolineConcatto   std::stringstream typeBindings;
338*64ab3302SCarolineConcatto   SubprogramSymbolCollector collector{symbol};
339*64ab3302SCarolineConcatto   collector.Collect();
340*64ab3302SCarolineConcatto   for (const Symbol &need : collector.symbols()) {
341*64ab3302SCarolineConcatto     writer.PutSymbol(typeBindings, need);
342*64ab3302SCarolineConcatto   }
343*64ab3302SCarolineConcatto   CHECK(typeBindings.str().empty());
344*64ab3302SCarolineConcatto   os << writer.uses_.str();
345*64ab3302SCarolineConcatto   for (const SourceName &import : collector.imports()) {
346*64ab3302SCarolineConcatto     decls_ << "import::" << import << "\n";
347*64ab3302SCarolineConcatto   }
348*64ab3302SCarolineConcatto   os << writer.decls_.str();
349*64ab3302SCarolineConcatto   os << "end\n";
350*64ab3302SCarolineConcatto   if (isInterface) {
351*64ab3302SCarolineConcatto     os << "end interface\n";
352*64ab3302SCarolineConcatto   }
353*64ab3302SCarolineConcatto }
354*64ab3302SCarolineConcatto 
355*64ab3302SCarolineConcatto static bool IsIntrinsicOp(const Symbol &symbol) {
356*64ab3302SCarolineConcatto   if (const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()}) {
357*64ab3302SCarolineConcatto     return details->kind().IsIntrinsicOperator();
358*64ab3302SCarolineConcatto   } else {
359*64ab3302SCarolineConcatto     return false;
360*64ab3302SCarolineConcatto   }
361*64ab3302SCarolineConcatto }
362*64ab3302SCarolineConcatto 
363*64ab3302SCarolineConcatto static std::ostream &PutGenericName(std::ostream &os, const Symbol &symbol) {
364*64ab3302SCarolineConcatto   if (IsGenericDefinedOp(symbol)) {
365*64ab3302SCarolineConcatto     return os << "operator(" << symbol.name() << ')';
366*64ab3302SCarolineConcatto   } else {
367*64ab3302SCarolineConcatto     return os << symbol.name();
368*64ab3302SCarolineConcatto   }
369*64ab3302SCarolineConcatto }
370*64ab3302SCarolineConcatto 
371*64ab3302SCarolineConcatto void ModFileWriter::PutGeneric(const Symbol &symbol) {
372*64ab3302SCarolineConcatto   auto &details{symbol.get<GenericDetails>()};
373*64ab3302SCarolineConcatto   PutGenericName(decls_ << "interface ", symbol) << '\n';
374*64ab3302SCarolineConcatto   for (const Symbol &specific : details.specificProcs()) {
375*64ab3302SCarolineConcatto     decls_ << "procedure::" << specific.name() << '\n';
376*64ab3302SCarolineConcatto   }
377*64ab3302SCarolineConcatto   decls_ << "end interface\n";
378*64ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::PRIVATE)) {
379*64ab3302SCarolineConcatto     PutGenericName(decls_ << "private::", symbol) << '\n';
380*64ab3302SCarolineConcatto   }
381*64ab3302SCarolineConcatto }
382*64ab3302SCarolineConcatto 
383*64ab3302SCarolineConcatto void ModFileWriter::PutUse(const Symbol &symbol) {
384*64ab3302SCarolineConcatto   auto &details{symbol.get<UseDetails>()};
385*64ab3302SCarolineConcatto   auto &use{details.symbol()};
386*64ab3302SCarolineConcatto   uses_ << "use " << details.module().name();
387*64ab3302SCarolineConcatto   PutGenericName(uses_ << ",only:", symbol);
388*64ab3302SCarolineConcatto   // Can have intrinsic op with different local-name and use-name
389*64ab3302SCarolineConcatto   // (e.g. `operator(<)` and `operator(.lt.)`) but rename is not allowed
390*64ab3302SCarolineConcatto   if (!IsIntrinsicOp(symbol) && use.name() != symbol.name()) {
391*64ab3302SCarolineConcatto     PutGenericName(uses_ << "=>", use);
392*64ab3302SCarolineConcatto   }
393*64ab3302SCarolineConcatto   uses_ << '\n';
394*64ab3302SCarolineConcatto   PutUseExtraAttr(Attr::VOLATILE, symbol, use);
395*64ab3302SCarolineConcatto   PutUseExtraAttr(Attr::ASYNCHRONOUS, symbol, use);
396*64ab3302SCarolineConcatto }
397*64ab3302SCarolineConcatto 
398*64ab3302SCarolineConcatto // We have "USE local => use" in this module. If attr was added locally
399*64ab3302SCarolineConcatto // (i.e. on local but not on use), also write it out in the mod file.
400*64ab3302SCarolineConcatto void ModFileWriter::PutUseExtraAttr(
401*64ab3302SCarolineConcatto     Attr attr, const Symbol &local, const Symbol &use) {
402*64ab3302SCarolineConcatto   if (local.attrs().test(attr) && !use.attrs().test(attr)) {
403*64ab3302SCarolineConcatto     PutAttr(useExtraAttrs_, attr) << "::";
404*64ab3302SCarolineConcatto     useExtraAttrs_ << local.name() << '\n';
405*64ab3302SCarolineConcatto   }
406*64ab3302SCarolineConcatto }
407*64ab3302SCarolineConcatto 
408*64ab3302SCarolineConcatto // Collect the symbols of this scope sorted by their original order, not name.
409*64ab3302SCarolineConcatto // Namelists are an exception: they are sorted after other symbols.
410*64ab3302SCarolineConcatto SymbolVector CollectSymbols(const Scope &scope) {
411*64ab3302SCarolineConcatto   SymbolSet symbols;  // to prevent duplicates
412*64ab3302SCarolineConcatto   SymbolVector sorted;
413*64ab3302SCarolineConcatto   SymbolVector namelist;
414*64ab3302SCarolineConcatto   SymbolVector common;
415*64ab3302SCarolineConcatto   sorted.reserve(scope.size() + scope.commonBlocks().size());
416*64ab3302SCarolineConcatto   for (const auto &pair : scope) {
417*64ab3302SCarolineConcatto     const Symbol &symbol{*pair.second};
418*64ab3302SCarolineConcatto     if (!symbol.test(Symbol::Flag::ParentComp)) {
419*64ab3302SCarolineConcatto       if (symbols.insert(symbol).second) {
420*64ab3302SCarolineConcatto         if (symbol.has<NamelistDetails>()) {
421*64ab3302SCarolineConcatto           namelist.push_back(symbol);
422*64ab3302SCarolineConcatto         } else {
423*64ab3302SCarolineConcatto           sorted.push_back(symbol);
424*64ab3302SCarolineConcatto         }
425*64ab3302SCarolineConcatto       }
426*64ab3302SCarolineConcatto     }
427*64ab3302SCarolineConcatto   }
428*64ab3302SCarolineConcatto   for (const auto &pair : scope.commonBlocks()) {
429*64ab3302SCarolineConcatto     const Symbol &symbol{*pair.second};
430*64ab3302SCarolineConcatto     if (symbols.insert(symbol).second) {
431*64ab3302SCarolineConcatto       common.push_back(symbol);
432*64ab3302SCarolineConcatto     }
433*64ab3302SCarolineConcatto   }
434*64ab3302SCarolineConcatto   // sort normal symbols, then namelists, then common blocks:
435*64ab3302SCarolineConcatto   auto cursor{sorted.begin()};
436*64ab3302SCarolineConcatto   std::sort(cursor, sorted.end());
437*64ab3302SCarolineConcatto   cursor = sorted.insert(sorted.end(), namelist.begin(), namelist.end());
438*64ab3302SCarolineConcatto   std::sort(cursor, sorted.end());
439*64ab3302SCarolineConcatto   cursor = sorted.insert(sorted.end(), common.begin(), common.end());
440*64ab3302SCarolineConcatto   std::sort(cursor, sorted.end());
441*64ab3302SCarolineConcatto   return sorted;
442*64ab3302SCarolineConcatto }
443*64ab3302SCarolineConcatto 
444*64ab3302SCarolineConcatto void PutEntity(std::ostream &os, const Symbol &symbol) {
445*64ab3302SCarolineConcatto   std::visit(
446*64ab3302SCarolineConcatto       common::visitors{
447*64ab3302SCarolineConcatto           [&](const ObjectEntityDetails &) { PutObjectEntity(os, symbol); },
448*64ab3302SCarolineConcatto           [&](const ProcEntityDetails &) { PutProcEntity(os, symbol); },
449*64ab3302SCarolineConcatto           [&](const TypeParamDetails &) { PutTypeParam(os, symbol); },
450*64ab3302SCarolineConcatto           [&](const auto &) {
451*64ab3302SCarolineConcatto             common::die("PutEntity: unexpected details: %s",
452*64ab3302SCarolineConcatto                 DetailsToString(symbol.details()).c_str());
453*64ab3302SCarolineConcatto           },
454*64ab3302SCarolineConcatto       },
455*64ab3302SCarolineConcatto       symbol.details());
456*64ab3302SCarolineConcatto }
457*64ab3302SCarolineConcatto 
458*64ab3302SCarolineConcatto void PutShapeSpec(std::ostream &os, const ShapeSpec &x) {
459*64ab3302SCarolineConcatto   if (x.lbound().isAssumed()) {
460*64ab3302SCarolineConcatto     CHECK(x.ubound().isAssumed());
461*64ab3302SCarolineConcatto     os << "..";
462*64ab3302SCarolineConcatto   } else {
463*64ab3302SCarolineConcatto     if (!x.lbound().isDeferred()) {
464*64ab3302SCarolineConcatto       PutBound(os, x.lbound());
465*64ab3302SCarolineConcatto     }
466*64ab3302SCarolineConcatto     os << ':';
467*64ab3302SCarolineConcatto     if (!x.ubound().isDeferred()) {
468*64ab3302SCarolineConcatto       PutBound(os, x.ubound());
469*64ab3302SCarolineConcatto     }
470*64ab3302SCarolineConcatto   }
471*64ab3302SCarolineConcatto }
472*64ab3302SCarolineConcatto void PutShape(std::ostream &os, const ArraySpec &shape, char open, char close) {
473*64ab3302SCarolineConcatto   if (!shape.empty()) {
474*64ab3302SCarolineConcatto     os << open;
475*64ab3302SCarolineConcatto     bool first{true};
476*64ab3302SCarolineConcatto     for (const auto &shapeSpec : shape) {
477*64ab3302SCarolineConcatto       if (first) {
478*64ab3302SCarolineConcatto         first = false;
479*64ab3302SCarolineConcatto       } else {
480*64ab3302SCarolineConcatto         os << ',';
481*64ab3302SCarolineConcatto       }
482*64ab3302SCarolineConcatto       PutShapeSpec(os, shapeSpec);
483*64ab3302SCarolineConcatto     }
484*64ab3302SCarolineConcatto     os << close;
485*64ab3302SCarolineConcatto   }
486*64ab3302SCarolineConcatto }
487*64ab3302SCarolineConcatto 
488*64ab3302SCarolineConcatto void PutObjectEntity(std::ostream &os, const Symbol &symbol) {
489*64ab3302SCarolineConcatto   auto &details{symbol.get<ObjectEntityDetails>()};
490*64ab3302SCarolineConcatto   PutEntity(os, symbol, [&]() { PutType(os, DEREF(symbol.GetType())); },
491*64ab3302SCarolineConcatto       symbol.attrs());
492*64ab3302SCarolineConcatto   PutShape(os, details.shape(), '(', ')');
493*64ab3302SCarolineConcatto   PutShape(os, details.coshape(), '[', ']');
494*64ab3302SCarolineConcatto   PutInit(os, symbol, details.init());
495*64ab3302SCarolineConcatto   os << '\n';
496*64ab3302SCarolineConcatto }
497*64ab3302SCarolineConcatto 
498*64ab3302SCarolineConcatto void PutProcEntity(std::ostream &os, const Symbol &symbol) {
499*64ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::INTRINSIC)) {
500*64ab3302SCarolineConcatto     os << "intrinsic::" << symbol.name() << '\n';
501*64ab3302SCarolineConcatto     return;
502*64ab3302SCarolineConcatto   }
503*64ab3302SCarolineConcatto   const auto &details{symbol.get<ProcEntityDetails>()};
504*64ab3302SCarolineConcatto   const ProcInterface &interface{details.interface()};
505*64ab3302SCarolineConcatto   Attrs attrs{symbol.attrs()};
506*64ab3302SCarolineConcatto   if (details.passName()) {
507*64ab3302SCarolineConcatto     attrs.reset(Attr::PASS);
508*64ab3302SCarolineConcatto   }
509*64ab3302SCarolineConcatto   PutEntity(os, symbol,
510*64ab3302SCarolineConcatto       [&]() {
511*64ab3302SCarolineConcatto         os << "procedure(";
512*64ab3302SCarolineConcatto         if (interface.symbol()) {
513*64ab3302SCarolineConcatto           os << interface.symbol()->name();
514*64ab3302SCarolineConcatto         } else if (interface.type()) {
515*64ab3302SCarolineConcatto           PutType(os, *interface.type());
516*64ab3302SCarolineConcatto         }
517*64ab3302SCarolineConcatto         os << ')';
518*64ab3302SCarolineConcatto         PutPassName(os, details.passName());
519*64ab3302SCarolineConcatto       },
520*64ab3302SCarolineConcatto       attrs);
521*64ab3302SCarolineConcatto   os << '\n';
522*64ab3302SCarolineConcatto }
523*64ab3302SCarolineConcatto 
524*64ab3302SCarolineConcatto void PutPassName(std::ostream &os, const std::optional<SourceName> &passName) {
525*64ab3302SCarolineConcatto   if (passName) {
526*64ab3302SCarolineConcatto     os << ",pass(" << *passName << ')';
527*64ab3302SCarolineConcatto   }
528*64ab3302SCarolineConcatto }
529*64ab3302SCarolineConcatto 
530*64ab3302SCarolineConcatto void PutTypeParam(std::ostream &os, const Symbol &symbol) {
531*64ab3302SCarolineConcatto   auto &details{symbol.get<TypeParamDetails>()};
532*64ab3302SCarolineConcatto   PutEntity(os, symbol,
533*64ab3302SCarolineConcatto       [&]() {
534*64ab3302SCarolineConcatto         PutType(os, DEREF(symbol.GetType()));
535*64ab3302SCarolineConcatto         PutLower(os << ',', common::EnumToString(details.attr()));
536*64ab3302SCarolineConcatto       },
537*64ab3302SCarolineConcatto       symbol.attrs());
538*64ab3302SCarolineConcatto   PutInit(os, details.init());
539*64ab3302SCarolineConcatto   os << '\n';
540*64ab3302SCarolineConcatto }
541*64ab3302SCarolineConcatto 
542*64ab3302SCarolineConcatto void PutInit(std::ostream &os, const Symbol &symbol, const MaybeExpr &init) {
543*64ab3302SCarolineConcatto   if (init) {
544*64ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::PARAMETER) ||
545*64ab3302SCarolineConcatto         symbol.owner().IsDerivedType()) {
546*64ab3302SCarolineConcatto       os << (symbol.attrs().test(Attr::POINTER) ? "=>" : "=");
547*64ab3302SCarolineConcatto       init->AsFortran(os);
548*64ab3302SCarolineConcatto     }
549*64ab3302SCarolineConcatto   }
550*64ab3302SCarolineConcatto }
551*64ab3302SCarolineConcatto 
552*64ab3302SCarolineConcatto void PutInit(std::ostream &os, const MaybeIntExpr &init) {
553*64ab3302SCarolineConcatto   if (init) {
554*64ab3302SCarolineConcatto     init->AsFortran(os << '=');
555*64ab3302SCarolineConcatto   }
556*64ab3302SCarolineConcatto }
557*64ab3302SCarolineConcatto 
558*64ab3302SCarolineConcatto void PutBound(std::ostream &os, const Bound &x) {
559*64ab3302SCarolineConcatto   if (x.isAssumed()) {
560*64ab3302SCarolineConcatto     os << '*';
561*64ab3302SCarolineConcatto   } else if (x.isDeferred()) {
562*64ab3302SCarolineConcatto     os << ':';
563*64ab3302SCarolineConcatto   } else {
564*64ab3302SCarolineConcatto     x.GetExplicit()->AsFortran(os);
565*64ab3302SCarolineConcatto   }
566*64ab3302SCarolineConcatto }
567*64ab3302SCarolineConcatto 
568*64ab3302SCarolineConcatto // Write an entity (object or procedure) declaration.
569*64ab3302SCarolineConcatto // writeType is called to write out the type.
570*64ab3302SCarolineConcatto void PutEntity(std::ostream &os, const Symbol &symbol,
571*64ab3302SCarolineConcatto     std::function<void()> writeType, Attrs attrs) {
572*64ab3302SCarolineConcatto   writeType();
573*64ab3302SCarolineConcatto   MaybeExpr bindName;
574*64ab3302SCarolineConcatto   std::visit(
575*64ab3302SCarolineConcatto       common::visitors{
576*64ab3302SCarolineConcatto           [&](const SubprogramDetails &x) { bindName = x.bindName(); },
577*64ab3302SCarolineConcatto           [&](const ObjectEntityDetails &x) { bindName = x.bindName(); },
578*64ab3302SCarolineConcatto           [&](const ProcEntityDetails &x) { bindName = x.bindName(); },
579*64ab3302SCarolineConcatto           [&](const auto &) {},
580*64ab3302SCarolineConcatto       },
581*64ab3302SCarolineConcatto       symbol.details());
582*64ab3302SCarolineConcatto   PutAttrs(os, attrs, bindName);
583*64ab3302SCarolineConcatto   os << "::" << symbol.name();
584*64ab3302SCarolineConcatto }
585*64ab3302SCarolineConcatto 
586*64ab3302SCarolineConcatto // Put out each attribute to os, surrounded by `before` and `after` and
587*64ab3302SCarolineConcatto // mapped to lower case.
588*64ab3302SCarolineConcatto std::ostream &PutAttrs(std::ostream &os, Attrs attrs, const MaybeExpr &bindName,
589*64ab3302SCarolineConcatto     std::string before, std::string after) {
590*64ab3302SCarolineConcatto   attrs.set(Attr::PUBLIC, false);  // no need to write PUBLIC
591*64ab3302SCarolineConcatto   attrs.set(Attr::EXTERNAL, false);  // no need to write EXTERNAL
592*64ab3302SCarolineConcatto   if (bindName) {
593*64ab3302SCarolineConcatto     bindName->AsFortran(os << before << "bind(c, name=") << ')' << after;
594*64ab3302SCarolineConcatto     attrs.set(Attr::BIND_C, false);
595*64ab3302SCarolineConcatto   }
596*64ab3302SCarolineConcatto   for (std::size_t i{0}; i < Attr_enumSize; ++i) {
597*64ab3302SCarolineConcatto     Attr attr{static_cast<Attr>(i)};
598*64ab3302SCarolineConcatto     if (attrs.test(attr)) {
599*64ab3302SCarolineConcatto       PutAttr(os << before, attr) << after;
600*64ab3302SCarolineConcatto     }
601*64ab3302SCarolineConcatto   }
602*64ab3302SCarolineConcatto   return os;
603*64ab3302SCarolineConcatto }
604*64ab3302SCarolineConcatto 
605*64ab3302SCarolineConcatto std::ostream &PutAttr(std::ostream &os, Attr attr) {
606*64ab3302SCarolineConcatto   return PutLower(os, AttrToString(attr));
607*64ab3302SCarolineConcatto }
608*64ab3302SCarolineConcatto 
609*64ab3302SCarolineConcatto std::ostream &PutType(std::ostream &os, const DeclTypeSpec &type) {
610*64ab3302SCarolineConcatto   return PutLower(os, type.AsFortran());
611*64ab3302SCarolineConcatto }
612*64ab3302SCarolineConcatto 
613*64ab3302SCarolineConcatto std::ostream &PutLower(std::ostream &os, const std::string &str) {
614*64ab3302SCarolineConcatto   for (char c : str) {
615*64ab3302SCarolineConcatto     os << parser::ToLowerCaseLetter(c);
616*64ab3302SCarolineConcatto   }
617*64ab3302SCarolineConcatto   return os;
618*64ab3302SCarolineConcatto }
619*64ab3302SCarolineConcatto 
620*64ab3302SCarolineConcatto struct Temp {
621*64ab3302SCarolineConcatto   Temp() = delete;
622*64ab3302SCarolineConcatto   ~Temp() {
623*64ab3302SCarolineConcatto     close(fd);
624*64ab3302SCarolineConcatto     unlink(path.c_str());
625*64ab3302SCarolineConcatto   }
626*64ab3302SCarolineConcatto   int fd;
627*64ab3302SCarolineConcatto   std::string path;
628*64ab3302SCarolineConcatto };
629*64ab3302SCarolineConcatto 
630*64ab3302SCarolineConcatto // Create a temp file in the same directory and with the same suffix as path.
631*64ab3302SCarolineConcatto // Return an open file descriptor and its path.
632*64ab3302SCarolineConcatto static Temp MkTemp(const std::string &path) {
633*64ab3302SCarolineConcatto   auto length{path.length()};
634*64ab3302SCarolineConcatto   auto dot{path.find_last_of("./")};
635*64ab3302SCarolineConcatto   std::string suffix{dot < length && path[dot] == '.' ? path.substr(dot) : ""};
636*64ab3302SCarolineConcatto   CHECK(length > suffix.length() &&
637*64ab3302SCarolineConcatto       path.substr(length - suffix.length()) == suffix);
638*64ab3302SCarolineConcatto   auto tempPath{path.substr(0, length - suffix.length()) + "XXXXXX" + suffix};
639*64ab3302SCarolineConcatto   int fd{mkstemps(&tempPath[0], suffix.length())};
640*64ab3302SCarolineConcatto   auto mask{umask(0777)};
641*64ab3302SCarolineConcatto   umask(mask);
642*64ab3302SCarolineConcatto   chmod(tempPath.c_str(), 0666 & ~mask);  // temp is created with mode 0600
643*64ab3302SCarolineConcatto   return Temp{fd, tempPath};
644*64ab3302SCarolineConcatto }
645*64ab3302SCarolineConcatto 
646*64ab3302SCarolineConcatto // Write the module file at path, prepending header. If an error occurs,
647*64ab3302SCarolineConcatto // return errno, otherwise 0.
648*64ab3302SCarolineConcatto static int WriteFile(const std::string &path, const std::string &contents) {
649*64ab3302SCarolineConcatto   auto header{std::string{ModHeader::bom} + ModHeader::magic +
650*64ab3302SCarolineConcatto       CheckSum(contents) + ModHeader::terminator};
651*64ab3302SCarolineConcatto   if (FileContentsMatch(path, header, contents)) {
652*64ab3302SCarolineConcatto     return 0;
653*64ab3302SCarolineConcatto   }
654*64ab3302SCarolineConcatto   Temp temp{MkTemp(path)};
655*64ab3302SCarolineConcatto   if (temp.fd < 0) {
656*64ab3302SCarolineConcatto     return errno;
657*64ab3302SCarolineConcatto   }
658*64ab3302SCarolineConcatto   if (write(temp.fd, header.c_str(), header.size()) !=
659*64ab3302SCarolineConcatto           static_cast<ssize_t>(header.size()) ||
660*64ab3302SCarolineConcatto       write(temp.fd, contents.c_str(), contents.size()) !=
661*64ab3302SCarolineConcatto           static_cast<ssize_t>(contents.size())) {
662*64ab3302SCarolineConcatto     return errno;
663*64ab3302SCarolineConcatto   }
664*64ab3302SCarolineConcatto   if (std::rename(temp.path.c_str(), path.c_str()) == -1) {
665*64ab3302SCarolineConcatto     return errno;
666*64ab3302SCarolineConcatto   }
667*64ab3302SCarolineConcatto   return 0;
668*64ab3302SCarolineConcatto }
669*64ab3302SCarolineConcatto 
670*64ab3302SCarolineConcatto // Return true if the stream matches what we would write for the mod file.
671*64ab3302SCarolineConcatto static bool FileContentsMatch(const std::string &path,
672*64ab3302SCarolineConcatto     const std::string &header, const std::string &contents) {
673*64ab3302SCarolineConcatto   std::size_t hsize{header.size()};
674*64ab3302SCarolineConcatto   std::size_t csize{contents.size()};
675*64ab3302SCarolineConcatto   if (GetFileSize(path) != hsize + csize) {
676*64ab3302SCarolineConcatto     return false;
677*64ab3302SCarolineConcatto   }
678*64ab3302SCarolineConcatto   int fd{open(path.c_str(), O_RDONLY)};
679*64ab3302SCarolineConcatto   if (fd < 0) {
680*64ab3302SCarolineConcatto     return false;
681*64ab3302SCarolineConcatto   }
682*64ab3302SCarolineConcatto   constexpr std::size_t bufSize{4096};
683*64ab3302SCarolineConcatto   std::string buffer(bufSize, '\0');
684*64ab3302SCarolineConcatto   if (read(fd, &buffer[0], hsize) != static_cast<ssize_t>(hsize) ||
685*64ab3302SCarolineConcatto       std::memcmp(&buffer[0], &header[0], hsize) != 0) {
686*64ab3302SCarolineConcatto     close(fd);
687*64ab3302SCarolineConcatto     return false;  // header doesn't match
688*64ab3302SCarolineConcatto   }
689*64ab3302SCarolineConcatto   for (auto remaining{csize};;) {
690*64ab3302SCarolineConcatto     auto bytes{std::min(bufSize, remaining)};
691*64ab3302SCarolineConcatto     auto got{read(fd, &buffer[0], bytes)};
692*64ab3302SCarolineConcatto     if (got != static_cast<ssize_t>(bytes) ||
693*64ab3302SCarolineConcatto         std::memcmp(&buffer[0], &contents[csize - remaining], bytes) != 0) {
694*64ab3302SCarolineConcatto       close(fd);
695*64ab3302SCarolineConcatto       return false;
696*64ab3302SCarolineConcatto     }
697*64ab3302SCarolineConcatto     if (bytes == 0 && remaining == 0) {
698*64ab3302SCarolineConcatto       close(fd);
699*64ab3302SCarolineConcatto       return true;
700*64ab3302SCarolineConcatto     }
701*64ab3302SCarolineConcatto     remaining -= bytes;
702*64ab3302SCarolineConcatto   }
703*64ab3302SCarolineConcatto }
704*64ab3302SCarolineConcatto 
705*64ab3302SCarolineConcatto // Compute a simple hash of the contents of a module file and
706*64ab3302SCarolineConcatto // return it as a string of hex digits.
707*64ab3302SCarolineConcatto // This uses the Fowler-Noll-Vo hash function.
708*64ab3302SCarolineConcatto static std::string CheckSum(const std::string_view &contents) {
709*64ab3302SCarolineConcatto   std::uint64_t hash{0xcbf29ce484222325ull};
710*64ab3302SCarolineConcatto   for (char c : contents) {
711*64ab3302SCarolineConcatto     hash ^= c & 0xff;
712*64ab3302SCarolineConcatto     hash *= 0x100000001b3;
713*64ab3302SCarolineConcatto   }
714*64ab3302SCarolineConcatto   static const char *digits = "0123456789abcdef";
715*64ab3302SCarolineConcatto   std::string result(ModHeader::sumLen, '0');
716*64ab3302SCarolineConcatto   for (size_t i{ModHeader::sumLen}; hash != 0; hash >>= 4) {
717*64ab3302SCarolineConcatto     result[--i] = digits[hash & 0xf];
718*64ab3302SCarolineConcatto   }
719*64ab3302SCarolineConcatto   return result;
720*64ab3302SCarolineConcatto }
721*64ab3302SCarolineConcatto 
722*64ab3302SCarolineConcatto static bool VerifyHeader(const char *content, std::size_t len) {
723*64ab3302SCarolineConcatto   std::string_view sv{content, len};
724*64ab3302SCarolineConcatto   if (sv.substr(0, ModHeader::magicLen) != ModHeader::magic) {
725*64ab3302SCarolineConcatto     return false;
726*64ab3302SCarolineConcatto   }
727*64ab3302SCarolineConcatto   std::string_view expectSum{sv.substr(ModHeader::magicLen, ModHeader::sumLen)};
728*64ab3302SCarolineConcatto   std::string actualSum{CheckSum(sv.substr(ModHeader::len))};
729*64ab3302SCarolineConcatto   return expectSum == actualSum;
730*64ab3302SCarolineConcatto }
731*64ab3302SCarolineConcatto 
732*64ab3302SCarolineConcatto static std::size_t GetFileSize(const std::string &path) {
733*64ab3302SCarolineConcatto   struct stat statbuf;
734*64ab3302SCarolineConcatto   if (stat(path.c_str(), &statbuf) == 0) {
735*64ab3302SCarolineConcatto     return static_cast<std::size_t>(statbuf.st_size);
736*64ab3302SCarolineConcatto   } else {
737*64ab3302SCarolineConcatto     return 0;
738*64ab3302SCarolineConcatto   }
739*64ab3302SCarolineConcatto }
740*64ab3302SCarolineConcatto 
741*64ab3302SCarolineConcatto Scope *ModFileReader::Read(const SourceName &name, Scope *ancestor) {
742*64ab3302SCarolineConcatto   std::string ancestorName;  // empty for module
743*64ab3302SCarolineConcatto   if (ancestor) {
744*64ab3302SCarolineConcatto     if (auto *scope{ancestor->FindSubmodule(name)}) {
745*64ab3302SCarolineConcatto       return scope;
746*64ab3302SCarolineConcatto     }
747*64ab3302SCarolineConcatto     ancestorName = ancestor->GetName().value().ToString();
748*64ab3302SCarolineConcatto   } else {
749*64ab3302SCarolineConcatto     auto it{context_.globalScope().find(name)};
750*64ab3302SCarolineConcatto     if (it != context_.globalScope().end()) {
751*64ab3302SCarolineConcatto       return it->second->scope();
752*64ab3302SCarolineConcatto     }
753*64ab3302SCarolineConcatto   }
754*64ab3302SCarolineConcatto   parser::Parsing parsing{context_.allSources()};
755*64ab3302SCarolineConcatto   parser::Options options;
756*64ab3302SCarolineConcatto   options.isModuleFile = true;
757*64ab3302SCarolineConcatto   options.features.Enable(common::LanguageFeature::BackslashEscapes);
758*64ab3302SCarolineConcatto   options.searchDirectories = context_.searchDirectories();
759*64ab3302SCarolineConcatto   auto path{ModFileName(name, ancestorName, context_.moduleFileSuffix())};
760*64ab3302SCarolineConcatto   const auto *sourceFile{parsing.Prescan(path, options)};
761*64ab3302SCarolineConcatto   if (parsing.messages().AnyFatalError()) {
762*64ab3302SCarolineConcatto     for (auto &msg : parsing.messages().messages()) {
763*64ab3302SCarolineConcatto       std::string str{msg.ToString()};
764*64ab3302SCarolineConcatto       Say(name, ancestorName, parser::MessageFixedText{str.c_str(), str.size()},
765*64ab3302SCarolineConcatto           path);
766*64ab3302SCarolineConcatto     }
767*64ab3302SCarolineConcatto     return nullptr;
768*64ab3302SCarolineConcatto   }
769*64ab3302SCarolineConcatto   CHECK(sourceFile);
770*64ab3302SCarolineConcatto   if (!VerifyHeader(sourceFile->content(), sourceFile->bytes())) {
771*64ab3302SCarolineConcatto     Say(name, ancestorName, "File has invalid checksum: %s"_en_US,
772*64ab3302SCarolineConcatto         sourceFile->path());
773*64ab3302SCarolineConcatto     return nullptr;
774*64ab3302SCarolineConcatto   }
775*64ab3302SCarolineConcatto 
776*64ab3302SCarolineConcatto   parsing.Parse(nullptr);
777*64ab3302SCarolineConcatto   auto &parseTree{parsing.parseTree()};
778*64ab3302SCarolineConcatto   if (!parsing.messages().empty() || !parsing.consumedWholeFile() ||
779*64ab3302SCarolineConcatto       !parseTree) {
780*64ab3302SCarolineConcatto     Say(name, ancestorName, "Module file is corrupt: %s"_err_en_US,
781*64ab3302SCarolineConcatto         sourceFile->path());
782*64ab3302SCarolineConcatto     return nullptr;
783*64ab3302SCarolineConcatto   }
784*64ab3302SCarolineConcatto   Scope *parentScope;  // the scope this module/submodule goes into
785*64ab3302SCarolineConcatto   if (!ancestor) {
786*64ab3302SCarolineConcatto     parentScope = &context_.globalScope();
787*64ab3302SCarolineConcatto   } else if (std::optional<SourceName> parent{GetSubmoduleParent(*parseTree)}) {
788*64ab3302SCarolineConcatto     parentScope = Read(*parent, ancestor);
789*64ab3302SCarolineConcatto   } else {
790*64ab3302SCarolineConcatto     parentScope = ancestor;
791*64ab3302SCarolineConcatto   }
792*64ab3302SCarolineConcatto   ResolveNames(context_, *parseTree);
793*64ab3302SCarolineConcatto   const auto &it{parentScope->find(name)};
794*64ab3302SCarolineConcatto   if (it == parentScope->end()) {
795*64ab3302SCarolineConcatto     return nullptr;
796*64ab3302SCarolineConcatto   }
797*64ab3302SCarolineConcatto   auto &modSymbol{*it->second};
798*64ab3302SCarolineConcatto   modSymbol.set(Symbol::Flag::ModFile);
799*64ab3302SCarolineConcatto   modSymbol.scope()->set_chars(parsing.cooked());
800*64ab3302SCarolineConcatto   return modSymbol.scope();
801*64ab3302SCarolineConcatto }
802*64ab3302SCarolineConcatto 
803*64ab3302SCarolineConcatto parser::Message &ModFileReader::Say(const SourceName &name,
804*64ab3302SCarolineConcatto     const std::string &ancestor, parser::MessageFixedText &&msg,
805*64ab3302SCarolineConcatto     const std::string &arg) {
806*64ab3302SCarolineConcatto   return context_
807*64ab3302SCarolineConcatto       .Say(name,
808*64ab3302SCarolineConcatto           ancestor.empty()
809*64ab3302SCarolineConcatto               ? "Error reading module file for module '%s'"_err_en_US
810*64ab3302SCarolineConcatto               : "Error reading module file for submodule '%s' of module '%s'"_err_en_US,
811*64ab3302SCarolineConcatto           name, ancestor)
812*64ab3302SCarolineConcatto       .Attach(name, std::move(msg), arg);
813*64ab3302SCarolineConcatto }
814*64ab3302SCarolineConcatto 
815*64ab3302SCarolineConcatto // program was read from a .mod file for a submodule; return the name of the
816*64ab3302SCarolineConcatto // submodule's parent submodule, nullptr if none.
817*64ab3302SCarolineConcatto static std::optional<SourceName> GetSubmoduleParent(
818*64ab3302SCarolineConcatto     const parser::Program &program) {
819*64ab3302SCarolineConcatto   CHECK(program.v.size() == 1);
820*64ab3302SCarolineConcatto   auto &unit{program.v.front()};
821*64ab3302SCarolineConcatto   auto &submod{std::get<common::Indirection<parser::Submodule>>(unit.u)};
822*64ab3302SCarolineConcatto   auto &stmt{
823*64ab3302SCarolineConcatto       std::get<parser::Statement<parser::SubmoduleStmt>>(submod.value().t)};
824*64ab3302SCarolineConcatto   auto &parentId{std::get<parser::ParentIdentifier>(stmt.statement.t)};
825*64ab3302SCarolineConcatto   if (auto &parent{std::get<std::optional<parser::Name>>(parentId.t)}) {
826*64ab3302SCarolineConcatto     return parent->source;
827*64ab3302SCarolineConcatto   } else {
828*64ab3302SCarolineConcatto     return std::nullopt;
829*64ab3302SCarolineConcatto   }
830*64ab3302SCarolineConcatto }
831*64ab3302SCarolineConcatto 
832*64ab3302SCarolineConcatto void SubprogramSymbolCollector::Collect() {
833*64ab3302SCarolineConcatto   const auto &details{symbol_.get<SubprogramDetails>()};
834*64ab3302SCarolineConcatto   isInterface_ = details.isInterface();
835*64ab3302SCarolineConcatto   for (const Symbol *dummyArg : details.dummyArgs()) {
836*64ab3302SCarolineConcatto     DoSymbol(DEREF(dummyArg));
837*64ab3302SCarolineConcatto   }
838*64ab3302SCarolineConcatto   if (details.isFunction()) {
839*64ab3302SCarolineConcatto     DoSymbol(details.result());
840*64ab3302SCarolineConcatto   }
841*64ab3302SCarolineConcatto   for (const auto &pair : scope_) {
842*64ab3302SCarolineConcatto     const Symbol &symbol{*pair.second};
843*64ab3302SCarolineConcatto     if (const auto *useDetails{symbol.detailsIf<UseDetails>()}) {
844*64ab3302SCarolineConcatto       if (useSet_.count(useDetails->symbol()) > 0) {
845*64ab3302SCarolineConcatto         need_.push_back(symbol);
846*64ab3302SCarolineConcatto       }
847*64ab3302SCarolineConcatto     }
848*64ab3302SCarolineConcatto   }
849*64ab3302SCarolineConcatto }
850*64ab3302SCarolineConcatto 
851*64ab3302SCarolineConcatto void SubprogramSymbolCollector::DoSymbol(const Symbol &symbol) {
852*64ab3302SCarolineConcatto   DoSymbol(symbol.name(), symbol);
853*64ab3302SCarolineConcatto }
854*64ab3302SCarolineConcatto 
855*64ab3302SCarolineConcatto // Do symbols this one depends on; then add to need_
856*64ab3302SCarolineConcatto void SubprogramSymbolCollector::DoSymbol(
857*64ab3302SCarolineConcatto     const SourceName &name, const Symbol &symbol) {
858*64ab3302SCarolineConcatto   const auto &scope{symbol.owner()};
859*64ab3302SCarolineConcatto   if (scope != scope_ && !scope.IsDerivedType()) {
860*64ab3302SCarolineConcatto     if (scope != scope_.parent()) {
861*64ab3302SCarolineConcatto       useSet_.insert(symbol);
862*64ab3302SCarolineConcatto     }
863*64ab3302SCarolineConcatto     if (NeedImport(name, symbol)) {
864*64ab3302SCarolineConcatto       imports_.insert(name);
865*64ab3302SCarolineConcatto     }
866*64ab3302SCarolineConcatto     return;
867*64ab3302SCarolineConcatto   }
868*64ab3302SCarolineConcatto   if (!needSet_.insert(symbol).second) {
869*64ab3302SCarolineConcatto     return;  // already done
870*64ab3302SCarolineConcatto   }
871*64ab3302SCarolineConcatto   std::visit(
872*64ab3302SCarolineConcatto       common::visitors{
873*64ab3302SCarolineConcatto           [this](const ObjectEntityDetails &details) {
874*64ab3302SCarolineConcatto             for (const ShapeSpec &spec : details.shape()) {
875*64ab3302SCarolineConcatto               DoBound(spec.lbound());
876*64ab3302SCarolineConcatto               DoBound(spec.ubound());
877*64ab3302SCarolineConcatto             }
878*64ab3302SCarolineConcatto             for (const ShapeSpec &spec : details.coshape()) {
879*64ab3302SCarolineConcatto               DoBound(spec.lbound());
880*64ab3302SCarolineConcatto               DoBound(spec.ubound());
881*64ab3302SCarolineConcatto             }
882*64ab3302SCarolineConcatto             if (const Symbol * commonBlock{details.commonBlock()}) {
883*64ab3302SCarolineConcatto               DoSymbol(*commonBlock);
884*64ab3302SCarolineConcatto             }
885*64ab3302SCarolineConcatto           },
886*64ab3302SCarolineConcatto           [this](const CommonBlockDetails &details) {
887*64ab3302SCarolineConcatto             for (const Symbol &object : details.objects()) {
888*64ab3302SCarolineConcatto               DoSymbol(object);
889*64ab3302SCarolineConcatto             }
890*64ab3302SCarolineConcatto           },
891*64ab3302SCarolineConcatto           [](const auto &) {},
892*64ab3302SCarolineConcatto       },
893*64ab3302SCarolineConcatto       symbol.details());
894*64ab3302SCarolineConcatto   if (!symbol.has<UseDetails>()) {
895*64ab3302SCarolineConcatto     DoType(symbol.GetType());
896*64ab3302SCarolineConcatto   }
897*64ab3302SCarolineConcatto   if (!scope.IsDerivedType()) {
898*64ab3302SCarolineConcatto     need_.push_back(symbol);
899*64ab3302SCarolineConcatto   }
900*64ab3302SCarolineConcatto }
901*64ab3302SCarolineConcatto 
902*64ab3302SCarolineConcatto void SubprogramSymbolCollector::DoType(const DeclTypeSpec *type) {
903*64ab3302SCarolineConcatto   if (!type) {
904*64ab3302SCarolineConcatto     return;
905*64ab3302SCarolineConcatto   }
906*64ab3302SCarolineConcatto   switch (type->category()) {
907*64ab3302SCarolineConcatto   case DeclTypeSpec::Numeric:
908*64ab3302SCarolineConcatto   case DeclTypeSpec::Logical: break;  // nothing to do
909*64ab3302SCarolineConcatto   case DeclTypeSpec::Character:
910*64ab3302SCarolineConcatto     DoParamValue(type->characterTypeSpec().length());
911*64ab3302SCarolineConcatto     break;
912*64ab3302SCarolineConcatto   default:
913*64ab3302SCarolineConcatto     if (const DerivedTypeSpec * derived{type->AsDerived()}) {
914*64ab3302SCarolineConcatto       const auto &typeSymbol{derived->typeSymbol()};
915*64ab3302SCarolineConcatto       if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
916*64ab3302SCarolineConcatto         DoSymbol(extends->name(), extends->typeSymbol());
917*64ab3302SCarolineConcatto       }
918*64ab3302SCarolineConcatto       for (const auto &pair : derived->parameters()) {
919*64ab3302SCarolineConcatto         DoParamValue(pair.second);
920*64ab3302SCarolineConcatto       }
921*64ab3302SCarolineConcatto       for (const auto &pair : *typeSymbol.scope()) {
922*64ab3302SCarolineConcatto         const Symbol &comp{*pair.second};
923*64ab3302SCarolineConcatto         DoSymbol(comp);
924*64ab3302SCarolineConcatto       }
925*64ab3302SCarolineConcatto       DoSymbol(derived->name(), derived->typeSymbol());
926*64ab3302SCarolineConcatto     }
927*64ab3302SCarolineConcatto   }
928*64ab3302SCarolineConcatto }
929*64ab3302SCarolineConcatto 
930*64ab3302SCarolineConcatto void SubprogramSymbolCollector::DoBound(const Bound &bound) {
931*64ab3302SCarolineConcatto   if (const MaybeSubscriptIntExpr & expr{bound.GetExplicit()}) {
932*64ab3302SCarolineConcatto     DoExpr(*expr);
933*64ab3302SCarolineConcatto   }
934*64ab3302SCarolineConcatto }
935*64ab3302SCarolineConcatto void SubprogramSymbolCollector::DoParamValue(const ParamValue &paramValue) {
936*64ab3302SCarolineConcatto   if (const auto &expr{paramValue.GetExplicit()}) {
937*64ab3302SCarolineConcatto     DoExpr(*expr);
938*64ab3302SCarolineConcatto   }
939*64ab3302SCarolineConcatto }
940*64ab3302SCarolineConcatto 
941*64ab3302SCarolineConcatto // Do we need a IMPORT of this symbol into an interface block?
942*64ab3302SCarolineConcatto bool SubprogramSymbolCollector::NeedImport(
943*64ab3302SCarolineConcatto     const SourceName &name, const Symbol &symbol) {
944*64ab3302SCarolineConcatto   if (!isInterface_) {
945*64ab3302SCarolineConcatto     return false;
946*64ab3302SCarolineConcatto   } else if (symbol.owner() != scope_.parent()) {
947*64ab3302SCarolineConcatto     // detect import from parent of use-associated symbol
948*64ab3302SCarolineConcatto     // can be null in the case of a use-associated derived type's parent type
949*64ab3302SCarolineConcatto     const auto *found{scope_.FindSymbol(name)};
950*64ab3302SCarolineConcatto     CHECK(found || symbol.has<DerivedTypeDetails>());
951*64ab3302SCarolineConcatto     return found && found->has<UseDetails>() && found->owner() != scope_;
952*64ab3302SCarolineConcatto   } else {
953*64ab3302SCarolineConcatto     return true;
954*64ab3302SCarolineConcatto   }
955*64ab3302SCarolineConcatto }
956*64ab3302SCarolineConcatto 
957*64ab3302SCarolineConcatto }
958