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