1 //===-- lib/Semantics/mod-file.cpp ----------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
10 #include "resolve-names.h"
11 #include "flang/Evaluate/tools.h"
12 #include "flang/Parser/message.h"
13 #include "flang/Parser/parsing.h"
14 #include "flang/Semantics/scope.h"
15 #include "flang/Semantics/semantics.h"
16 #include "flang/Semantics/symbol.h"
17 #include "flang/Semantics/tools.h"
18 #include "llvm/Support/FileSystem.h"
19 #include "llvm/Support/MemoryBuffer.h"
20 #include "llvm/Support/raw_ostream.h"
25 #include <string_view>
28 namespace Fortran::semantics {
30 using namespace parser::literals;
32 // The first line of a file that identifies it as a .mod file.
33 // The first three bytes are a Unicode byte order mark that ensures
34 // that the module file is decoded as UTF-8 even if source files
35 // are using another encoding.
37 static constexpr const char bom[3 + 1]{"\xef\xbb\xbf"};
38 static constexpr int magicLen{13};
39 static constexpr int sumLen{16};
40 static constexpr const char magic[magicLen + 1]{"!mod$ v1 sum:"};
41 static constexpr char terminator{'\n'};
42 static constexpr int len{magicLen + 1 + sumLen};
45 static std::optional<SourceName> GetSubmoduleParent(const parser::Program &);
46 static SymbolVector CollectSymbols(const Scope &);
47 static void PutEntity(std::ostream &, const Symbol &);
48 static void PutObjectEntity(std::ostream &, const Symbol &);
49 static void PutProcEntity(std::ostream &, const Symbol &);
50 static void PutPassName(std::ostream &, const std::optional<SourceName> &);
51 static void PutTypeParam(std::ostream &, const Symbol &);
52 static void PutEntity(
53 std::ostream &, const Symbol &, std::function<void()>, Attrs);
54 static void PutInit(std::ostream &, const Symbol &, const MaybeExpr &);
55 static void PutInit(std::ostream &, const MaybeIntExpr &);
56 static void PutBound(std::ostream &, const Bound &);
57 static std::ostream &PutAttrs(std::ostream &, Attrs,
58 const MaybeExpr & = std::nullopt, std::string before = ","s,
59 std::string after = ""s);
60 static std::ostream &PutAttr(std::ostream &, Attr);
61 static std::ostream &PutType(std::ostream &, const DeclTypeSpec &);
62 static std::ostream &PutLower(std::ostream &, const std::string &);
63 static std::error_code WriteFile(
64 const std::string &, const std::string &, bool = true);
65 static bool FileContentsMatch(
66 const std::string &, const std::string &, const std::string &);
67 static std::string CheckSum(const std::string_view &);
69 // Collect symbols needed for a subprogram interface
70 class SubprogramSymbolCollector {
72 SubprogramSymbolCollector(const Symbol &symbol)
73 : symbol_{symbol}, scope_{DEREF(symbol.scope())} {}
74 const SymbolVector &symbols() const { return need_; }
75 const std::set<SourceName> &imports() const { return imports_; }
79 const Symbol &symbol_;
81 bool isInterface_{false};
82 SymbolVector need_; // symbols that are needed
83 SymbolSet needSet_; // symbols already in need_
84 SymbolSet useSet_; // use-associations that might be needed
85 std::set<SourceName> imports_; // imports from host that are needed
87 void DoSymbol(const Symbol &);
88 void DoSymbol(const SourceName &, const Symbol &);
89 void DoType(const DeclTypeSpec *);
90 void DoBound(const Bound &);
91 void DoParamValue(const ParamValue &);
92 bool NeedImport(const SourceName &, const Symbol &);
94 template<typename T> void DoExpr(evaluate::Expr<T> expr) {
95 for (const Symbol &symbol : evaluate::CollectSymbols(expr)) {
101 bool ModFileWriter::WriteAll() {
102 WriteAll(context_.globalScope());
103 return !context_.AnyFatalError();
106 void ModFileWriter::WriteAll(const Scope &scope) {
107 for (const auto &child : scope.children()) {
112 void ModFileWriter::WriteOne(const Scope &scope) {
113 if (scope.kind() == Scope::Kind::Module) {
114 auto *symbol{scope.symbol()};
115 if (!symbol->test(Symbol::Flag::ModFile)) {
118 WriteAll(scope); // write out submodules
122 // Construct the name of a module file. Non-empty ancestorName means submodule.
123 static std::string ModFileName(const SourceName &name,
124 const std::string &ancestorName, const std::string &suffix) {
125 std::string result{name.ToString() + suffix};
126 return ancestorName.empty() ? result : ancestorName + '-' + result;
129 // Write the module file for symbol, which must be a module or submodule.
130 void ModFileWriter::Write(const Symbol &symbol) {
131 auto *ancestor{symbol.get<ModuleDetails>().ancestor()};
132 auto ancestorName{ancestor ? ancestor->GetName().value().ToString() : ""s};
133 auto path{context_.moduleDirectory() + '/' +
134 ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())};
135 PutSymbols(DEREF(symbol.scope()));
136 if (std::error_code error{
137 WriteFile(path, GetAsString(symbol), context_.debugModuleWriter())}) {
139 symbol.name(), "Error writing %s: %s"_err_en_US, path, error.message());
143 // Return the entire body of the module file
144 // and clear saved uses, decls, and contains.
145 std::string ModFileWriter::GetAsString(const Symbol &symbol) {
146 std::stringstream all;
147 auto &details{symbol.get<ModuleDetails>()};
148 if (!details.isSubmodule()) {
149 all << "module " << symbol.name();
151 auto *parent{details.parent()->symbol()};
152 auto *ancestor{details.ancestor()->symbol()};
153 all << "submodule(" << ancestor->name();
154 if (parent != ancestor) {
155 all << ':' << parent->name();
157 all << ") " << symbol.name();
159 all << '\n' << uses_.str();
161 all << useExtraAttrs_.str();
162 useExtraAttrs_.str(""s);
165 auto str{contains_.str()};
168 all << "contains\n" << str;
174 // Put out the visible symbols from scope.
175 void ModFileWriter::PutSymbols(const Scope &scope) {
176 std::stringstream typeBindings; // stuff after CONTAINS in derived type
177 for (const Symbol &symbol : CollectSymbols(scope)) {
178 PutSymbol(typeBindings, symbol);
180 if (auto str{typeBindings.str()}; !str.empty()) {
181 CHECK(scope.IsDerivedType());
182 decls_ << "contains\n" << str;
186 // Emit a symbol to decls_, except for bindings in a derived type (type-bound
187 // procedures, type-bound generics, final procedures) which go to typeBindings.
188 void ModFileWriter::PutSymbol(
189 std::stringstream &typeBindings, const Symbol &symbol) {
192 [&](const ModuleDetails &) { /* should be current module */ },
193 [&](const DerivedTypeDetails &) { PutDerivedType(symbol); },
194 [&](const SubprogramDetails &) { PutSubprogram(symbol); },
195 [&](const GenericDetails &x) {
196 if (symbol.owner().IsDerivedType()) {
198 for (const Symbol &proc : x.specificProcs()) {
199 typeBindings << "generic::" << symbol.name() << "=>"
200 << proc.name() << '\n';
205 PutSymbol(typeBindings, *x.specific());
207 if (x.derivedType()) {
208 PutSymbol(typeBindings, *x.derivedType());
212 [&](const UseDetails &) { PutUse(symbol); },
213 [](const UseErrorDetails &) {},
214 [&](const ProcBindingDetails &x) {
215 bool deferred{symbol.attrs().test(Attr::DEFERRED)};
216 typeBindings << "procedure";
218 typeBindings << '(' << x.symbol().name() << ')';
220 PutPassName(typeBindings, x.passName());
221 auto attrs{symbol.attrs()};
223 attrs.reset(Attr::PASS);
225 PutAttrs(typeBindings, attrs);
226 typeBindings << "::" << symbol.name();
227 if (!deferred && x.symbol().name() != symbol.name()) {
228 typeBindings << "=>" << x.symbol().name();
230 typeBindings << '\n';
232 [&](const NamelistDetails &x) {
233 decls_ << "namelist/" << symbol.name();
235 for (const Symbol &object : x.objects()) {
236 decls_ << sep << object.name();
241 [&](const CommonBlockDetails &x) {
242 decls_ << "common/" << symbol.name();
244 for (const Symbol &object : x.objects()) {
245 decls_ << sep << object.name();
249 if (symbol.attrs().test(Attr::BIND_C)) {
250 PutAttrs(decls_, symbol.attrs(), x.bindName(), ""s);
251 decls_ << "::/" << symbol.name() << "/\n";
254 [&](const FinalProcDetails &) {
255 typeBindings << "final::" << symbol.name() << '\n';
257 [](const HostAssocDetails &) {},
258 [](const MiscDetails &) {},
259 [&](const auto &) { PutEntity(decls_, symbol); },
264 void ModFileWriter::PutDerivedType(const Symbol &typeSymbol) {
265 auto &details{typeSymbol.get<DerivedTypeDetails>()};
266 PutAttrs(decls_ << "type", typeSymbol.attrs());
267 if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
268 decls_ << ",extends(" << extends->name() << ')';
270 decls_ << "::" << typeSymbol.name();
271 auto &typeScope{*typeSymbol.scope()};
272 if (!details.paramNames().empty()) {
274 for (const auto &name : details.paramNames()) {
275 decls_ << sep << name;
281 if (details.sequence()) {
282 decls_ << "sequence\n";
284 PutSymbols(typeScope);
285 decls_ << "end type\n";
288 // Attributes that may be in a subprogram prefix
289 static const Attrs subprogramPrefixAttrs{Attr::ELEMENTAL, Attr::IMPURE,
290 Attr::MODULE, Attr::NON_RECURSIVE, Attr::PURE, Attr::RECURSIVE};
292 void ModFileWriter::PutSubprogram(const Symbol &symbol) {
293 auto attrs{symbol.attrs()};
294 auto &details{symbol.get<SubprogramDetails>()};
296 if (attrs.test(Attr::BIND_C)) {
297 // bind(c) is a suffix, not prefix
298 bindAttrs.set(Attr::BIND_C, true);
299 attrs.set(Attr::BIND_C, false);
301 Attrs prefixAttrs{subprogramPrefixAttrs & attrs};
302 // emit any non-prefix attributes in an attribute statement
303 attrs &= ~subprogramPrefixAttrs;
304 std::stringstream ss;
306 if (!ss.str().empty()) {
307 decls_ << ss.str().substr(1) << "::" << symbol.name() << '\n';
309 bool isInterface{details.isInterface()};
310 std::ostream &os{isInterface ? decls_ : contains_};
314 PutAttrs(os, prefixAttrs, std::nullopt, ""s, " "s);
315 os << (details.isFunction() ? "function " : "subroutine ");
316 os << symbol.name() << '(';
318 for (const auto &dummy : details.dummyArgs()) {
325 PutAttrs(os, bindAttrs, details.bindName(), " "s, ""s);
326 if (details.isFunction()) {
327 const Symbol &result{details.result()};
328 if (result.name() != symbol.name()) {
329 os << " result(" << result.name() << ')';
334 // walk symbols, collect ones needed
335 ModFileWriter writer{context_};
336 std::stringstream typeBindings;
337 SubprogramSymbolCollector collector{symbol};
339 for (const Symbol &need : collector.symbols()) {
340 writer.PutSymbol(typeBindings, need);
342 CHECK(typeBindings.str().empty());
343 os << writer.uses_.str();
344 for (const SourceName &import : collector.imports()) {
345 decls_ << "import::" << import << "\n";
347 os << writer.decls_.str();
350 os << "end interface\n";
354 static bool IsIntrinsicOp(const Symbol &symbol) {
355 if (const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()}) {
356 return details->kind().IsIntrinsicOperator();
362 static std::ostream &PutGenericName(std::ostream &os, const Symbol &symbol) {
363 if (IsGenericDefinedOp(symbol)) {
364 return os << "operator(" << symbol.name() << ')';
366 return os << symbol.name();
370 void ModFileWriter::PutGeneric(const Symbol &symbol) {
371 auto &details{symbol.get<GenericDetails>()};
372 PutGenericName(decls_ << "interface ", symbol) << '\n';
373 for (const Symbol &specific : details.specificProcs()) {
374 decls_ << "procedure::" << specific.name() << '\n';
376 decls_ << "end interface\n";
377 if (symbol.attrs().test(Attr::PRIVATE)) {
378 PutGenericName(decls_ << "private::", symbol) << '\n';
382 void ModFileWriter::PutUse(const Symbol &symbol) {
383 auto &details{symbol.get<UseDetails>()};
384 auto &use{details.symbol()};
385 uses_ << "use " << details.module().name();
386 PutGenericName(uses_ << ",only:", symbol);
387 // Can have intrinsic op with different local-name and use-name
388 // (e.g. `operator(<)` and `operator(.lt.)`) but rename is not allowed
389 if (!IsIntrinsicOp(symbol) && use.name() != symbol.name()) {
390 PutGenericName(uses_ << "=>", use);
393 PutUseExtraAttr(Attr::VOLATILE, symbol, use);
394 PutUseExtraAttr(Attr::ASYNCHRONOUS, symbol, use);
397 // We have "USE local => use" in this module. If attr was added locally
398 // (i.e. on local but not on use), also write it out in the mod file.
399 void ModFileWriter::PutUseExtraAttr(
400 Attr attr, const Symbol &local, const Symbol &use) {
401 if (local.attrs().test(attr) && !use.attrs().test(attr)) {
402 PutAttr(useExtraAttrs_, attr) << "::";
403 useExtraAttrs_ << local.name() << '\n';
407 // Collect the symbols of this scope sorted by their original order, not name.
408 // Namelists are an exception: they are sorted after other symbols.
409 SymbolVector CollectSymbols(const Scope &scope) {
410 SymbolSet symbols; // to prevent duplicates
412 SymbolVector namelist;
414 sorted.reserve(scope.size() + scope.commonBlocks().size());
415 for (const auto &pair : scope) {
416 const Symbol &symbol{*pair.second};
417 if (!symbol.test(Symbol::Flag::ParentComp)) {
418 if (symbols.insert(symbol).second) {
419 if (symbol.has<NamelistDetails>()) {
420 namelist.push_back(symbol);
422 sorted.push_back(symbol);
427 for (const auto &pair : scope.commonBlocks()) {
428 const Symbol &symbol{*pair.second};
429 if (symbols.insert(symbol).second) {
430 common.push_back(symbol);
433 // sort normal symbols, then namelists, then common blocks:
434 auto cursor{sorted.begin()};
435 std::sort(cursor, sorted.end());
436 cursor = sorted.insert(sorted.end(), namelist.begin(), namelist.end());
437 std::sort(cursor, sorted.end());
438 cursor = sorted.insert(sorted.end(), common.begin(), common.end());
439 std::sort(cursor, sorted.end());
443 void PutEntity(std::ostream &os, const Symbol &symbol) {
446 [&](const ObjectEntityDetails &) { PutObjectEntity(os, symbol); },
447 [&](const ProcEntityDetails &) { PutProcEntity(os, symbol); },
448 [&](const TypeParamDetails &) { PutTypeParam(os, symbol); },
450 common::die("PutEntity: unexpected details: %s",
451 DetailsToString(symbol.details()).c_str());
457 void PutShapeSpec(std::ostream &os, const ShapeSpec &x) {
458 if (x.lbound().isAssumed()) {
459 CHECK(x.ubound().isAssumed());
462 if (!x.lbound().isDeferred()) {
463 PutBound(os, x.lbound());
466 if (!x.ubound().isDeferred()) {
467 PutBound(os, x.ubound());
471 void PutShape(std::ostream &os, const ArraySpec &shape, char open, char close) {
472 if (!shape.empty()) {
475 for (const auto &shapeSpec : shape) {
481 PutShapeSpec(os, shapeSpec);
487 void PutObjectEntity(std::ostream &os, const Symbol &symbol) {
488 auto &details{symbol.get<ObjectEntityDetails>()};
489 PutEntity(os, symbol, [&]() { PutType(os, DEREF(symbol.GetType())); },
491 PutShape(os, details.shape(), '(', ')');
492 PutShape(os, details.coshape(), '[', ']');
493 PutInit(os, symbol, details.init());
497 void PutProcEntity(std::ostream &os, const Symbol &symbol) {
498 if (symbol.attrs().test(Attr::INTRINSIC)) {
499 os << "intrinsic::" << symbol.name() << '\n';
502 const auto &details{symbol.get<ProcEntityDetails>()};
503 const ProcInterface &interface{details.interface()};
504 Attrs attrs{symbol.attrs()};
505 if (details.passName()) {
506 attrs.reset(Attr::PASS);
508 PutEntity(os, symbol,
511 if (interface.symbol()) {
512 os << interface.symbol()->name();
513 } else if (interface.type()) {
514 PutType(os, *interface.type());
517 PutPassName(os, details.passName());
523 void PutPassName(std::ostream &os, const std::optional<SourceName> &passName) {
525 os << ",pass(" << *passName << ')';
529 void PutTypeParam(std::ostream &os, const Symbol &symbol) {
530 auto &details{symbol.get<TypeParamDetails>()};
531 PutEntity(os, symbol,
533 PutType(os, DEREF(symbol.GetType()));
534 PutLower(os << ',', common::EnumToString(details.attr()));
537 PutInit(os, details.init());
541 void PutInit(std::ostream &os, const Symbol &symbol, const MaybeExpr &init) {
543 if (symbol.attrs().test(Attr::PARAMETER) ||
544 symbol.owner().IsDerivedType()) {
545 os << (symbol.attrs().test(Attr::POINTER) ? "=>" : "=");
551 void PutInit(std::ostream &os, const MaybeIntExpr &init) {
553 init->AsFortran(os << '=');
557 void PutBound(std::ostream &os, const Bound &x) {
560 } else if (x.isDeferred()) {
563 x.GetExplicit()->AsFortran(os);
567 // Write an entity (object or procedure) declaration.
568 // writeType is called to write out the type.
569 void PutEntity(std::ostream &os, const Symbol &symbol,
570 std::function<void()> writeType, Attrs attrs) {
575 [&](const SubprogramDetails &x) { bindName = x.bindName(); },
576 [&](const ObjectEntityDetails &x) { bindName = x.bindName(); },
577 [&](const ProcEntityDetails &x) { bindName = x.bindName(); },
578 [&](const auto &) {},
581 PutAttrs(os, attrs, bindName);
582 os << "::" << symbol.name();
585 // Put out each attribute to os, surrounded by `before` and `after` and
586 // mapped to lower case.
587 std::ostream &PutAttrs(std::ostream &os, Attrs attrs, const MaybeExpr &bindName,
588 std::string before, std::string after) {
589 attrs.set(Attr::PUBLIC, false); // no need to write PUBLIC
590 attrs.set(Attr::EXTERNAL, false); // no need to write EXTERNAL
592 bindName->AsFortran(os << before << "bind(c, name=") << ')' << after;
593 attrs.set(Attr::BIND_C, false);
595 for (std::size_t i{0}; i < Attr_enumSize; ++i) {
596 Attr attr{static_cast<Attr>(i)};
597 if (attrs.test(attr)) {
598 PutAttr(os << before, attr) << after;
604 std::ostream &PutAttr(std::ostream &os, Attr attr) {
605 return PutLower(os, AttrToString(attr));
608 std::ostream &PutType(std::ostream &os, const DeclTypeSpec &type) {
609 return PutLower(os, type.AsFortran());
612 std::ostream &PutLower(std::ostream &os, const std::string &str) {
614 os << parser::ToLowerCaseLetter(c);
620 Temp(llvm::sys::fs::file_t fd, std::string path) : fd{fd}, path{path} {}
621 Temp(Temp &&t) : fd{std::exchange(t.fd, -1)}, path{std::move(t.path)} {}
624 llvm::sys::fs::closeFile(fd);
625 llvm::sys::fs::remove(path.c_str());
628 llvm::sys::fs::file_t fd;
632 // Create a temp file in the same directory and with the same suffix as path.
633 // Return an open file descriptor and its path.
634 static llvm::ErrorOr<Temp> MkTemp(const std::string &path) {
635 auto length{path.length()};
636 auto dot{path.find_last_of("./")};
638 dot < length && path[dot] == '.' ? path.substr(dot + 1) : ""};
639 CHECK(length > suffix.length() &&
640 path.substr(length - suffix.length()) == suffix);
641 auto prefix{path.substr(0, length - suffix.length())};
642 llvm::sys::fs::file_t fd;
643 llvm::SmallString<16> tempPath;
644 if (std::error_code err{llvm::sys::fs::createUniqueFile(
645 prefix + "%%%%%%" + suffix, fd, tempPath)}) {
648 return Temp{fd, tempPath.c_str()};
651 // Write the module file at path, prepending header. If an error occurs,
652 // return errno, otherwise 0.
653 static std::error_code WriteFile(
654 const std::string &path, const std::string &contents, bool debug) {
655 auto header{std::string{ModHeader::bom} + ModHeader::magic +
656 CheckSum(contents) + ModHeader::terminator};
658 llvm::dbgs() << "Processing module " << path << ": ";
660 if (FileContentsMatch(path, header, contents)) {
662 llvm::dbgs() << "module unchanged, not writing\n";
666 llvm::ErrorOr<Temp> temp{MkTemp(path)};
668 return temp.getError();
670 llvm::raw_fd_ostream writer(temp->fd, /*shouldClose=*/false);
674 if (writer.has_error()) {
675 return writer.error();
678 llvm::dbgs() << "module written\n";
680 return llvm::sys::fs::rename(temp->path, path);
683 // Return true if the stream matches what we would write for the mod file.
684 static bool FileContentsMatch(const std::string &path,
685 const std::string &header, const std::string &contents) {
686 std::size_t hsize{header.size()};
687 std::size_t csize{contents.size()};
688 auto buf_or{llvm::MemoryBuffer::getFile(path)};
692 auto buf = std::move(buf_or.get());
693 if (buf->getBufferSize() != hsize + csize) {
696 if (!std::equal(header.begin(), header.end(), buf->getBufferStart(),
697 buf->getBufferStart() + hsize)) {
701 return std::equal(contents.begin(), contents.end(),
702 buf->getBufferStart() + hsize, buf->getBufferEnd());
705 // Compute a simple hash of the contents of a module file and
706 // return it as a string of hex digits.
707 // This uses the Fowler-Noll-Vo hash function.
708 static std::string CheckSum(const std::string_view &contents) {
709 std::uint64_t hash{0xcbf29ce484222325ull};
710 for (char c : contents) {
712 hash *= 0x100000001b3;
714 static const char *digits = "0123456789abcdef";
715 std::string result(ModHeader::sumLen, '0');
716 for (size_t i{ModHeader::sumLen}; hash != 0; hash >>= 4) {
717 result[--i] = digits[hash & 0xf];
722 static bool VerifyHeader(const char *content, std::size_t len) {
723 std::string_view sv{content, len};
724 if (sv.substr(0, ModHeader::magicLen) != ModHeader::magic) {
727 std::string_view expectSum{sv.substr(ModHeader::magicLen, ModHeader::sumLen)};
728 std::string actualSum{CheckSum(sv.substr(ModHeader::len))};
729 return expectSum == actualSum;
732 Scope *ModFileReader::Read(const SourceName &name, Scope *ancestor) {
733 std::string ancestorName; // empty for module
735 if (auto *scope{ancestor->FindSubmodule(name)}) {
738 ancestorName = ancestor->GetName().value().ToString();
740 auto it{context_.globalScope().find(name)};
741 if (it != context_.globalScope().end()) {
742 return it->second->scope();
745 parser::Parsing parsing{context_.allSources()};
746 parser::Options options;
747 options.isModuleFile = true;
748 options.features.Enable(common::LanguageFeature::BackslashEscapes);
749 options.searchDirectories = context_.searchDirectories();
750 auto path{ModFileName(name, ancestorName, context_.moduleFileSuffix())};
751 const auto *sourceFile{parsing.Prescan(path, options)};
752 if (parsing.messages().AnyFatalError()) {
753 for (auto &msg : parsing.messages().messages()) {
754 std::string str{msg.ToString()};
755 Say(name, ancestorName, parser::MessageFixedText{str.c_str(), str.size()},
761 if (!VerifyHeader(sourceFile->content(), sourceFile->bytes())) {
762 Say(name, ancestorName, "File has invalid checksum: %s"_en_US,
767 parsing.Parse(nullptr);
768 auto &parseTree{parsing.parseTree()};
769 if (!parsing.messages().empty() || !parsing.consumedWholeFile() ||
771 Say(name, ancestorName, "Module file is corrupt: %s"_err_en_US,
775 Scope *parentScope; // the scope this module/submodule goes into
777 parentScope = &context_.globalScope();
778 } else if (std::optional<SourceName> parent{GetSubmoduleParent(*parseTree)}) {
779 parentScope = Read(*parent, ancestor);
781 parentScope = ancestor;
783 ResolveNames(context_, *parseTree);
784 const auto &it{parentScope->find(name)};
785 if (it == parentScope->end()) {
788 auto &modSymbol{*it->second};
789 modSymbol.set(Symbol::Flag::ModFile);
790 modSymbol.scope()->set_chars(parsing.cooked());
791 return modSymbol.scope();
794 parser::Message &ModFileReader::Say(const SourceName &name,
795 const std::string &ancestor, parser::MessageFixedText &&msg,
796 const std::string &arg) {
800 ? "Error reading module file for module '%s'"_err_en_US
801 : "Error reading module file for submodule '%s' of module '%s'"_err_en_US,
803 .Attach(name, std::move(msg), arg);
806 // program was read from a .mod file for a submodule; return the name of the
807 // submodule's parent submodule, nullptr if none.
808 static std::optional<SourceName> GetSubmoduleParent(
809 const parser::Program &program) {
810 CHECK(program.v.size() == 1);
811 auto &unit{program.v.front()};
812 auto &submod{std::get<common::Indirection<parser::Submodule>>(unit.u)};
814 std::get<parser::Statement<parser::SubmoduleStmt>>(submod.value().t)};
815 auto &parentId{std::get<parser::ParentIdentifier>(stmt.statement.t)};
816 if (auto &parent{std::get<std::optional<parser::Name>>(parentId.t)}) {
817 return parent->source;
823 void SubprogramSymbolCollector::Collect() {
824 const auto &details{symbol_.get<SubprogramDetails>()};
825 isInterface_ = details.isInterface();
826 for (const Symbol *dummyArg : details.dummyArgs()) {
827 DoSymbol(DEREF(dummyArg));
829 if (details.isFunction()) {
830 DoSymbol(details.result());
832 for (const auto &pair : scope_) {
833 const Symbol &symbol{*pair.second};
834 if (const auto *useDetails{symbol.detailsIf<UseDetails>()}) {
835 if (useSet_.count(useDetails->symbol()) > 0) {
836 need_.push_back(symbol);
842 void SubprogramSymbolCollector::DoSymbol(const Symbol &symbol) {
843 DoSymbol(symbol.name(), symbol);
846 // Do symbols this one depends on; then add to need_
847 void SubprogramSymbolCollector::DoSymbol(
848 const SourceName &name, const Symbol &symbol) {
849 const auto &scope{symbol.owner()};
850 if (scope != scope_ && !scope.IsDerivedType()) {
851 if (scope != scope_.parent()) {
852 useSet_.insert(symbol);
854 if (NeedImport(name, symbol)) {
855 imports_.insert(name);
859 if (!needSet_.insert(symbol).second) {
860 return; // already done
864 [this](const ObjectEntityDetails &details) {
865 for (const ShapeSpec &spec : details.shape()) {
866 DoBound(spec.lbound());
867 DoBound(spec.ubound());
869 for (const ShapeSpec &spec : details.coshape()) {
870 DoBound(spec.lbound());
871 DoBound(spec.ubound());
873 if (const Symbol * commonBlock{details.commonBlock()}) {
874 DoSymbol(*commonBlock);
877 [this](const CommonBlockDetails &details) {
878 for (const Symbol &object : details.objects()) {
885 if (!symbol.has<UseDetails>()) {
886 DoType(symbol.GetType());
888 if (!scope.IsDerivedType()) {
889 need_.push_back(symbol);
893 void SubprogramSymbolCollector::DoType(const DeclTypeSpec *type) {
897 switch (type->category()) {
898 case DeclTypeSpec::Numeric:
899 case DeclTypeSpec::Logical: break; // nothing to do
900 case DeclTypeSpec::Character:
901 DoParamValue(type->characterTypeSpec().length());
904 if (const DerivedTypeSpec * derived{type->AsDerived()}) {
905 const auto &typeSymbol{derived->typeSymbol()};
906 if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
907 DoSymbol(extends->name(), extends->typeSymbol());
909 for (const auto &pair : derived->parameters()) {
910 DoParamValue(pair.second);
912 for (const auto &pair : *typeSymbol.scope()) {
913 const Symbol &comp{*pair.second};
916 DoSymbol(derived->name(), derived->typeSymbol());
921 void SubprogramSymbolCollector::DoBound(const Bound &bound) {
922 if (const MaybeSubscriptIntExpr & expr{bound.GetExplicit()}) {
926 void SubprogramSymbolCollector::DoParamValue(const ParamValue ¶mValue) {
927 if (const auto &expr{paramValue.GetExplicit()}) {
932 // Do we need a IMPORT of this symbol into an interface block?
933 bool SubprogramSymbolCollector::NeedImport(
934 const SourceName &name, const Symbol &symbol) {
937 } else if (symbol.owner() != scope_.parent()) {
938 // detect import from parent of use-associated symbol
939 // can be null in the case of a use-associated derived type's parent type
940 const auto *found{scope_.FindSymbol(name)};
941 CHECK(found || symbol.has<DerivedTypeDetails>());
942 return found && found->has<UseDetails>() && found->owner() != scope_;