blob: 5d0d210fa3487d3452befbf218db570aeb8f068a [file] [log] [blame]
CarolineConcatto64ab3302020-02-25 15:11:521//===-- 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"
Tim Keith3780d3e2020-07-13 19:19:1711#include "flang/Common/restorer.h"
CarolineConcatto64ab3302020-02-25 15:11:5212#include "flang/Evaluate/tools.h"
13#include "flang/Parser/message.h"
14#include "flang/Parser/parsing.h"
Peter Klausler9e7eef92022-04-13 17:39:1615#include "flang/Parser/unparse.h"
CarolineConcatto64ab3302020-02-25 15:11:5216#include "flang/Semantics/scope.h"
17#include "flang/Semantics/semantics.h"
18#include "flang/Semantics/symbol.h"
19#include "flang/Semantics/tools.h"
David Truby0855c452020-02-25 15:59:5020#include "llvm/Support/FileSystem.h"
21#include "llvm/Support/MemoryBuffer.h"
22#include "llvm/Support/raw_ostream.h"
CarolineConcatto64ab3302020-02-25 15:11:5223#include <algorithm>
CarolineConcatto64ab3302020-02-25 15:11:5224#include <fstream>
CarolineConcatto64ab3302020-02-25 15:11:5225#include <set>
26#include <string_view>
CarolineConcatto64ab3302020-02-25 15:11:5227#include <vector>
28
29namespace Fortran::semantics {
30
31using namespace parser::literals;
32
33// The first line of a file that identifies it as a .mod file.
34// The first three bytes are a Unicode byte order mark that ensures
35// that the module file is decoded as UTF-8 even if source files
36// are using another encoding.
37struct ModHeader {
38 static constexpr const char bom[3 + 1]{"\xef\xbb\xbf"};
39 static constexpr int magicLen{13};
40 static constexpr int sumLen{16};
41 static constexpr const char magic[magicLen + 1]{"!mod$ v1 sum:"};
42 static constexpr char terminator{'\n'};
43 static constexpr int len{magicLen + 1 + sumLen};
Peter Klauslerf7a15e02024-03-01 21:58:3644 static constexpr int needLen{7};
45 static constexpr const char need[needLen + 1]{"!need$ "};
CarolineConcatto64ab3302020-02-25 15:11:5246};
47
48static std::optional<SourceName> GetSubmoduleParent(const parser::Program &);
Peter Klausler1bea0342023-10-31 19:17:0049static void CollectSymbols(const Scope &, SymbolVector &, SymbolVector &,
Peter Klauslerf7a15e02024-03-01 21:58:3650 std::map<const Symbol *, SourceName> &, UnorderedSymbolSet &);
Caroline Concatto8670e492020-02-28 15:11:0351static void PutPassName(llvm::raw_ostream &, const std::optional<SourceName> &);
Peter Klausler9e7eef92022-04-13 17:39:1652static void PutInit(llvm::raw_ostream &, const Symbol &, const MaybeExpr &,
Peter Klausler1bea0342023-10-31 19:17:0053 const parser::Expr *, const std::map<const Symbol *, SourceName> &);
Caroline Concatto8670e492020-02-28 15:11:0354static void PutInit(llvm::raw_ostream &, const MaybeIntExpr &);
55static void PutBound(llvm::raw_ostream &, const Bound &);
Peter Klauslerc14cf922021-12-18 00:48:1656static void PutShapeSpec(llvm::raw_ostream &, const ShapeSpec &);
57static void PutShape(
58 llvm::raw_ostream &, const ArraySpec &, char open, char close);
Caroline Concatto8670e492020-02-28 15:11:0359
60static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr);
61static llvm::raw_ostream &PutType(llvm::raw_ostream &, const DeclTypeSpec &);
Peter Klauslerbcba39a52022-11-11 01:29:2962static llvm::raw_ostream &PutLower(llvm::raw_ostream &, std::string_view);
Peter Klauslerf7a15e02024-03-01 21:58:3663static std::error_code WriteFile(const std::string &, const std::string &,
64 ModuleCheckSumType &, bool debug = true);
CarolineConcatto64ab3302020-02-25 15:11:5265static bool FileContentsMatch(
66 const std::string &, const std::string &, const std::string &);
Peter Klauslerf7a15e02024-03-01 21:58:3667static ModuleCheckSumType ComputeCheckSum(const std::string_view &);
68static std::string CheckSumString(ModuleCheckSumType);
CarolineConcatto64ab3302020-02-25 15:11:5269
70// Collect symbols needed for a subprogram interface
71class SubprogramSymbolCollector {
72public:
peter klauslerc42f6312020-03-19 23:31:1073 SubprogramSymbolCollector(const Symbol &symbol, const Scope &scope)
Tim Keith1f879002020-03-29 04:00:1674 : symbol_{symbol}, scope_{scope} {}
CarolineConcatto64ab3302020-02-25 15:11:5275 const SymbolVector &symbols() const { return need_; }
76 const std::set<SourceName> &imports() const { return imports_; }
77 void Collect();
78
79private:
80 const Symbol &symbol_;
81 const Scope &scope_;
82 bool isInterface_{false};
Tim Keith1f879002020-03-29 04:00:1683 SymbolVector need_; // symbols that are needed
peter klausler0d8331c2021-03-18 17:26:2384 UnorderedSymbolSet needSet_; // symbols already in need_
85 UnorderedSymbolSet useSet_; // use-associations that might be needed
Tim Keith1f879002020-03-29 04:00:1686 std::set<SourceName> imports_; // imports from host that are needed
CarolineConcatto64ab3302020-02-25 15:11:5287
88 void DoSymbol(const Symbol &);
89 void DoSymbol(const SourceName &, const Symbol &);
90 void DoType(const DeclTypeSpec *);
91 void DoBound(const Bound &);
92 void DoParamValue(const ParamValue &);
93 bool NeedImport(const SourceName &, const Symbol &);
94
Tim Keith1f879002020-03-29 04:00:1695 template <typename T> void DoExpr(evaluate::Expr<T> expr) {
CarolineConcatto64ab3302020-02-25 15:11:5296 for (const Symbol &symbol : evaluate::CollectSymbols(expr)) {
97 DoSymbol(symbol);
98 }
99 }
100};
101
102bool ModFileWriter::WriteAll() {
Tim Keith3780d3e2020-07-13 19:19:17103 // this flag affects character literals: force it to be consistent
104 auto restorer{
105 common::ScopedSet(parser::useHexadecimalEscapeSequences, false)};
CarolineConcatto64ab3302020-02-25 15:11:52106 WriteAll(context_.globalScope());
107 return !context_.AnyFatalError();
108}
109
110void ModFileWriter::WriteAll(const Scope &scope) {
111 for (const auto &child : scope.children()) {
112 WriteOne(child);
113 }
114}
115
116void ModFileWriter::WriteOne(const Scope &scope) {
117 if (scope.kind() == Scope::Kind::Module) {
118 auto *symbol{scope.symbol()};
119 if (!symbol->test(Symbol::Flag::ModFile)) {
120 Write(*symbol);
121 }
Tim Keith1f879002020-03-29 04:00:16122 WriteAll(scope); // write out submodules
CarolineConcatto64ab3302020-02-25 15:11:52123 }
124}
125
126// Construct the name of a module file. Non-empty ancestorName means submodule.
127static std::string ModFileName(const SourceName &name,
128 const std::string &ancestorName, const std::string &suffix) {
129 std::string result{name.ToString() + suffix};
130 return ancestorName.empty() ? result : ancestorName + '-' + result;
131}
132
133// Write the module file for symbol, which must be a module or submodule.
134void ModFileWriter::Write(const Symbol &symbol) {
Peter Klauslerf7a15e02024-03-01 21:58:36135 auto &module{symbol.get<ModuleDetails>()};
136 if (module.moduleFileHash()) {
137 return; // already written
138 }
139 auto *ancestor{module.ancestor()};
Peter Klausler4b7428e2022-11-17 21:34:40140 isSubmodule_ = ancestor != nullptr;
CarolineConcatto64ab3302020-02-25 15:11:52141 auto ancestorName{ancestor ? ancestor->GetName().value().ToString() : ""s};
142 auto path{context_.moduleDirectory() + '/' +
143 ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())};
144 PutSymbols(DEREF(symbol.scope()));
Peter Klauslerf7a15e02024-03-01 21:58:36145 ModuleCheckSumType checkSum;
146 if (std::error_code error{WriteFile(
147 path, GetAsString(symbol), checkSum, context_.debugModuleWriter())}) {
David Truby0855c452020-02-25 15:59:50148 context_.Say(
149 symbol.name(), "Error writing %s: %s"_err_en_US, path, error.message());
CarolineConcatto64ab3302020-02-25 15:11:52150 }
Peter Klauslerf7a15e02024-03-01 21:58:36151 const_cast<ModuleDetails &>(module).set_moduleFileHash(checkSum);
CarolineConcatto64ab3302020-02-25 15:11:52152}
153
154// Return the entire body of the module file
155// and clear saved uses, decls, and contains.
156std::string ModFileWriter::GetAsString(const Symbol &symbol) {
Caroline Concatto8670e492020-02-28 15:11:03157 std::string buf;
158 llvm::raw_string_ostream all{buf};
Peter Klauslerf7a15e02024-03-01 21:58:36159 all << needs_.str();
160 needs_.str().clear();
CarolineConcatto64ab3302020-02-25 15:11:52161 auto &details{symbol.get<ModuleDetails>()};
162 if (!details.isSubmodule()) {
163 all << "module " << symbol.name();
164 } else {
165 auto *parent{details.parent()->symbol()};
166 auto *ancestor{details.ancestor()->symbol()};
167 all << "submodule(" << ancestor->name();
168 if (parent != ancestor) {
169 all << ':' << parent->name();
170 }
171 all << ") " << symbol.name();
172 }
173 all << '\n' << uses_.str();
Caroline Concatto8670e492020-02-28 15:11:03174 uses_.str().clear();
CarolineConcatto64ab3302020-02-25 15:11:52175 all << useExtraAttrs_.str();
Caroline Concatto8670e492020-02-28 15:11:03176 useExtraAttrs_.str().clear();
CarolineConcatto64ab3302020-02-25 15:11:52177 all << decls_.str();
Caroline Concatto8670e492020-02-28 15:11:03178 decls_.str().clear();
CarolineConcatto64ab3302020-02-25 15:11:52179 auto str{contains_.str()};
Caroline Concatto8670e492020-02-28 15:11:03180 contains_.str().clear();
CarolineConcatto64ab3302020-02-25 15:11:52181 if (!str.empty()) {
182 all << "contains\n" << str;
183 }
184 all << "end\n";
185 return all.str();
186}
187
Peter Klausler1bea0342023-10-31 19:17:00188// Collect symbols from initializations that are being referenced directly
189// from other modules; they may require new USE associations.
190static void HarvestInitializerSymbols(
191 SourceOrderedSymbolSet &set, const Scope &scope) {
192 for (const auto &[_, symbol] : scope) {
193 if (symbol->has<DerivedTypeDetails>()) {
194 if (symbol->scope()) {
195 HarvestInitializerSymbols(set, *symbol->scope());
196 }
Peter Klausler7f542662024-01-25 22:53:52197 } else if (const auto &generic{symbol->detailsIf<GenericDetails>()};
198 generic && generic->derivedType()) {
199 const Symbol &dtSym{*generic->derivedType()};
Peter Klausler37180ed2024-01-29 22:36:37200 if (dtSym.has<DerivedTypeDetails>()) {
201 if (dtSym.scope()) {
202 HarvestInitializerSymbols(set, *dtSym.scope());
203 }
204 } else {
205 CHECK(dtSym.has<UseErrorDetails>());
Peter Klausler7f542662024-01-25 22:53:52206 }
Peter Klausler1bea0342023-10-31 19:17:00207 } else if (IsNamedConstant(*symbol) || scope.IsDerivedType()) {
208 if (const auto *object{symbol->detailsIf<ObjectEntityDetails>()}) {
209 if (object->init()) {
210 for (SymbolRef ref : evaluate::CollectSymbols(*object->init())) {
211 set.emplace(*ref);
212 }
213 }
214 } else if (const auto *proc{symbol->detailsIf<ProcEntityDetails>()}) {
215 if (proc->init() && *proc->init()) {
216 set.emplace(**proc->init());
217 }
218 }
219 }
220 }
221}
222
223void ModFileWriter::PrepareRenamings(const Scope &scope) {
224 SourceOrderedSymbolSet symbolsInInits;
225 HarvestInitializerSymbols(symbolsInInits, scope);
226 for (SymbolRef s : symbolsInInits) {
227 const Scope *sMod{FindModuleContaining(s->owner())};
228 if (!sMod) {
229 continue;
230 }
231 SourceName rename{s->name()};
232 if (const Symbol * found{scope.FindSymbol(s->name())}) {
233 if (found == &*s) {
234 continue; // available in scope
235 }
236 if (const auto *generic{found->detailsIf<GenericDetails>()}) {
237 if (generic->derivedType() == &*s || generic->specific() == &*s) {
238 continue;
239 }
240 } else if (found->has<UseDetails>()) {
241 if (&found->GetUltimate() == &*s) {
242 continue; // already use-associated with same name
243 }
244 }
245 if (&s->owner() != &found->owner()) { // Symbol needs renaming
246 rename = scope.context().SaveTempName(
247 DEREF(sMod->symbol()).name().ToString() + "$" +
248 s->name().ToString());
249 }
250 }
251 // Symbol is used in this scope but not visible under its name
252 if (sMod->parent().IsIntrinsicModules()) {
253 uses_ << "use,intrinsic::";
254 } else {
255 uses_ << "use ";
256 }
257 uses_ << DEREF(sMod->symbol()).name() << ",only:";
258 if (rename != s->name()) {
259 uses_ << rename << "=>";
260 }
261 uses_ << s->name() << '\n';
262 useExtraAttrs_ << "private::" << rename << '\n';
263 renamings_.emplace(&*s, rename);
264 }
265}
266
CarolineConcatto64ab3302020-02-25 15:11:52267// Put out the visible symbols from scope.
Peter Klauslerc14cf922021-12-18 00:48:16268void ModFileWriter::PutSymbols(const Scope &scope) {
Tim Keith86f59de2020-12-02 23:13:49269 SymbolVector sorted;
270 SymbolVector uses;
Peter Klausler1bea0342023-10-31 19:17:00271 PrepareRenamings(scope);
Peter Klauslerf7a15e02024-03-01 21:58:36272 UnorderedSymbolSet modules;
273 CollectSymbols(scope, sorted, uses, renamings_, modules);
274 // Write module files for dependencies first so that their
275 // hashes are known.
276 for (auto ref : modules) {
277 Write(*ref);
278 needs_ << ModHeader::need
279 << CheckSumString(ref->get<ModuleDetails>().moduleFileHash().value())
280 << (ref->owner().IsIntrinsicModules() ? " i " : " n ")
281 << ref->name().ToString() << '\n';
282 }
Tim Keith86f59de2020-12-02 23:13:49283 std::string buf; // stuff after CONTAINS in derived type
284 llvm::raw_string_ostream typeBindings{buf};
285 for (const Symbol &symbol : sorted) {
peter klauslerd60a0222021-08-10 17:22:39286 if (!symbol.test(Symbol::Flag::CompilerCreated)) {
287 PutSymbol(typeBindings, symbol);
288 }
CarolineConcatto64ab3302020-02-25 15:11:52289 }
Tim Keith86f59de2020-12-02 23:13:49290 for (const Symbol &symbol : uses) {
291 PutUse(symbol);
292 }
peter klauslerd60a0222021-08-10 17:22:39293 for (const auto &set : scope.equivalenceSets()) {
294 if (!set.empty() &&
295 !set.front().symbol.test(Symbol::Flag::CompilerCreated)) {
296 char punctuation{'('};
297 decls_ << "equivalence";
298 for (const auto &object : set) {
299 decls_ << punctuation << object.AsFortran();
300 punctuation = ',';
301 }
302 decls_ << ")\n";
303 }
304 }
Peter Klauslerc14cf922021-12-18 00:48:16305 CHECK(typeBindings.str().empty());
306}
307
308// Emit components in order
309bool ModFileWriter::PutComponents(const Symbol &typeSymbol) {
310 const auto &scope{DEREF(typeSymbol.scope())};
311 std::string buf; // stuff after CONTAINS in derived type
312 llvm::raw_string_ostream typeBindings{buf};
313 UnorderedSymbolSet emitted;
314 SymbolVector symbols{scope.GetSymbols()};
315 // Emit type parameters first
316 for (const Symbol &symbol : symbols) {
317 if (symbol.has<TypeParamDetails>()) {
318 PutSymbol(typeBindings, symbol);
319 emitted.emplace(symbol);
320 }
321 }
322 // Emit components in component order.
323 const auto &details{typeSymbol.get<DerivedTypeDetails>()};
324 for (SourceName name : details.componentNames()) {
325 auto iter{scope.find(name)};
326 if (iter != scope.end()) {
327 const Symbol &component{*iter->second};
328 if (!component.test(Symbol::Flag::ParentComp)) {
329 PutSymbol(typeBindings, component);
330 }
331 emitted.emplace(component);
332 }
333 }
334 // Emit remaining symbols from the type's scope
335 for (const Symbol &symbol : symbols) {
336 if (emitted.find(symbol) == emitted.end()) {
337 PutSymbol(typeBindings, symbol);
338 }
339 }
CarolineConcatto64ab3302020-02-25 15:11:52340 if (auto str{typeBindings.str()}; !str.empty()) {
341 CHECK(scope.IsDerivedType());
342 decls_ << "contains\n" << str;
peter klausler37b2e2b2020-09-30 20:34:23343 return true;
344 } else {
345 return false;
CarolineConcatto64ab3302020-02-25 15:11:52346 }
347}
348
Slava Zakharin1db42fa2023-09-25 16:35:43349// Return the symbol's attributes that should be written
350// into the mod file.
351static Attrs getSymbolAttrsToWrite(const Symbol &symbol) {
352 // Is SAVE attribute is implicit, it should be omitted
353 // to not violate F202x C862 for a common block member.
354 return symbol.attrs() & ~(symbol.implicitAttrs() & Attrs{Attr::SAVE});
355}
356
peter klausler4864d9f2021-01-13 22:12:23357static llvm::raw_ostream &PutGenericName(
358 llvm::raw_ostream &os, const Symbol &symbol) {
359 if (IsGenericDefinedOp(symbol)) {
360 return os << "operator(" << symbol.name() << ')';
361 } else {
362 return os << symbol.name();
363 }
364}
365
CarolineConcatto64ab3302020-02-25 15:11:52366// Emit a symbol to decls_, except for bindings in a derived type (type-bound
367// procedures, type-bound generics, final procedures) which go to typeBindings.
368void ModFileWriter::PutSymbol(
Caroline Concatto8670e492020-02-28 15:11:03369 llvm::raw_ostream &typeBindings, const Symbol &symbol) {
Peter Klauslercd03e962022-03-23 21:05:50370 common::visit(
371 common::visitors{
372 [&](const ModuleDetails &) { /* should be current module */ },
373 [&](const DerivedTypeDetails &) { PutDerivedType(symbol); },
374 [&](const SubprogramDetails &) { PutSubprogram(symbol); },
375 [&](const GenericDetails &x) {
376 if (symbol.owner().IsDerivedType()) {
377 // generic binding
378 for (const Symbol &proc : x.specificProcs()) {
379 PutGenericName(typeBindings << "generic::", symbol)
380 << "=>" << proc.name() << '\n';
381 }
382 } else {
383 PutGeneric(symbol);
Peter Klauslercd03e962022-03-23 21:05:50384 }
385 },
386 [&](const UseDetails &) { PutUse(symbol); },
387 [](const UseErrorDetails &) {},
388 [&](const ProcBindingDetails &x) {
389 bool deferred{symbol.attrs().test(Attr::DEFERRED)};
390 typeBindings << "procedure";
391 if (deferred) {
392 typeBindings << '(' << x.symbol().name() << ')';
393 }
394 PutPassName(typeBindings, x.passName());
395 auto attrs{symbol.attrs()};
396 if (x.passName()) {
397 attrs.reset(Attr::PASS);
398 }
399 PutAttrs(typeBindings, attrs);
400 typeBindings << "::" << symbol.name();
401 if (!deferred && x.symbol().name() != symbol.name()) {
402 typeBindings << "=>" << x.symbol().name();
403 }
404 typeBindings << '\n';
405 },
406 [&](const NamelistDetails &x) {
407 decls_ << "namelist/" << symbol.name();
408 char sep{'/'};
409 for (const Symbol &object : x.objects()) {
410 decls_ << sep << object.name();
411 sep = ',';
412 }
413 decls_ << '\n';
Peter Klausler4b7428e2022-11-17 21:34:40414 if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) {
415 decls_ << "private::" << symbol.name() << '\n';
416 }
Peter Klauslercd03e962022-03-23 21:05:50417 },
418 [&](const CommonBlockDetails &x) {
419 decls_ << "common/" << symbol.name();
420 char sep = '/';
421 for (const auto &object : x.objects()) {
422 decls_ << sep << object->name();
423 sep = ',';
424 }
425 decls_ << '\n';
426 if (symbol.attrs().test(Attr::BIND_C)) {
Slava Zakharin1db42fa2023-09-25 16:35:43427 PutAttrs(decls_, getSymbolAttrsToWrite(symbol), x.bindName(),
Peter Klausler69e26652023-02-28 19:58:30428 x.isExplicitBindName(), ""s);
Peter Klauslercd03e962022-03-23 21:05:50429 decls_ << "::/" << symbol.name() << "/\n";
430 }
431 },
432 [](const HostAssocDetails &) {},
433 [](const MiscDetails &) {},
434 [&](const auto &) {
435 PutEntity(decls_, symbol);
Valentin Clement22f63b52023-08-04 21:01:45436 PutDirective(decls_, symbol);
Peter Klauslercd03e962022-03-23 21:05:50437 },
438 },
CarolineConcatto64ab3302020-02-25 15:11:52439 symbol.details());
440}
441
Peter Klauslerc14cf922021-12-18 00:48:16442void ModFileWriter::PutDerivedType(
443 const Symbol &typeSymbol, const Scope *scope) {
CarolineConcatto64ab3302020-02-25 15:11:52444 auto &details{typeSymbol.get<DerivedTypeDetails>()};
Peter Klauslerc14cf922021-12-18 00:48:16445 if (details.isDECStructure()) {
446 PutDECStructure(typeSymbol, scope);
447 return;
448 }
CarolineConcatto64ab3302020-02-25 15:11:52449 PutAttrs(decls_ << "type", typeSymbol.attrs());
450 if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
451 decls_ << ",extends(" << extends->name() << ')';
452 }
453 decls_ << "::" << typeSymbol.name();
CarolineConcatto64ab3302020-02-25 15:11:52454 if (!details.paramNames().empty()) {
455 char sep{'('};
456 for (const auto &name : details.paramNames()) {
457 decls_ << sep << name;
458 sep = ',';
459 }
460 decls_ << ')';
461 }
462 decls_ << '\n';
463 if (details.sequence()) {
464 decls_ << "sequence\n";
465 }
Peter Klauslerc14cf922021-12-18 00:48:16466 bool contains{PutComponents(typeSymbol)};
peter klausler37b2e2b2020-09-30 20:34:23467 if (!details.finals().empty()) {
468 const char *sep{contains ? "final::" : "contains\nfinal::"};
469 for (const auto &pair : details.finals()) {
470 decls_ << sep << pair.second->name();
471 sep = ",";
472 }
473 if (*sep == ',') {
474 decls_ << '\n';
475 }
476 }
CarolineConcatto64ab3302020-02-25 15:11:52477 decls_ << "end type\n";
478}
479
Peter Klauslerc14cf922021-12-18 00:48:16480void ModFileWriter::PutDECStructure(
481 const Symbol &typeSymbol, const Scope *scope) {
482 if (emittedDECStructures_.find(typeSymbol) != emittedDECStructures_.end()) {
483 return;
484 }
485 if (!scope && context_.IsTempName(typeSymbol.name().ToString())) {
486 return; // defer until used
487 }
488 emittedDECStructures_.insert(typeSymbol);
489 decls_ << "structure ";
490 if (!context_.IsTempName(typeSymbol.name().ToString())) {
491 decls_ << typeSymbol.name();
492 }
493 if (scope && scope->kind() == Scope::Kind::DerivedType) {
494 // Nested STRUCTURE: emit entity declarations right now
495 // on the STRUCTURE statement.
496 bool any{false};
497 for (const auto &ref : scope->GetSymbols()) {
498 const auto *object{ref->detailsIf<ObjectEntityDetails>()};
499 if (object && object->type() &&
500 object->type()->category() == DeclTypeSpec::TypeDerived &&
501 &object->type()->derivedTypeSpec().typeSymbol() == &typeSymbol) {
502 if (any) {
503 decls_ << ',';
504 } else {
505 any = true;
506 }
507 decls_ << ref->name();
508 PutShape(decls_, object->shape(), '(', ')');
Peter Klausler1bea0342023-10-31 19:17:00509 PutInit(decls_, *ref, object->init(), nullptr, renamings_);
Peter Klauslerc14cf922021-12-18 00:48:16510 emittedDECFields_.insert(*ref);
511 } else if (any) {
512 break; // any later use of this structure will use RECORD/str/
513 }
514 }
515 }
516 decls_ << '\n';
517 PutComponents(typeSymbol);
518 decls_ << "end structure\n";
519}
520
CarolineConcatto64ab3302020-02-25 15:11:52521// Attributes that may be in a subprogram prefix
522static const Attrs subprogramPrefixAttrs{Attr::ELEMENTAL, Attr::IMPURE,
523 Attr::MODULE, Attr::NON_RECURSIVE, Attr::PURE, Attr::RECURSIVE};
524
Valentin Clementc8ad8022024-01-11 05:26:53525static void PutOpenACCDeviceTypeRoutineInfo(
526 llvm::raw_ostream &os, const OpenACCRoutineDeviceTypeInfo &info) {
527 if (info.isSeq()) {
528 os << " seq";
529 }
530 if (info.isGang()) {
531 os << " gang";
532 if (info.gangDim() > 0) {
533 os << "(dim: " << info.gangDim() << ")";
534 }
535 }
536 if (info.isVector()) {
537 os << " vector";
538 }
539 if (info.isWorker()) {
540 os << " worker";
541 }
542 if (info.bindName()) {
543 os << " bind(" << *info.bindName() << ")";
544 }
545}
546
Valentin Clement22209a62023-08-23 15:56:34547static void PutOpenACCRoutineInfo(
548 llvm::raw_ostream &os, const SubprogramDetails &details) {
549 for (auto info : details.openACCRoutineInfos()) {
550 os << "!$acc routine";
Valentin Clementc8ad8022024-01-11 05:26:53551
552 PutOpenACCDeviceTypeRoutineInfo(os, info);
553
Valentin Clement22209a62023-08-23 15:56:34554 if (info.isNohost()) {
555 os << " nohost";
556 }
Valentin Clementc8ad8022024-01-11 05:26:53557
558 for (auto dtype : info.deviceTypeInfos()) {
559 os << " device_type(";
560 if (dtype.dType() == common::OpenACCDeviceType::Star) {
561 os << "*";
562 } else {
563 os << parser::ToLowerCaseLetters(common::EnumToString(dtype.dType()));
564 }
565 os << ")";
566
567 PutOpenACCDeviceTypeRoutineInfo(os, dtype);
Valentin Clement22209a62023-08-23 15:56:34568 }
Valentin Clementc8ad8022024-01-11 05:26:53569
Valentin Clement22209a62023-08-23 15:56:34570 os << "\n";
571 }
572}
573
CarolineConcatto64ab3302020-02-25 15:11:52574void ModFileWriter::PutSubprogram(const Symbol &symbol) {
CarolineConcatto64ab3302020-02-25 15:11:52575 auto &details{symbol.get<SubprogramDetails>()};
Peter Klauslerb67984d2022-06-09 23:06:23576 if (const Symbol * interface{details.moduleInterface()}) {
Peter Klauslerbb7e31b2022-12-01 19:41:40577 const Scope *module{FindModuleContaining(interface->owner())};
578 if (module && module != &symbol.owner()) {
579 // Interface is in ancestor module
580 } else {
581 PutSubprogram(*interface);
582 }
Peter Klauslerb67984d2022-06-09 23:06:23583 }
584 auto attrs{symbol.attrs()};
CarolineConcatto64ab3302020-02-25 15:11:52585 Attrs bindAttrs{};
586 if (attrs.test(Attr::BIND_C)) {
587 // bind(c) is a suffix, not prefix
588 bindAttrs.set(Attr::BIND_C, true);
589 attrs.set(Attr::BIND_C, false);
590 }
Tim Keithd55627d2020-12-28 16:50:30591 bool isAbstract{attrs.test(Attr::ABSTRACT)};
592 if (isAbstract) {
593 attrs.set(Attr::ABSTRACT, false);
594 }
CarolineConcatto64ab3302020-02-25 15:11:52595 Attrs prefixAttrs{subprogramPrefixAttrs & attrs};
596 // emit any non-prefix attributes in an attribute statement
597 attrs &= ~subprogramPrefixAttrs;
Caroline Concatto8670e492020-02-28 15:11:03598 std::string ssBuf;
599 llvm::raw_string_ostream ss{ssBuf};
CarolineConcatto64ab3302020-02-25 15:11:52600 PutAttrs(ss, attrs);
601 if (!ss.str().empty()) {
602 decls_ << ss.str().substr(1) << "::" << symbol.name() << '\n';
603 }
604 bool isInterface{details.isInterface()};
Caroline Concatto8670e492020-02-28 15:11:03605 llvm::raw_ostream &os{isInterface ? decls_ : contains_};
CarolineConcatto64ab3302020-02-25 15:11:52606 if (isInterface) {
Tim Keithd55627d2020-12-28 16:50:30607 os << (isAbstract ? "abstract " : "") << "interface\n";
CarolineConcatto64ab3302020-02-25 15:11:52608 }
Peter Klausler69e26652023-02-28 19:58:30609 PutAttrs(os, prefixAttrs, nullptr, false, ""s, " "s);
Peter Klausler27f71802023-05-06 22:03:39610 if (auto attrs{details.cudaSubprogramAttrs()}) {
611 if (*attrs == common::CUDASubprogramAttrs::HostDevice) {
612 os << "attributes(host,device) ";
613 } else {
614 PutLower(os << "attributes(", common::EnumToString(*attrs)) << ") ";
615 }
616 if (!details.cudaLaunchBounds().empty()) {
617 os << "launch_bounds";
618 char sep{'('};
619 for (auto x : details.cudaLaunchBounds()) {
620 os << sep << x;
621 sep = ',';
622 }
623 os << ") ";
624 }
625 if (!details.cudaClusterDims().empty()) {
626 os << "cluster_dims";
627 char sep{'('};
628 for (auto x : details.cudaClusterDims()) {
629 os << sep << x;
630 sep = ',';
631 }
632 os << ") ";
633 }
634 }
CarolineConcatto64ab3302020-02-25 15:11:52635 os << (details.isFunction() ? "function " : "subroutine ");
636 os << symbol.name() << '(';
637 int n = 0;
638 for (const auto &dummy : details.dummyArgs()) {
639 if (n++ > 0) {
640 os << ',';
641 }
Pete Steinfeld3ed29092020-06-18 14:05:08642 if (dummy) {
643 os << dummy->name();
644 } else {
645 os << "*";
646 }
CarolineConcatto64ab3302020-02-25 15:11:52647 }
648 os << ')';
Peter Klausler69e26652023-02-28 19:58:30649 PutAttrs(os, bindAttrs, details.bindName(), details.isExplicitBindName(),
650 " "s, ""s);
CarolineConcatto64ab3302020-02-25 15:11:52651 if (details.isFunction()) {
652 const Symbol &result{details.result()};
653 if (result.name() != symbol.name()) {
654 os << " result(" << result.name() << ')';
655 }
656 }
657 os << '\n';
peter klauslerc42f6312020-03-19 23:31:10658 // walk symbols, collect ones needed for interface
659 const Scope &scope{
660 details.entryScope() ? *details.entryScope() : DEREF(symbol.scope())};
661 SubprogramSymbolCollector collector{symbol, scope};
662 collector.Collect();
Caroline Concatto8670e492020-02-28 15:11:03663 std::string typeBindingsBuf;
664 llvm::raw_string_ostream typeBindings{typeBindingsBuf};
peter klauslerc42f6312020-03-19 23:31:10665 ModFileWriter writer{context_};
CarolineConcatto64ab3302020-02-25 15:11:52666 for (const Symbol &need : collector.symbols()) {
667 writer.PutSymbol(typeBindings, need);
668 }
669 CHECK(typeBindings.str().empty());
670 os << writer.uses_.str();
671 for (const SourceName &import : collector.imports()) {
672 decls_ << "import::" << import << "\n";
673 }
674 os << writer.decls_.str();
Valentin Clement22209a62023-08-23 15:56:34675 PutOpenACCRoutineInfo(os, details);
CarolineConcatto64ab3302020-02-25 15:11:52676 os << "end\n";
677 if (isInterface) {
678 os << "end interface\n";
679 }
680}
681
682static bool IsIntrinsicOp(const Symbol &symbol) {
683 if (const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()}) {
684 return details->kind().IsIntrinsicOperator();
685 } else {
686 return false;
687 }
688}
689
CarolineConcatto64ab3302020-02-25 15:11:52690void ModFileWriter::PutGeneric(const Symbol &symbol) {
Tim Keith86f59de2020-12-02 23:13:49691 const auto &genericOwner{symbol.owner()};
CarolineConcatto64ab3302020-02-25 15:11:52692 auto &details{symbol.get<GenericDetails>()};
693 PutGenericName(decls_ << "interface ", symbol) << '\n';
694 for (const Symbol &specific : details.specificProcs()) {
Tim Keith86f59de2020-12-02 23:13:49695 if (specific.owner() == genericOwner) {
696 decls_ << "procedure::" << specific.name() << '\n';
697 }
CarolineConcatto64ab3302020-02-25 15:11:52698 }
699 decls_ << "end interface\n";
Peter Klausler4b7428e2022-11-17 21:34:40700 if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) {
CarolineConcatto64ab3302020-02-25 15:11:52701 PutGenericName(decls_ << "private::", symbol) << '\n';
702 }
703}
704
705void ModFileWriter::PutUse(const Symbol &symbol) {
706 auto &details{symbol.get<UseDetails>()};
707 auto &use{details.symbol()};
Peter Klausler15faac92022-05-30 19:47:32708 const Symbol &module{GetUsedModule(details)};
709 if (use.owner().parent().IsIntrinsicModules()) {
710 uses_ << "use,intrinsic::";
711 } else {
712 uses_ << "use ";
713 }
714 uses_ << module.name() << ",only:";
715 PutGenericName(uses_, symbol);
CarolineConcatto64ab3302020-02-25 15:11:52716 // Can have intrinsic op with different local-name and use-name
717 // (e.g. `operator(<)` and `operator(.lt.)`) but rename is not allowed
718 if (!IsIntrinsicOp(symbol) && use.name() != symbol.name()) {
719 PutGenericName(uses_ << "=>", use);
720 }
721 uses_ << '\n';
722 PutUseExtraAttr(Attr::VOLATILE, symbol, use);
723 PutUseExtraAttr(Attr::ASYNCHRONOUS, symbol, use);
Peter Klausler4b7428e2022-11-17 21:34:40724 if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) {
peter klausler4864d9f2021-01-13 22:12:23725 PutGenericName(useExtraAttrs_ << "private::", symbol) << '\n';
726 }
CarolineConcatto64ab3302020-02-25 15:11:52727}
728
729// We have "USE local => use" in this module. If attr was added locally
730// (i.e. on local but not on use), also write it out in the mod file.
731void ModFileWriter::PutUseExtraAttr(
732 Attr attr, const Symbol &local, const Symbol &use) {
733 if (local.attrs().test(attr) && !use.attrs().test(attr)) {
734 PutAttr(useExtraAttrs_, attr) << "::";
735 useExtraAttrs_ << local.name() << '\n';
736 }
737}
738
peter klausler4864d9f2021-01-13 22:12:23739static inline SourceName NameInModuleFile(const Symbol &symbol) {
Peter Klauslera3e9d3c2023-08-04 21:37:34740 if (const auto *use{symbol.detailsIf<UseDetails>()}) {
peter klausler4864d9f2021-01-13 22:12:23741 if (use->symbol().attrs().test(Attr::PRIVATE)) {
742 // Avoid the use in sorting of names created to access private
743 // specific procedures as a result of generic resolution;
744 // they're not in the cooked source.
745 return use->symbol().name();
746 }
747 }
748 return symbol.name();
749}
750
CarolineConcatto64ab3302020-02-25 15:11:52751// Collect the symbols of this scope sorted by their original order, not name.
Peter Klauslera3e9d3c2023-08-04 21:37:34752// Generics and namelists are exceptions: they are sorted after other symbols.
Peter Klausler1bea0342023-10-31 19:17:00753void CollectSymbols(const Scope &scope, SymbolVector &sorted,
Peter Klauslerf7a15e02024-03-01 21:58:36754 SymbolVector &uses, std::map<const Symbol *, SourceName> &renamings,
755 UnorderedSymbolSet &modules) {
Peter Klauslera3e9d3c2023-08-04 21:37:34756 SymbolVector namelist, generics;
Tim Keithc353ebb2020-04-22 22:39:24757 auto symbols{scope.GetSymbols()};
Peter Klausler1bea0342023-10-31 19:17:00758 std::size_t commonSize{scope.commonBlocks().size()};
Tim Keithc353ebb2020-04-22 22:39:24759 sorted.reserve(symbols.size() + commonSize);
760 for (SymbolRef symbol : symbols) {
Peter Klauslerf7a15e02024-03-01 21:58:36761 const auto *generic{symbol->detailsIf<GenericDetails>()};
762 if (generic) {
763 uses.insert(uses.end(), generic->uses().begin(), generic->uses().end());
764 for (auto ref : generic->uses()) {
765 modules.insert(GetUsedModule(ref->get<UseDetails>()));
766 }
767 } else if (const auto *use{symbol->detailsIf<UseDetails>()}) {
768 modules.insert(GetUsedModule(*use));
769 }
Peter Klausler1bea0342023-10-31 19:17:00770 if (symbol->test(Symbol::Flag::ParentComp)) {
771 } else if (symbol->has<NamelistDetails>()) {
772 namelist.push_back(symbol);
Peter Klauslerf7a15e02024-03-01 21:58:36773 } else if (generic) {
Peter Klausler1bea0342023-10-31 19:17:00774 if (generic->specific() &&
775 &generic->specific()->owner() == &symbol->owner()) {
776 sorted.push_back(*generic->specific());
777 } else if (generic->derivedType() &&
778 &generic->derivedType()->owner() == &symbol->owner()) {
779 sorted.push_back(*generic->derivedType());
CarolineConcatto64ab3302020-02-25 15:11:52780 }
Peter Klausler1bea0342023-10-31 19:17:00781 generics.push_back(symbol);
782 } else {
783 sorted.push_back(symbol);
784 }
CarolineConcatto64ab3302020-02-25 15:11:52785 }
Tim Keithd55627d2020-12-28 16:50:30786 // Sort most symbols by name: use of Symbol::ReplaceName ensures the source
787 // location of a symbol's name is the first "real" use.
Peter Klauslera3e9d3c2023-08-04 21:37:34788 auto sorter{[](SymbolRef x, SymbolRef y) {
789 return NameInModuleFile(*x).begin() < NameInModuleFile(*y).begin();
790 }};
791 std::sort(sorted.begin(), sorted.end(), sorter);
792 std::sort(generics.begin(), generics.end(), sorter);
793 sorted.insert(sorted.end(), generics.begin(), generics.end());
Tim Keithc353ebb2020-04-22 22:39:24794 sorted.insert(sorted.end(), namelist.begin(), namelist.end());
CarolineConcatto64ab3302020-02-25 15:11:52795 for (const auto &pair : scope.commonBlocks()) {
Tim Keithc353ebb2020-04-22 22:39:24796 sorted.push_back(*pair.second);
CarolineConcatto64ab3302020-02-25 15:11:52797 }
peter klausler0d8331c2021-03-18 17:26:23798 std::sort(
799 sorted.end() - commonSize, sorted.end(), SymbolSourcePositionCompare{});
CarolineConcatto64ab3302020-02-25 15:11:52800}
801
Peter Klauslerc14cf922021-12-18 00:48:16802void ModFileWriter::PutEntity(llvm::raw_ostream &os, const Symbol &symbol) {
Peter Klauslercd03e962022-03-23 21:05:50803 common::visit(
CarolineConcatto64ab3302020-02-25 15:11:52804 common::visitors{
805 [&](const ObjectEntityDetails &) { PutObjectEntity(os, symbol); },
806 [&](const ProcEntityDetails &) { PutProcEntity(os, symbol); },
807 [&](const TypeParamDetails &) { PutTypeParam(os, symbol); },
808 [&](const auto &) {
809 common::die("PutEntity: unexpected details: %s",
810 DetailsToString(symbol.details()).c_str());
811 },
812 },
813 symbol.details());
814}
815
Caroline Concatto8670e492020-02-28 15:11:03816void PutShapeSpec(llvm::raw_ostream &os, const ShapeSpec &x) {
Peter Klausler44bc97c2021-11-26 21:26:50817 if (x.lbound().isStar()) {
818 CHECK(x.ubound().isStar());
819 os << ".."; // assumed rank
CarolineConcatto64ab3302020-02-25 15:11:52820 } else {
Peter Klausler44bc97c2021-11-26 21:26:50821 if (!x.lbound().isColon()) {
CarolineConcatto64ab3302020-02-25 15:11:52822 PutBound(os, x.lbound());
823 }
824 os << ':';
Peter Klausler44bc97c2021-11-26 21:26:50825 if (!x.ubound().isColon()) {
CarolineConcatto64ab3302020-02-25 15:11:52826 PutBound(os, x.ubound());
827 }
828 }
829}
Caroline Concatto8670e492020-02-28 15:11:03830void PutShape(
831 llvm::raw_ostream &os, const ArraySpec &shape, char open, char close) {
CarolineConcatto64ab3302020-02-25 15:11:52832 if (!shape.empty()) {
833 os << open;
834 bool first{true};
835 for (const auto &shapeSpec : shape) {
836 if (first) {
837 first = false;
838 } else {
839 os << ',';
840 }
841 PutShapeSpec(os, shapeSpec);
842 }
843 os << close;
844 }
845}
846
Peter Klauslerc14cf922021-12-18 00:48:16847void ModFileWriter::PutObjectEntity(
848 llvm::raw_ostream &os, const Symbol &symbol) {
CarolineConcatto64ab3302020-02-25 15:11:52849 auto &details{symbol.get<ObjectEntityDetails>()};
Peter Klauslerc14cf922021-12-18 00:48:16850 if (details.type() &&
851 details.type()->category() == DeclTypeSpec::TypeDerived) {
852 const Symbol &typeSymbol{details.type()->derivedTypeSpec().typeSymbol()};
853 if (typeSymbol.get<DerivedTypeDetails>().isDECStructure()) {
854 PutDerivedType(typeSymbol, &symbol.owner());
855 if (emittedDECFields_.find(symbol) != emittedDECFields_.end()) {
856 return; // symbol was emitted on STRUCTURE statement
857 }
858 }
859 }
Tim Keith1f879002020-03-29 04:00:16860 PutEntity(
861 os, symbol, [&]() { PutType(os, DEREF(symbol.GetType())); },
Slava Zakharin1db42fa2023-09-25 16:35:43862 getSymbolAttrsToWrite(symbol));
CarolineConcatto64ab3302020-02-25 15:11:52863 PutShape(os, details.shape(), '(', ')');
864 PutShape(os, details.coshape(), '[', ']');
Peter Klausler1bea0342023-10-31 19:17:00865 PutInit(os, symbol, details.init(), details.unanalyzedPDTComponentInit(),
866 renamings_);
CarolineConcatto64ab3302020-02-25 15:11:52867 os << '\n';
Peter Klausler864cb2a2023-04-10 18:05:03868 if (auto tkr{GetIgnoreTKR(symbol)}; !tkr.empty()) {
869 os << "!dir$ ignore_tkr(";
870 tkr.IterateOverMembers([&](common::IgnoreTKR tkr) {
871 switch (tkr) {
872 SWITCH_COVERS_ALL_CASES
873 case common::IgnoreTKR::Type:
874 os << 't';
875 break;
876 case common::IgnoreTKR::Kind:
877 os << 'k';
878 break;
879 case common::IgnoreTKR::Rank:
880 os << 'r';
881 break;
882 case common::IgnoreTKR::Device:
883 os << 'd';
884 break;
885 case common::IgnoreTKR::Managed:
886 os << 'm';
887 break;
888 case common::IgnoreTKR::Contiguous:
889 os << 'c';
890 break;
891 }
892 });
893 os << ") " << symbol.name() << '\n';
894 }
Peter Klausler27f71802023-05-06 22:03:39895 if (auto attr{details.cudaDataAttr()}) {
896 PutLower(os << "attributes(", common::EnumToString(*attr))
897 << ") " << symbol.name() << '\n';
898 }
kkwli602e5092023-09-12 20:51:43899 if (symbol.test(Fortran::semantics::Symbol::Flag::CrayPointer)) {
900 if (!symbol.owner().crayPointers().empty()) {
901 for (const auto &[pointee, pointer] : symbol.owner().crayPointers()) {
902 if (pointer == symbol) {
903 os << "pointer(" << symbol.name() << "," << pointee << ")\n";
904 }
905 }
906 }
907 }
CarolineConcatto64ab3302020-02-25 15:11:52908}
909
Peter Klauslerc14cf922021-12-18 00:48:16910void ModFileWriter::PutProcEntity(llvm::raw_ostream &os, const Symbol &symbol) {
CarolineConcatto64ab3302020-02-25 15:11:52911 if (symbol.attrs().test(Attr::INTRINSIC)) {
912 os << "intrinsic::" << symbol.name() << '\n';
Peter Klausler4b7428e2022-11-17 21:34:40913 if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) {
peter klausler8f161012021-04-07 20:21:10914 os << "private::" << symbol.name() << '\n';
915 }
CarolineConcatto64ab3302020-02-25 15:11:52916 return;
917 }
918 const auto &details{symbol.get<ProcEntityDetails>()};
CarolineConcatto64ab3302020-02-25 15:11:52919 Attrs attrs{symbol.attrs()};
920 if (details.passName()) {
921 attrs.reset(Attr::PASS);
922 }
Tim Keith1f879002020-03-29 04:00:16923 PutEntity(
924 os, symbol,
CarolineConcatto64ab3302020-02-25 15:11:52925 [&]() {
926 os << "procedure(";
Peter Klausler83ca78d2024-03-05 20:00:46927 if (details.rawProcInterface()) {
928 os << details.rawProcInterface()->name();
Peter Klausler635656f2022-12-16 17:54:55929 } else if (details.type()) {
930 PutType(os, *details.type());
CarolineConcatto64ab3302020-02-25 15:11:52931 }
932 os << ')';
933 PutPassName(os, details.passName());
934 },
935 attrs);
936 os << '\n';
937}
938
Caroline Concatto8670e492020-02-28 15:11:03939void PutPassName(
940 llvm::raw_ostream &os, const std::optional<SourceName> &passName) {
CarolineConcatto64ab3302020-02-25 15:11:52941 if (passName) {
942 os << ",pass(" << *passName << ')';
943 }
944}
Peter Klauslerc14cf922021-12-18 00:48:16945
946void ModFileWriter::PutTypeParam(llvm::raw_ostream &os, const Symbol &symbol) {
CarolineConcatto64ab3302020-02-25 15:11:52947 auto &details{symbol.get<TypeParamDetails>()};
Tim Keith1f879002020-03-29 04:00:16948 PutEntity(
949 os, symbol,
CarolineConcatto64ab3302020-02-25 15:11:52950 [&]() {
951 PutType(os, DEREF(symbol.GetType()));
952 PutLower(os << ',', common::EnumToString(details.attr()));
953 },
954 symbol.attrs());
955 PutInit(os, details.init());
956 os << '\n';
957}
958
Peter Klausler9e7eef92022-04-13 17:39:16959void PutInit(llvm::raw_ostream &os, const Symbol &symbol, const MaybeExpr &init,
Peter Klausler1bea0342023-10-31 19:17:00960 const parser::Expr *unanalyzed,
961 const std::map<const Symbol *, SourceName> &renamings) {
962 if (IsNamedConstant(symbol) || symbol.owner().IsDerivedType()) {
Peter Klausler9e7eef92022-04-13 17:39:16963 const char *assign{symbol.attrs().test(Attr::POINTER) ? "=>" : "="};
964 if (unanalyzed) {
965 parser::Unparse(os << assign, *unanalyzed);
966 } else if (init) {
Peter Klausler1bea0342023-10-31 19:17:00967 if (const auto *dtConst{
968 evaluate::UnwrapExpr<evaluate::Constant<evaluate::SomeDerived>>(
969 *init)}) {
970 const Symbol &dtSym{dtConst->result().derivedTypeSpec().typeSymbol()};
971 if (auto iter{renamings.find(&dtSym)}; iter != renamings.end()) {
972 // Initializer is a constant whose derived type's name has
973 // been brought into scope from a module under a new name
974 // to avoid a conflict.
975 dtConst->AsFortran(os << assign, &iter->second);
976 return;
977 }
978 }
Peter Klausler9e7eef92022-04-13 17:39:16979 init->AsFortran(os << assign);
CarolineConcatto64ab3302020-02-25 15:11:52980 }
981 }
982}
983
Caroline Concatto8670e492020-02-28 15:11:03984void PutInit(llvm::raw_ostream &os, const MaybeIntExpr &init) {
CarolineConcatto64ab3302020-02-25 15:11:52985 if (init) {
986 init->AsFortran(os << '=');
987 }
988}
989
Caroline Concatto8670e492020-02-28 15:11:03990void PutBound(llvm::raw_ostream &os, const Bound &x) {
Peter Klausler44bc97c2021-11-26 21:26:50991 if (x.isStar()) {
CarolineConcatto64ab3302020-02-25 15:11:52992 os << '*';
Peter Klausler44bc97c2021-11-26 21:26:50993 } else if (x.isColon()) {
CarolineConcatto64ab3302020-02-25 15:11:52994 os << ':';
995 } else {
996 x.GetExplicit()->AsFortran(os);
997 }
998}
999
1000// Write an entity (object or procedure) declaration.
1001// writeType is called to write out the type.
Peter Klauslerc14cf922021-12-18 00:48:161002void ModFileWriter::PutEntity(llvm::raw_ostream &os, const Symbol &symbol,
CarolineConcatto64ab3302020-02-25 15:11:521003 std::function<void()> writeType, Attrs attrs) {
1004 writeType();
Peter Klausler69e26652023-02-28 19:58:301005 PutAttrs(os, attrs, symbol.GetBindName(), symbol.GetIsExplicitBindName());
Peter Klauslerc14cf922021-12-18 00:48:161006 if (symbol.owner().kind() == Scope::Kind::DerivedType &&
1007 context_.IsTempName(symbol.name().ToString())) {
1008 os << "::%FILL";
1009 } else {
1010 os << "::" << symbol.name();
1011 }
CarolineConcatto64ab3302020-02-25 15:11:521012}
1013
1014// Put out each attribute to os, surrounded by `before` and `after` and
1015// mapped to lower case.
Peter Klausler4b7428e2022-11-17 21:34:401016llvm::raw_ostream &ModFileWriter::PutAttrs(llvm::raw_ostream &os, Attrs attrs,
Peter Klausler69e26652023-02-28 19:58:301017 const std::string *bindName, bool isExplicitBindName, std::string before,
1018 std::string after) const {
Tim Keith1f879002020-03-29 04:00:161019 attrs.set(Attr::PUBLIC, false); // no need to write PUBLIC
1020 attrs.set(Attr::EXTERNAL, false); // no need to write EXTERNAL
Peter Klausler4b7428e2022-11-17 21:34:401021 if (isSubmodule_) {
1022 attrs.set(Attr::PRIVATE, false);
1023 }
Peter Klausler69e26652023-02-28 19:58:301024 if (bindName || isExplicitBindName) {
1025 os << before << "bind(c";
1026 if (isExplicitBindName) {
1027 os << ",name=\"" << (bindName ? *bindName : ""s) << '"';
1028 }
1029 os << ')' << after;
CarolineConcatto64ab3302020-02-25 15:11:521030 attrs.set(Attr::BIND_C, false);
1031 }
1032 for (std::size_t i{0}; i < Attr_enumSize; ++i) {
1033 Attr attr{static_cast<Attr>(i)};
1034 if (attrs.test(attr)) {
1035 PutAttr(os << before, attr) << after;
1036 }
1037 }
1038 return os;
1039}
1040
Caroline Concatto8670e492020-02-28 15:11:031041llvm::raw_ostream &PutAttr(llvm::raw_ostream &os, Attr attr) {
CarolineConcatto64ab3302020-02-25 15:11:521042 return PutLower(os, AttrToString(attr));
1043}
1044
Caroline Concatto8670e492020-02-28 15:11:031045llvm::raw_ostream &PutType(llvm::raw_ostream &os, const DeclTypeSpec &type) {
CarolineConcatto64ab3302020-02-25 15:11:521046 return PutLower(os, type.AsFortran());
1047}
1048
Peter Klauslerbcba39a52022-11-11 01:29:291049llvm::raw_ostream &PutLower(llvm::raw_ostream &os, std::string_view str) {
CarolineConcatto64ab3302020-02-25 15:11:521050 for (char c : str) {
1051 os << parser::ToLowerCaseLetter(c);
1052 }
1053 return os;
1054}
1055
Valentin Clement68f36102023-08-04 21:42:381056void PutOpenACCDirective(llvm::raw_ostream &os, const Symbol &symbol) {
Valentin Clement22f63b52023-08-04 21:01:451057 if (symbol.test(Symbol::Flag::AccDeclare)) {
1058 os << "!$acc declare ";
1059 if (symbol.test(Symbol::Flag::AccCopy)) {
1060 os << "copy";
Valentin Clementa749b322023-08-07 16:52:441061 } else if (symbol.test(Symbol::Flag::AccCopyIn) ||
1062 symbol.test(Symbol::Flag::AccCopyInReadOnly)) {
Valentin Clement22f63b52023-08-04 21:01:451063 os << "copyin";
1064 } else if (symbol.test(Symbol::Flag::AccCopyOut)) {
1065 os << "copyout";
1066 } else if (symbol.test(Symbol::Flag::AccCreate)) {
1067 os << "create";
1068 } else if (symbol.test(Symbol::Flag::AccPresent)) {
1069 os << "present";
1070 } else if (symbol.test(Symbol::Flag::AccDevicePtr)) {
1071 os << "deviceptr";
1072 } else if (symbol.test(Symbol::Flag::AccDeviceResident)) {
1073 os << "device_resident";
1074 } else if (symbol.test(Symbol::Flag::AccLink)) {
1075 os << "link";
1076 }
Valentin Clementa749b322023-08-07 16:52:441077 os << "(";
1078 if (symbol.test(Symbol::Flag::AccCopyInReadOnly)) {
1079 os << "readonly: ";
1080 }
1081 os << symbol.name() << ")\n";
Valentin Clement22f63b52023-08-04 21:01:451082 }
1083}
1084
Valentin Clement68f36102023-08-04 21:42:381085void PutOpenMPDirective(llvm::raw_ostream &os, const Symbol &symbol) {
1086 if (symbol.test(Symbol::Flag::OmpThreadprivate)) {
1087 os << "!$omp threadprivate(" << symbol.name() << ")\n";
1088 }
1089}
1090
1091void ModFileWriter::PutDirective(llvm::raw_ostream &os, const Symbol &symbol) {
1092 PutOpenACCDirective(os, symbol);
1093 PutOpenMPDirective(os, symbol);
1094}
1095
CarolineConcatto64ab3302020-02-25 15:11:521096struct Temp {
Steve Scalpone61106302020-03-05 15:09:291097 Temp(int fd, std::string path) : fd{fd}, path{path} {}
David Truby0855c452020-02-25 15:59:501098 Temp(Temp &&t) : fd{std::exchange(t.fd, -1)}, path{std::move(t.path)} {}
CarolineConcatto64ab3302020-02-25 15:11:521099 ~Temp() {
David Truby0855c452020-02-25 15:59:501100 if (fd >= 0) {
Steve Scalpone61106302020-03-05 15:09:291101 llvm::sys::fs::file_t native{llvm::sys::fs::convertFDToNativeFile(fd)};
1102 llvm::sys::fs::closeFile(native);
David Truby0855c452020-02-25 15:59:501103 llvm::sys::fs::remove(path.c_str());
1104 }
CarolineConcatto64ab3302020-02-25 15:11:521105 }
Steve Scalpone61106302020-03-05 15:09:291106 int fd;
CarolineConcatto64ab3302020-02-25 15:11:521107 std::string path;
1108};
1109
1110// Create a temp file in the same directory and with the same suffix as path.
1111// Return an open file descriptor and its path.
David Truby0855c452020-02-25 15:59:501112static llvm::ErrorOr<Temp> MkTemp(const std::string &path) {
CarolineConcatto64ab3302020-02-25 15:11:521113 auto length{path.length()};
1114 auto dot{path.find_last_of("./")};
David Truby0855c452020-02-25 15:59:501115 std::string suffix{
1116 dot < length && path[dot] == '.' ? path.substr(dot + 1) : ""};
CarolineConcatto64ab3302020-02-25 15:11:521117 CHECK(length > suffix.length() &&
1118 path.substr(length - suffix.length()) == suffix);
David Truby0855c452020-02-25 15:59:501119 auto prefix{path.substr(0, length - suffix.length())};
Steve Scalpone61106302020-03-05 15:09:291120 int fd;
David Truby0855c452020-02-25 15:59:501121 llvm::SmallString<16> tempPath;
1122 if (std::error_code err{llvm::sys::fs::createUniqueFile(
1123 prefix + "%%%%%%" + suffix, fd, tempPath)}) {
1124 return err;
1125 }
1126 return Temp{fd, tempPath.c_str()};
CarolineConcatto64ab3302020-02-25 15:11:521127}
1128
1129// Write the module file at path, prepending header. If an error occurs,
1130// return errno, otherwise 0.
Peter Klauslerf7a15e02024-03-01 21:58:361131static std::error_code WriteFile(const std::string &path,
1132 const std::string &contents, ModuleCheckSumType &checkSum, bool debug) {
1133 checkSum = ComputeCheckSum(contents);
CarolineConcatto64ab3302020-02-25 15:11:521134 auto header{std::string{ModHeader::bom} + ModHeader::magic +
Peter Klauslerf7a15e02024-03-01 21:58:361135 CheckSumString(checkSum) + ModHeader::terminator};
David Truby0855c452020-02-25 15:59:501136 if (debug) {
1137 llvm::dbgs() << "Processing module " << path << ": ";
1138 }
CarolineConcatto64ab3302020-02-25 15:11:521139 if (FileContentsMatch(path, header, contents)) {
David Truby0855c452020-02-25 15:59:501140 if (debug) {
1141 llvm::dbgs() << "module unchanged, not writing\n";
1142 }
1143 return {};
CarolineConcatto64ab3302020-02-25 15:11:521144 }
David Truby0855c452020-02-25 15:59:501145 llvm::ErrorOr<Temp> temp{MkTemp(path)};
1146 if (!temp) {
1147 return temp.getError();
CarolineConcatto64ab3302020-02-25 15:11:521148 }
David Truby0855c452020-02-25 15:59:501149 llvm::raw_fd_ostream writer(temp->fd, /*shouldClose=*/false);
1150 writer << header;
1151 writer << contents;
1152 writer.flush();
1153 if (writer.has_error()) {
1154 return writer.error();
CarolineConcatto64ab3302020-02-25 15:11:521155 }
David Truby0855c452020-02-25 15:59:501156 if (debug) {
1157 llvm::dbgs() << "module written\n";
CarolineConcatto64ab3302020-02-25 15:11:521158 }
David Truby0855c452020-02-25 15:59:501159 return llvm::sys::fs::rename(temp->path, path);
CarolineConcatto64ab3302020-02-25 15:11:521160}
1161
1162// Return true if the stream matches what we would write for the mod file.
1163static bool FileContentsMatch(const std::string &path,
1164 const std::string &header, const std::string &contents) {
1165 std::size_t hsize{header.size()};
1166 std::size_t csize{contents.size()};
David Truby0855c452020-02-25 15:59:501167 auto buf_or{llvm::MemoryBuffer::getFile(path)};
1168 if (!buf_or) {
CarolineConcatto64ab3302020-02-25 15:11:521169 return false;
1170 }
David Truby0855c452020-02-25 15:59:501171 auto buf = std::move(buf_or.get());
1172 if (buf->getBufferSize() != hsize + csize) {
CarolineConcatto64ab3302020-02-25 15:11:521173 return false;
1174 }
David Truby0855c452020-02-25 15:59:501175 if (!std::equal(header.begin(), header.end(), buf->getBufferStart(),
1176 buf->getBufferStart() + hsize)) {
1177 return false;
CarolineConcatto64ab3302020-02-25 15:11:521178 }
David Truby0855c452020-02-25 15:59:501179
1180 return std::equal(contents.begin(), contents.end(),
1181 buf->getBufferStart() + hsize, buf->getBufferEnd());
CarolineConcatto64ab3302020-02-25 15:11:521182}
1183
1184// Compute a simple hash of the contents of a module file and
1185// return it as a string of hex digits.
1186// This uses the Fowler-Noll-Vo hash function.
Peter Klauslerf7a15e02024-03-01 21:58:361187static ModuleCheckSumType ComputeCheckSum(const std::string_view &contents) {
1188 ModuleCheckSumType hash{0xcbf29ce484222325ull};
CarolineConcatto64ab3302020-02-25 15:11:521189 for (char c : contents) {
1190 hash ^= c & 0xff;
1191 hash *= 0x100000001b3;
1192 }
Peter Klauslerf7a15e02024-03-01 21:58:361193 return hash;
1194}
1195
1196static std::string CheckSumString(ModuleCheckSumType hash) {
CarolineConcatto64ab3302020-02-25 15:11:521197 static const char *digits = "0123456789abcdef";
1198 std::string result(ModHeader::sumLen, '0');
1199 for (size_t i{ModHeader::sumLen}; hash != 0; hash >>= 4) {
1200 result[--i] = digits[hash & 0xf];
1201 }
1202 return result;
1203}
1204
Peter Klauslerf7a15e02024-03-01 21:58:361205std::optional<ModuleCheckSumType> ExtractCheckSum(const std::string_view &str) {
1206 if (str.size() == ModHeader::sumLen) {
1207 ModuleCheckSumType hash{0};
1208 for (size_t j{0}; j < ModHeader::sumLen; ++j) {
1209 hash <<= 4;
1210 char ch{str.at(j)};
1211 if (ch >= '0' && ch <= '9') {
1212 hash += ch - '0';
1213 } else if (ch >= 'a' && ch <= 'f') {
1214 hash += ch - 'a' + 10;
1215 } else {
1216 return std::nullopt;
1217 }
1218 }
1219 return hash;
CarolineConcatto64ab3302020-02-25 15:11:521220 }
Peter Klauslerf7a15e02024-03-01 21:58:361221 return std::nullopt;
CarolineConcatto64ab3302020-02-25 15:11:521222}
1223
Peter Klauslerf7a15e02024-03-01 21:58:361224static std::optional<ModuleCheckSumType> VerifyHeader(
1225 llvm::ArrayRef<char> content) {
1226 std::string_view sv{content.data(), content.size()};
1227 if (sv.substr(0, ModHeader::magicLen) != ModHeader::magic) {
1228 return std::nullopt;
1229 }
1230 ModuleCheckSumType checkSum{ComputeCheckSum(sv.substr(ModHeader::len))};
1231 std::string_view expectSum{sv.substr(ModHeader::magicLen, ModHeader::sumLen)};
1232 if (auto extracted{ExtractCheckSum(expectSum)};
1233 extracted && *extracted == checkSum) {
1234 return checkSum;
1235 } else {
1236 return std::nullopt;
1237 }
1238}
1239
1240static void GetModuleDependences(
1241 ModuleDependences &dependences, llvm::ArrayRef<char> content) {
1242 std::size_t limit{content.size()};
1243 std::string_view str{content.data(), limit};
1244 for (std::size_t j{ModHeader::len};
Peter Klausler56611882024-03-13 21:42:401245 str.substr(j, ModHeader::needLen) == ModHeader::need; ++j) {
Peter Klauslerf7a15e02024-03-01 21:58:361246 j += 7;
1247 auto checkSum{ExtractCheckSum(str.substr(j, ModHeader::sumLen))};
1248 if (!checkSum) {
1249 break;
1250 }
1251 j += ModHeader::sumLen;
1252 bool intrinsic{false};
1253 if (str.substr(j, 3) == " i ") {
1254 intrinsic = true;
1255 } else if (str.substr(j, 3) != " n ") {
1256 break;
1257 }
1258 j += 3;
1259 std::size_t start{j};
1260 for (; j < limit && str.at(j) != '\n'; ++j) {
1261 }
1262 if (j > start && j < limit && str.at(j) == '\n') {
Peter Klausler56611882024-03-13 21:42:401263 std::string depModName{str.substr(start, j - start)};
1264 dependences.AddDependence(std::move(depModName), intrinsic, *checkSum);
Peter Klauslerf7a15e02024-03-01 21:58:361265 } else {
1266 break;
1267 }
1268 }
1269}
1270
1271Scope *ModFileReader::Read(SourceName name, std::optional<bool> isIntrinsic,
1272 Scope *ancestor, bool silent) {
Tim Keith1f879002020-03-29 04:00:161273 std::string ancestorName; // empty for module
Peter Klausler56611882024-03-13 21:42:401274 const Symbol *notAModule{nullptr};
Peter Klausler9f33dd72022-05-11 21:13:501275 bool fatalError{false};
CarolineConcatto64ab3302020-02-25 15:11:521276 if (ancestor) {
1277 if (auto *scope{ancestor->FindSubmodule(name)}) {
1278 return scope;
1279 }
1280 ancestorName = ancestor->GetName().value().ToString();
Peter Klauslerdcbfabb2022-08-16 21:16:291281 }
Peter Klauslerf7a15e02024-03-01 21:58:361282 auto requiredHash{context_.moduleDependences().GetRequiredHash(
1283 name.ToString(), isIntrinsic.value_or(false))};
Peter Klauslerdcbfabb2022-08-16 21:16:291284 if (!isIntrinsic.value_or(false) && !ancestor) {
1285 // Already present in the symbol table as a usable non-intrinsic module?
1286 auto it{context_.globalScope().find(name)};
1287 if (it != context_.globalScope().end()) {
1288 Scope *scope{it->second->scope()};
1289 if (scope->kind() == Scope::Kind::Module) {
Peter Klausler56611882024-03-13 21:42:401290 for (const Symbol *found{scope->symbol()}; found;) {
1291 if (const auto *module{found->detailsIf<ModuleDetails>()}) {
1292 if (!requiredHash ||
1293 *requiredHash ==
1294 module->moduleFileHash().value_or(*requiredHash)) {
1295 return const_cast<Scope *>(found->scope());
Peter Klauslerf7a15e02024-03-01 21:58:361296 }
Peter Klausler56611882024-03-13 21:42:401297 found = module->previous(); // same name, distinct hash
1298 } else {
1299 notAModule = found;
1300 break;
Peter Klauslerf7a15e02024-03-01 21:58:361301 }
1302 }
Peter Klauslerdcbfabb2022-08-16 21:16:291303 } else {
1304 notAModule = scope->symbol();
Peter Klausler52a13462022-01-26 17:54:581305 }
CarolineConcatto64ab3302020-02-25 15:11:521306 }
1307 }
Peter Klausler56611882024-03-13 21:42:401308 if (notAModule) {
1309 // USE, NON_INTRINSIC global name isn't a module?
1310 fatalError = isIntrinsic.has_value();
1311 }
Peter Klauslerdcbfabb2022-08-16 21:16:291312 auto path{ModFileName(name, ancestorName, context_.moduleFileSuffix())};
peter klausler92a54192020-08-31 19:22:241313 parser::Parsing parsing{context_.allCookedSources()};
CarolineConcatto64ab3302020-02-25 15:11:521314 parser::Options options;
1315 options.isModuleFile = true;
1316 options.features.Enable(common::LanguageFeature::BackslashEscapes);
Peixin-Qiao2cbd4fc2022-04-09 05:52:311317 options.features.Enable(common::LanguageFeature::OpenMP);
Peter Klausler27f71802023-05-06 22:03:391318 options.features.Enable(common::LanguageFeature::CUDA);
Peter Klausler9f33dd72022-05-11 21:13:501319 if (!isIntrinsic.value_or(false) && !notAModule) {
Peter Klauslerdcbfabb2022-08-16 21:16:291320 // The search for this module file will scan non-intrinsic module
1321 // directories. If a directory is in both the intrinsic and non-intrinsic
1322 // directory lists, the intrinsic module directory takes precedence.
Peter Klausler52a13462022-01-26 17:54:581323 options.searchDirectories = context_.searchDirectories();
Peter Klausler52a13462022-01-26 17:54:581324 for (const auto &dir : context_.intrinsicModuleDirectories()) {
Shao-Ce SUNf95bdff2023-01-31 16:24:431325 options.searchDirectories.erase(
1326 std::remove(options.searchDirectories.begin(),
1327 options.searchDirectories.end(), dir),
1328 options.searchDirectories.end());
Peter Klausler52a13462022-01-26 17:54:581329 }
Peter Klausler15faac92022-05-30 19:47:321330 options.searchDirectories.insert(options.searchDirectories.begin(), "."s);
Peter Klausler52a13462022-01-26 17:54:581331 }
Peter Klauslerdcbfabb2022-08-16 21:16:291332 bool foundNonIntrinsicModuleFile{false};
1333 if (!isIntrinsic) {
1334 std::list<std::string> searchDirs;
1335 for (const auto &d : options.searchDirectories) {
1336 searchDirs.push_back(d);
1337 }
1338 foundNonIntrinsicModuleFile =
1339 parser::LocateSourceFile(path, searchDirs).has_value();
1340 }
1341 if (isIntrinsic.value_or(!foundNonIntrinsicModuleFile)) {
1342 // Explicitly intrinsic, or not specified and not found in the search
1343 // path; see whether it's already in the symbol table as an intrinsic
1344 // module.
1345 auto it{context_.intrinsicModulesScope().find(name)};
1346 if (it != context_.intrinsicModulesScope().end()) {
1347 return it->second->scope();
1348 }
1349 }
1350 // We don't have this module in the symbol table yet.
1351 // Find its module file and parse it. Define or extend the search
1352 // path with intrinsic module directories, if appropriate.
Peter Klausler52a13462022-01-26 17:54:581353 if (isIntrinsic.value_or(true)) {
1354 for (const auto &dir : context_.intrinsicModuleDirectories()) {
1355 options.searchDirectories.push_back(dir);
1356 }
Peter Klauslerf7a15e02024-03-01 21:58:361357 if (!requiredHash) {
1358 requiredHash =
1359 context_.moduleDependences().GetRequiredHash(name.ToString(), true);
1360 }
Peter Klausler52a13462022-01-26 17:54:581361 }
Peter Klauslerf7a15e02024-03-01 21:58:361362
1363 // Look for the right module file if its hash is known
1364 if (requiredHash && !fatalError) {
Peter Klauslerf7a15e02024-03-01 21:58:361365 for (const std::string &maybe :
1366 parser::LocateSourceFileAll(path, options.searchDirectories)) {
1367 if (const auto *srcFile{context_.allCookedSources().allSources().OpenPath(
1368 maybe, llvm::errs())}) {
Peter Klausler56611882024-03-13 21:42:401369 if (auto checkSum{VerifyHeader(srcFile->content())};
1370 checkSum && *checkSum == *requiredHash) {
1371 path = maybe;
1372 break;
Peter Klauslerf7a15e02024-03-01 21:58:361373 }
1374 }
1375 }
Peter Klauslerf7a15e02024-03-01 21:58:361376 }
Peter Klausler9f33dd72022-05-11 21:13:501377 const auto *sourceFile{fatalError ? nullptr : parsing.Prescan(path, options)};
1378 if (fatalError || parsing.messages().AnyFatalError()) {
peter klausler52711fb2021-09-21 23:06:301379 if (!silent) {
Peter Klausler9f33dd72022-05-11 21:13:501380 if (notAModule) {
1381 // Module is not explicitly INTRINSIC, and there's already a global
1382 // symbol of the same name that is not a module.
1383 context_.SayWithDecl(
1384 *notAModule, name, "'%s' is not a module"_err_en_US, name);
1385 } else {
1386 for (auto &msg : parsing.messages().messages()) {
1387 std::string str{msg.ToString()};
1388 Say(name, ancestorName,
1389 parser::MessageFixedText{str.c_str(), str.size(), msg.severity()},
1390 path);
1391 }
peter klausler52711fb2021-09-21 23:06:301392 }
CarolineConcatto64ab3302020-02-25 15:11:521393 }
1394 return nullptr;
1395 }
1396 CHECK(sourceFile);
Peter Klauslerf7a15e02024-03-01 21:58:361397 std::optional<ModuleCheckSumType> checkSum{
1398 VerifyHeader(sourceFile->content())};
1399 if (!checkSum) {
Peter Klauslera53967c2022-03-07 21:57:371400 Say(name, ancestorName, "File has invalid checksum: %s"_warn_en_US,
CarolineConcatto64ab3302020-02-25 15:11:521401 sourceFile->path());
1402 return nullptr;
Peter Klauslerf7a15e02024-03-01 21:58:361403 } else if (requiredHash && *requiredHash != *checkSum) {
1404 Say(name, ancestorName,
1405 "File is not the right module file for %s"_warn_en_US,
1406 "'"s + name.ToString() + "': "s + sourceFile->path());
1407 return nullptr;
CarolineConcatto64ab3302020-02-25 15:11:521408 }
Caroline Concatto8670e492020-02-28 15:11:031409 llvm::raw_null_ostream NullStream;
1410 parsing.Parse(NullStream);
Peter Klauslerf4bb2112022-04-25 23:00:011411 std::optional<parser::Program> &parsedProgram{parsing.parseTree()};
CarolineConcatto64ab3302020-02-25 15:11:521412 if (!parsing.messages().empty() || !parsing.consumedWholeFile() ||
Peter Klauslerf4bb2112022-04-25 23:00:011413 !parsedProgram) {
CarolineConcatto64ab3302020-02-25 15:11:521414 Say(name, ancestorName, "Module file is corrupt: %s"_err_en_US,
1415 sourceFile->path());
1416 return nullptr;
1417 }
Peter Klauslerf4bb2112022-04-25 23:00:011418 parser::Program &parseTree{context_.SaveParseTree(std::move(*parsedProgram))};
Tim Keith1f879002020-03-29 04:00:161419 Scope *parentScope; // the scope this module/submodule goes into
Peter Klausler52a13462022-01-26 17:54:581420 if (!isIntrinsic.has_value()) {
1421 for (const auto &dir : context_.intrinsicModuleDirectories()) {
1422 if (sourceFile->path().size() > dir.size() &&
1423 sourceFile->path().find(dir) == 0) {
1424 isIntrinsic = true;
1425 break;
1426 }
1427 }
1428 }
1429 Scope &topScope{isIntrinsic.value_or(false) ? context_.intrinsicModulesScope()
1430 : context_.globalScope()};
Peter Klauslere200b0e2023-10-16 21:15:401431 Symbol *moduleSymbol{nullptr};
Peter Klausler56611882024-03-13 21:42:401432 const Symbol *previousModuleSymbol{nullptr};
Peter Klauslere200b0e2023-10-16 21:15:401433 if (!ancestor) { // module, not submodule
Peter Klausler52a13462022-01-26 17:54:581434 parentScope = &topScope;
Peter Klauslere200b0e2023-10-16 21:15:401435 auto pair{parentScope->try_emplace(name, UnknownDetails{})};
1436 if (!pair.second) {
Peter Klausler56611882024-03-13 21:42:401437 // There is already a global symbol or intrinsic module of the same name.
1438 previousModuleSymbol = &*pair.first->second;
1439 if (const auto *details{
1440 previousModuleSymbol->detailsIf<ModuleDetails>()}) {
1441 if (!details->moduleFileHash().has_value()) {
1442 return nullptr;
1443 }
1444 } else {
1445 return nullptr;
1446 }
1447 CHECK(parentScope->erase(name) != 0);
1448 pair = parentScope->try_emplace(name, UnknownDetails{});
1449 CHECK(pair.second);
Peter Klauslere200b0e2023-10-16 21:15:401450 }
1451 moduleSymbol = &*pair.first->second;
1452 moduleSymbol->set(Symbol::Flag::ModFile);
Peter Klauslerf4bb2112022-04-25 23:00:011453 } else if (std::optional<SourceName> parent{GetSubmoduleParent(parseTree)}) {
Peter Klauslere200b0e2023-10-16 21:15:401454 // submodule with submodule parent
Peter Klausler52a13462022-01-26 17:54:581455 parentScope = Read(*parent, false /*not intrinsic*/, ancestor, silent);
CarolineConcatto64ab3302020-02-25 15:11:521456 } else {
Peter Klauslere200b0e2023-10-16 21:15:401457 // submodule with module parent
CarolineConcatto64ab3302020-02-25 15:11:521458 parentScope = ancestor;
1459 }
Peter Klausler73506252022-05-24 22:06:121460 // Process declarations from the module file
Peter Klausler73506252022-05-24 22:06:121461 bool wasInModuleFile{context_.foldingContext().inModuleFile()};
1462 context_.foldingContext().set_inModuleFile(true);
Peter Klauslerf7a15e02024-03-01 21:58:361463 GetModuleDependences(context_.moduleDependences(), sourceFile->content());
Peter Klauslerf4bb2112022-04-25 23:00:011464 ResolveNames(context_, parseTree, topScope);
Peter Klausler73506252022-05-24 22:06:121465 context_.foldingContext().set_inModuleFile(wasInModuleFile);
Peter Klauslere200b0e2023-10-16 21:15:401466 if (!moduleSymbol) {
1467 // Submodule symbols' storage are owned by their parents' scopes,
1468 // but their names are not in their parents' dictionaries -- we
1469 // don't want to report bogus errors about clashes between submodule
1470 // names and other objects in the parent scopes.
1471 if (Scope * submoduleScope{ancestor->FindSubmodule(name)}) {
1472 moduleSymbol = submoduleScope->symbol();
1473 if (moduleSymbol) {
1474 moduleSymbol->set(Symbol::Flag::ModFile);
1475 }
1476 }
Peter Klausler52a13462022-01-26 17:54:581477 }
Peter Klauslere200b0e2023-10-16 21:15:401478 if (moduleSymbol) {
Peter Klauslere200b0e2023-10-16 21:15:401479 CHECK(moduleSymbol->test(Symbol::Flag::ModFile));
Peter Klausler56611882024-03-13 21:42:401480 auto &details{moduleSymbol->get<ModuleDetails>()};
1481 details.set_moduleFileHash(checkSum.value());
1482 details.set_previous(previousModuleSymbol);
Peter Klauslere200b0e2023-10-16 21:15:401483 if (isIntrinsic.value_or(false)) {
1484 moduleSymbol->attrs().set(Attr::INTRINSIC);
1485 }
1486 return moduleSymbol->scope();
1487 } else {
1488 return nullptr;
1489 }
CarolineConcatto64ab3302020-02-25 15:11:521490}
1491
Peter Klauslerf7a15e02024-03-01 21:58:361492parser::Message &ModFileReader::Say(SourceName name,
CarolineConcatto64ab3302020-02-25 15:11:521493 const std::string &ancestor, parser::MessageFixedText &&msg,
1494 const std::string &arg) {
peter klausler0bfa4ac2021-02-11 00:20:591495 return context_.Say(name, "Cannot read module file for %s: %s"_err_en_US,
1496 parser::MessageFormattedText{ancestor.empty()
1497 ? "module '%s'"_en_US
1498 : "submodule '%s' of module '%s'"_en_US,
1499 name, ancestor}
1500 .MoveString(),
1501 parser::MessageFormattedText{std::move(msg), arg}.MoveString());
CarolineConcatto64ab3302020-02-25 15:11:521502}
1503
1504// program was read from a .mod file for a submodule; return the name of the
1505// submodule's parent submodule, nullptr if none.
1506static std::optional<SourceName> GetSubmoduleParent(
1507 const parser::Program &program) {
1508 CHECK(program.v.size() == 1);
1509 auto &unit{program.v.front()};
1510 auto &submod{std::get<common::Indirection<parser::Submodule>>(unit.u)};
1511 auto &stmt{
1512 std::get<parser::Statement<parser::SubmoduleStmt>>(submod.value().t)};
1513 auto &parentId{std::get<parser::ParentIdentifier>(stmt.statement.t)};
1514 if (auto &parent{std::get<std::optional<parser::Name>>(parentId.t)}) {
1515 return parent->source;
1516 } else {
1517 return std::nullopt;
1518 }
1519}
1520
1521void SubprogramSymbolCollector::Collect() {
1522 const auto &details{symbol_.get<SubprogramDetails>()};
1523 isInterface_ = details.isInterface();
1524 for (const Symbol *dummyArg : details.dummyArgs()) {
Pete Steinfeld3ed29092020-06-18 14:05:081525 if (dummyArg) {
1526 DoSymbol(*dummyArg);
1527 }
CarolineConcatto64ab3302020-02-25 15:11:521528 }
1529 if (details.isFunction()) {
1530 DoSymbol(details.result());
1531 }
1532 for (const auto &pair : scope_) {
1533 const Symbol &symbol{*pair.second};
1534 if (const auto *useDetails{symbol.detailsIf<UseDetails>()}) {
Peter Klausler665d4152022-02-25 21:54:441535 const Symbol &ultimate{useDetails->symbol().GetUltimate()};
1536 bool needed{useSet_.count(ultimate) > 0};
1537 if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
1538 // The generic may not be needed itself, but the specific procedure
1539 // &/or derived type that it shadows may be needed.
1540 const Symbol *spec{generic->specific()};
1541 const Symbol *dt{generic->derivedType()};
1542 needed = needed || (spec && useSet_.count(*spec) > 0) ||
1543 (dt && useSet_.count(*dt) > 0);
Peter Klauslerb67984d2022-06-09 23:06:231544 } else if (const auto *subp{ultimate.detailsIf<SubprogramDetails>()}) {
1545 const Symbol *interface { subp->moduleInterface() };
1546 needed = needed || (interface && useSet_.count(*interface) > 0);
Peter Klausler665d4152022-02-25 21:54:441547 }
1548 if (needed) {
CarolineConcatto64ab3302020-02-25 15:11:521549 need_.push_back(symbol);
1550 }
Emil Kierib85922c2022-03-15 17:40:061551 } else if (symbol.has<SubprogramDetails>()) {
1552 // An internal subprogram is needed if it is used as interface
1553 // for a dummy or return value procedure.
1554 bool needed{false};
1555 const auto hasInterface{[&symbol](const Symbol *s) -> bool {
1556 // Is 's' a procedure with interface 'symbol'?
1557 if (s) {
1558 if (const auto *sDetails{s->detailsIf<ProcEntityDetails>()}) {
Peter Klausler635656f2022-12-16 17:54:551559 if (sDetails->procInterface() == &symbol) {
Emil Kierib85922c2022-03-15 17:40:061560 return true;
1561 }
1562 }
1563 }
1564 return false;
1565 }};
1566 for (const Symbol *dummyArg : details.dummyArgs()) {
1567 needed = needed || hasInterface(dummyArg);
1568 }
1569 needed =
1570 needed || (details.isFunction() && hasInterface(&details.result()));
1571 if (needed && needSet_.insert(symbol).second) {
1572 need_.push_back(symbol);
1573 }
CarolineConcatto64ab3302020-02-25 15:11:521574 }
1575 }
1576}
1577
1578void SubprogramSymbolCollector::DoSymbol(const Symbol &symbol) {
1579 DoSymbol(symbol.name(), symbol);
1580}
1581
1582// Do symbols this one depends on; then add to need_
1583void SubprogramSymbolCollector::DoSymbol(
1584 const SourceName &name, const Symbol &symbol) {
1585 const auto &scope{symbol.owner()};
1586 if (scope != scope_ && !scope.IsDerivedType()) {
1587 if (scope != scope_.parent()) {
1588 useSet_.insert(symbol);
1589 }
1590 if (NeedImport(name, symbol)) {
1591 imports_.insert(name);
1592 }
1593 return;
1594 }
1595 if (!needSet_.insert(symbol).second) {
Tim Keith1f879002020-03-29 04:00:161596 return; // already done
CarolineConcatto64ab3302020-02-25 15:11:521597 }
Peter Klauslercd03e962022-03-23 21:05:501598 common::visit(common::visitors{
1599 [this](const ObjectEntityDetails &details) {
1600 for (const ShapeSpec &spec : details.shape()) {
1601 DoBound(spec.lbound());
1602 DoBound(spec.ubound());
1603 }
1604 for (const ShapeSpec &spec : details.coshape()) {
1605 DoBound(spec.lbound());
1606 DoBound(spec.ubound());
1607 }
1608 if (const Symbol * commonBlock{details.commonBlock()}) {
1609 DoSymbol(*commonBlock);
1610 }
1611 },
1612 [this](const CommonBlockDetails &details) {
1613 for (const auto &object : details.objects()) {
1614 DoSymbol(*object);
1615 }
1616 },
Peter Klauslerdf3e5f12022-08-25 17:22:101617 [this](const ProcEntityDetails &details) {
Peter Klausler83ca78d2024-03-05 20:00:461618 if (details.rawProcInterface()) {
1619 DoSymbol(*details.rawProcInterface());
Peter Klausler635656f2022-12-16 17:54:551620 } else {
1621 DoType(details.type());
Peter Klauslerdf3e5f12022-08-25 17:22:101622 }
Peter Klauslerdf3e5f12022-08-25 17:22:101623 },
Peter Klauslerd0708e62024-01-15 19:37:461624 [this](const ProcBindingDetails &details) {
1625 DoSymbol(details.symbol());
1626 },
Peter Klauslercd03e962022-03-23 21:05:501627 [](const auto &) {},
1628 },
CarolineConcatto64ab3302020-02-25 15:11:521629 symbol.details());
1630 if (!symbol.has<UseDetails>()) {
1631 DoType(symbol.GetType());
1632 }
1633 if (!scope.IsDerivedType()) {
1634 need_.push_back(symbol);
1635 }
1636}
1637
1638void SubprogramSymbolCollector::DoType(const DeclTypeSpec *type) {
1639 if (!type) {
1640 return;
1641 }
1642 switch (type->category()) {
1643 case DeclTypeSpec::Numeric:
Tim Keith1f879002020-03-29 04:00:161644 case DeclTypeSpec::Logical:
1645 break; // nothing to do
CarolineConcatto64ab3302020-02-25 15:11:521646 case DeclTypeSpec::Character:
1647 DoParamValue(type->characterTypeSpec().length());
1648 break;
1649 default:
1650 if (const DerivedTypeSpec * derived{type->AsDerived()}) {
1651 const auto &typeSymbol{derived->typeSymbol()};
CarolineConcatto64ab3302020-02-25 15:11:521652 for (const auto &pair : derived->parameters()) {
1653 DoParamValue(pair.second);
1654 }
Peter Klauslerd0708e62024-01-15 19:37:461655 // The components of the type (including its parent component, if
1656 // any) matter to IMPORT symbol collection only for derived types
1657 // defined in the subprogram.
1658 if (typeSymbol.owner() == scope_) {
1659 if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
1660 DoSymbol(extends->name(), extends->typeSymbol());
1661 }
1662 for (const auto &pair : *typeSymbol.scope()) {
1663 DoSymbol(*pair.second);
1664 }
CarolineConcatto64ab3302020-02-25 15:11:521665 }
Peter Klauslerd0708e62024-01-15 19:37:461666 DoSymbol(derived->name(), typeSymbol);
CarolineConcatto64ab3302020-02-25 15:11:521667 }
1668 }
1669}
1670
1671void SubprogramSymbolCollector::DoBound(const Bound &bound) {
1672 if (const MaybeSubscriptIntExpr & expr{bound.GetExplicit()}) {
1673 DoExpr(*expr);
1674 }
1675}
1676void SubprogramSymbolCollector::DoParamValue(const ParamValue &paramValue) {
1677 if (const auto &expr{paramValue.GetExplicit()}) {
1678 DoExpr(*expr);
1679 }
1680}
1681
1682// Do we need a IMPORT of this symbol into an interface block?
1683bool SubprogramSymbolCollector::NeedImport(
1684 const SourceName &name, const Symbol &symbol) {
1685 if (!isInterface_) {
1686 return false;
Peter Klausler9e855a62022-12-02 15:19:491687 } else if (IsSeparateModuleProcedureInterface(&symbol_)) {
1688 return false; // IMPORT needed only for external and dummy procedure
1689 // interfaces
Peter Klauslerdf3e5f12022-08-25 17:22:101690 } else if (&symbol == scope_.symbol()) {
1691 return false;
peter klausler4864d9f2021-01-13 22:12:231692 } else if (symbol.owner().Contains(scope_)) {
CarolineConcatto64ab3302020-02-25 15:11:521693 return true;
Peter Klausler9e855a62022-12-02 15:19:491694 } else if (const Symbol *found{scope_.FindSymbol(name)}) {
peter klausler4864d9f2021-01-13 22:12:231695 // detect import from ancestor of use-associated symbol
1696 return found->has<UseDetails>() && found->owner() != scope_;
1697 } else {
Peter Klauslerd0708e62024-01-15 19:37:461698 // "found" can be null in the case of a use-associated derived type's
1699 // parent type
peter klausler4864d9f2021-01-13 22:12:231700 CHECK(symbol.has<DerivedTypeDetails>());
1701 return false;
CarolineConcatto64ab3302020-02-25 15:11:521702 }
1703}
1704
Tim Keith1f879002020-03-29 04:00:161705} // namespace Fortran::semantics