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