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 ¶mValue) { 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