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/Parser/unparse.h" 16 #include "flang/Semantics/scope.h" 17 #include "flang/Semantics/semantics.h" 18 #include "flang/Semantics/symbol.h" 19 #include "flang/Semantics/tools.h" 20 #include "llvm/Support/FileSystem.h" 21 #include "llvm/Support/MemoryBuffer.h" 22 #include "llvm/Support/raw_ostream.h" 23 #include <algorithm> 24 #include <fstream> 25 #include <set> 26 #include <string_view> 27 #include <vector> 28 29 namespace Fortran::semantics { 30 31 using 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. 37 struct 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}; 44 static constexpr int needLen{7}; 45 static constexpr const char need[needLen + 1]{"!need$ "}; 46 }; 47 48 static std::optional<SourceName> GetSubmoduleParent(const parser::Program &); 49 static void CollectSymbols(const Scope &, SymbolVector &, SymbolVector &, 50 std::map<const Symbol *, SourceName> &, UnorderedSymbolSet &); 51 static void PutPassName(llvm::raw_ostream &, const std::optional<SourceName> &); 52 static void PutInit(llvm::raw_ostream &, const Symbol &, const MaybeExpr &, 53 const parser::Expr *, const std::map<const Symbol *, SourceName> &); 54 static void PutInit(llvm::raw_ostream &, const MaybeIntExpr &); 55 static void PutBound(llvm::raw_ostream &, const Bound &); 56 static void PutShapeSpec(llvm::raw_ostream &, const ShapeSpec &); 57 static void PutShape( 58 llvm::raw_ostream &, const ArraySpec &, char open, char close); 59 60 static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr); 61 static llvm::raw_ostream &PutType(llvm::raw_ostream &, const DeclTypeSpec &); 62 static llvm::raw_ostream &PutLower(llvm::raw_ostream &, std::string_view); 63 static std::error_code WriteFile(const std::string &, const std::string &, 64 ModuleCheckSumType &, bool debug = true); 65 static bool FileContentsMatch( 66 const std::string &, const std::string &, const std::string &); 67 static ModuleCheckSumType ComputeCheckSum(const std::string_view &); 68 static std::string CheckSumString(ModuleCheckSumType); 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 UnorderedSymbolSet needSet_; // symbols already in need_ 85 UnorderedSymbolSet 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 &module{symbol.get<ModuleDetails>()}; 136 if (module.moduleFileHash()) { 137 return; // already written 138 } 139 auto *ancestor{module.ancestor()}; 140 isSubmodule_ = ancestor != nullptr; 141 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())); 145 ModuleCheckSumType checkSum; 146 if (std::error_code error{WriteFile( 147 path, GetAsString(symbol), checkSum, context_.debugModuleWriter())}) { 148 context_.Say( 149 symbol.name(), "Error writing %s: %s"_err_en_US, path, error.message()); 150 } 151 const_cast<ModuleDetails &>(module).set_moduleFileHash(checkSum); 152 } 153 154 // Return the entire body of the module file 155 // and clear saved uses, decls, and contains. 156 std::string ModFileWriter::GetAsString(const Symbol &symbol) { 157 std::string buf; 158 llvm::raw_string_ostream all{buf}; 159 all << needs_.str(); 160 needs_.str().clear(); 161 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(); 174 uses_.str().clear(); 175 all << useExtraAttrs_.str(); 176 useExtraAttrs_.str().clear(); 177 all << decls_.str(); 178 decls_.str().clear(); 179 auto str{contains_.str()}; 180 contains_.str().clear(); 181 if (!str.empty()) { 182 all << "contains\n" << str; 183 } 184 all << "end\n"; 185 return all.str(); 186 } 187 188 // Collect symbols from initializations that are being referenced directly 189 // from other modules; they may require new USE associations. 190 static 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 } 197 } else if (const auto &generic{symbol->detailsIf<GenericDetails>()}; 198 generic && generic->derivedType()) { 199 const Symbol &dtSym{*generic->derivedType()}; 200 if (dtSym.has<DerivedTypeDetails>()) { 201 if (dtSym.scope()) { 202 HarvestInitializerSymbols(set, *dtSym.scope()); 203 } 204 } else { 205 CHECK(dtSym.has<UseErrorDetails>()); 206 } 207 } 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 223 void 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 267 // Put out the visible symbols from scope. 268 void ModFileWriter::PutSymbols(const Scope &scope) { 269 SymbolVector sorted; 270 SymbolVector uses; 271 PrepareRenamings(scope); 272 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 } 283 std::string buf; // stuff after CONTAINS in derived type 284 llvm::raw_string_ostream typeBindings{buf}; 285 for (const Symbol &symbol : sorted) { 286 if (!symbol.test(Symbol::Flag::CompilerCreated)) { 287 PutSymbol(typeBindings, symbol); 288 } 289 } 290 for (const Symbol &symbol : uses) { 291 PutUse(symbol); 292 } 293 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 } 305 CHECK(typeBindings.str().empty()); 306 } 307 308 // Emit components in order 309 bool 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 } 340 if (auto str{typeBindings.str()}; !str.empty()) { 341 CHECK(scope.IsDerivedType()); 342 decls_ << "contains\n" << str; 343 return true; 344 } else { 345 return false; 346 } 347 } 348 349 // Return the symbol's attributes that should be written 350 // into the mod file. 351 static 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 357 static 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 366 // 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. 368 void ModFileWriter::PutSymbol( 369 llvm::raw_ostream &typeBindings, const Symbol &symbol) { 370 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); 384 } 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'; 414 if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) { 415 decls_ << "private::" << symbol.name() << '\n'; 416 } 417 }, 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)) { 427 PutAttrs(decls_, getSymbolAttrsToWrite(symbol), x.bindName(), 428 x.isExplicitBindName(), ""s); 429 decls_ << "::/" << symbol.name() << "/\n"; 430 } 431 }, 432 [](const HostAssocDetails &) {}, 433 [](const MiscDetails &) {}, 434 [&](const auto &) { 435 PutEntity(decls_, symbol); 436 PutDirective(decls_, symbol); 437 }, 438 }, 439 symbol.details()); 440 } 441 442 void ModFileWriter::PutDerivedType( 443 const Symbol &typeSymbol, const Scope *scope) { 444 auto &details{typeSymbol.get<DerivedTypeDetails>()}; 445 if (details.isDECStructure()) { 446 PutDECStructure(typeSymbol, scope); 447 return; 448 } 449 PutAttrs(decls_ << "type", typeSymbol.attrs()); 450 if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) { 451 decls_ << ",extends(" << extends->name() << ')'; 452 } 453 decls_ << "::" << typeSymbol.name(); 454 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 } 466 bool contains{PutComponents(typeSymbol)}; 467 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 } 477 decls_ << "end type\n"; 478 } 479 480 void 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(), '(', ')'); 509 PutInit(decls_, *ref, object->init(), nullptr, renamings_); 510 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 521 // Attributes that may be in a subprogram prefix 522 static const Attrs subprogramPrefixAttrs{Attr::ELEMENTAL, Attr::IMPURE, 523 Attr::MODULE, Attr::NON_RECURSIVE, Attr::PURE, Attr::RECURSIVE}; 524 525 static 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 547 static void PutOpenACCRoutineInfo( 548 llvm::raw_ostream &os, const SubprogramDetails &details) { 549 for (auto info : details.openACCRoutineInfos()) { 550 os << "!$acc routine"; 551 552 PutOpenACCDeviceTypeRoutineInfo(os, info); 553 554 if (info.isNohost()) { 555 os << " nohost"; 556 } 557 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); 568 } 569 570 os << "\n"; 571 } 572 } 573 574 void ModFileWriter::PutSubprogram(const Symbol &symbol) { 575 auto &details{symbol.get<SubprogramDetails>()}; 576 if (const Symbol * interface{details.moduleInterface()}) { 577 const Scope *module{FindModuleContaining(interface->owner())}; 578 if (module && module != &symbol.owner()) { 579 // Interface is in ancestor module 580 } else { 581 PutSubprogram(*interface); 582 } 583 } 584 auto attrs{symbol.attrs()}; 585 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 } 591 bool isAbstract{attrs.test(Attr::ABSTRACT)}; 592 if (isAbstract) { 593 attrs.set(Attr::ABSTRACT, false); 594 } 595 Attrs prefixAttrs{subprogramPrefixAttrs & attrs}; 596 // emit any non-prefix attributes in an attribute statement 597 attrs &= ~subprogramPrefixAttrs; 598 std::string ssBuf; 599 llvm::raw_string_ostream ss{ssBuf}; 600 PutAttrs(ss, attrs); 601 if (!ss.str().empty()) { 602 decls_ << ss.str().substr(1) << "::" << symbol.name() << '\n'; 603 } 604 bool isInterface{details.isInterface()}; 605 llvm::raw_ostream &os{isInterface ? decls_ : contains_}; 606 if (isInterface) { 607 os << (isAbstract ? "abstract " : "") << "interface\n"; 608 } 609 PutAttrs(os, prefixAttrs, nullptr, false, ""s, " "s); 610 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 } 635 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 } 642 if (dummy) { 643 os << dummy->name(); 644 } else { 645 os << "*"; 646 } 647 } 648 os << ')'; 649 PutAttrs(os, bindAttrs, details.bindName(), details.isExplicitBindName(), 650 " "s, ""s); 651 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'; 658 // 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(); 663 std::string typeBindingsBuf; 664 llvm::raw_string_ostream typeBindings{typeBindingsBuf}; 665 ModFileWriter writer{context_}; 666 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(); 675 PutOpenACCRoutineInfo(os, details); 676 os << "end\n"; 677 if (isInterface) { 678 os << "end interface\n"; 679 } 680 } 681 682 static 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 690 void ModFileWriter::PutGeneric(const Symbol &symbol) { 691 const auto &genericOwner{symbol.owner()}; 692 auto &details{symbol.get<GenericDetails>()}; 693 PutGenericName(decls_ << "interface ", symbol) << '\n'; 694 for (const Symbol &specific : details.specificProcs()) { 695 if (specific.owner() == genericOwner) { 696 decls_ << "procedure::" << specific.name() << '\n'; 697 } 698 } 699 decls_ << "end interface\n"; 700 if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) { 701 PutGenericName(decls_ << "private::", symbol) << '\n'; 702 } 703 } 704 705 void ModFileWriter::PutUse(const Symbol &symbol) { 706 auto &details{symbol.get<UseDetails>()}; 707 auto &use{details.symbol()}; 708 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); 716 // 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); 724 if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) { 725 PutGenericName(useExtraAttrs_ << "private::", symbol) << '\n'; 726 } 727 } 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. 731 void 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 739 static inline SourceName NameInModuleFile(const Symbol &symbol) { 740 if (const auto *use{symbol.detailsIf<UseDetails>()}) { 741 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 751 // Collect the symbols of this scope sorted by their original order, not name. 752 // Generics and namelists are exceptions: they are sorted after other symbols. 753 void CollectSymbols(const Scope &scope, SymbolVector &sorted, 754 SymbolVector &uses, std::map<const Symbol *, SourceName> &renamings, 755 UnorderedSymbolSet &modules) { 756 SymbolVector namelist, generics; 757 auto symbols{scope.GetSymbols()}; 758 std::size_t commonSize{scope.commonBlocks().size()}; 759 sorted.reserve(symbols.size() + commonSize); 760 for (SymbolRef symbol : symbols) { 761 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 } 770 if (symbol->test(Symbol::Flag::ParentComp)) { 771 } else if (symbol->has<NamelistDetails>()) { 772 namelist.push_back(symbol); 773 } else if (generic) { 774 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()); 780 } 781 generics.push_back(symbol); 782 } else { 783 sorted.push_back(symbol); 784 } 785 } 786 // Sort most symbols by name: use of Symbol::ReplaceName ensures the source 787 // location of a symbol's name is the first "real" use. 788 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()); 794 sorted.insert(sorted.end(), namelist.begin(), namelist.end()); 795 for (const auto &pair : scope.commonBlocks()) { 796 sorted.push_back(*pair.second); 797 } 798 std::sort( 799 sorted.end() - commonSize, sorted.end(), SymbolSourcePositionCompare{}); 800 } 801 802 void ModFileWriter::PutEntity(llvm::raw_ostream &os, const Symbol &symbol) { 803 common::visit( 804 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 816 void PutShapeSpec(llvm::raw_ostream &os, const ShapeSpec &x) { 817 if (x.lbound().isStar()) { 818 CHECK(x.ubound().isStar()); 819 os << ".."; // assumed rank 820 } else { 821 if (!x.lbound().isColon()) { 822 PutBound(os, x.lbound()); 823 } 824 os << ':'; 825 if (!x.ubound().isColon()) { 826 PutBound(os, x.ubound()); 827 } 828 } 829 } 830 void PutShape( 831 llvm::raw_ostream &os, const ArraySpec &shape, char open, char close) { 832 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 847 void ModFileWriter::PutObjectEntity( 848 llvm::raw_ostream &os, const Symbol &symbol) { 849 auto &details{symbol.get<ObjectEntityDetails>()}; 850 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 } 860 PutEntity( 861 os, symbol, [&]() { PutType(os, DEREF(symbol.GetType())); }, 862 getSymbolAttrsToWrite(symbol)); 863 PutShape(os, details.shape(), '(', ')'); 864 PutShape(os, details.coshape(), '[', ']'); 865 PutInit(os, symbol, details.init(), details.unanalyzedPDTComponentInit(), 866 renamings_); 867 os << '\n'; 868 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 } 895 if (auto attr{details.cudaDataAttr()}) { 896 PutLower(os << "attributes(", common::EnumToString(*attr)) 897 << ") " << symbol.name() << '\n'; 898 } 899 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 } 908 } 909 910 void ModFileWriter::PutProcEntity(llvm::raw_ostream &os, const Symbol &symbol) { 911 if (symbol.attrs().test(Attr::INTRINSIC)) { 912 os << "intrinsic::" << symbol.name() << '\n'; 913 if (!isSubmodule_ && symbol.attrs().test(Attr::PRIVATE)) { 914 os << "private::" << symbol.name() << '\n'; 915 } 916 return; 917 } 918 const auto &details{symbol.get<ProcEntityDetails>()}; 919 Attrs attrs{symbol.attrs()}; 920 if (details.passName()) { 921 attrs.reset(Attr::PASS); 922 } 923 PutEntity( 924 os, symbol, 925 [&]() { 926 os << "procedure("; 927 if (details.rawProcInterface()) { 928 os << details.rawProcInterface()->name(); 929 } else if (details.type()) { 930 PutType(os, *details.type()); 931 } 932 os << ')'; 933 PutPassName(os, details.passName()); 934 }, 935 attrs); 936 os << '\n'; 937 } 938 939 void PutPassName( 940 llvm::raw_ostream &os, const std::optional<SourceName> &passName) { 941 if (passName) { 942 os << ",pass(" << *passName << ')'; 943 } 944 } 945 946 void ModFileWriter::PutTypeParam(llvm::raw_ostream &os, const Symbol &symbol) { 947 auto &details{symbol.get<TypeParamDetails>()}; 948 PutEntity( 949 os, symbol, 950 [&]() { 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 959 void PutInit(llvm::raw_ostream &os, const Symbol &symbol, const MaybeExpr &init, 960 const parser::Expr *unanalyzed, 961 const std::map<const Symbol *, SourceName> &renamings) { 962 if (IsNamedConstant(symbol) || symbol.owner().IsDerivedType()) { 963 const char *assign{symbol.attrs().test(Attr::POINTER) ? "=>" : "="}; 964 if (unanalyzed) { 965 parser::Unparse(os << assign, *unanalyzed); 966 } else if (init) { 967 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 } 979 init->AsFortran(os << assign); 980 } 981 } 982 } 983 984 void PutInit(llvm::raw_ostream &os, const MaybeIntExpr &init) { 985 if (init) { 986 init->AsFortran(os << '='); 987 } 988 } 989 990 void PutBound(llvm::raw_ostream &os, const Bound &x) { 991 if (x.isStar()) { 992 os << '*'; 993 } else if (x.isColon()) { 994 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. 1002 void ModFileWriter::PutEntity(llvm::raw_ostream &os, const Symbol &symbol, 1003 std::function<void()> writeType, Attrs attrs) { 1004 writeType(); 1005 PutAttrs(os, attrs, symbol.GetBindName(), symbol.GetIsExplicitBindName()); 1006 if (symbol.owner().kind() == Scope::Kind::DerivedType && 1007 context_.IsTempName(symbol.name().ToString())) { 1008 os << "::%FILL"; 1009 } else { 1010 os << "::" << symbol.name(); 1011 } 1012 } 1013 1014 // Put out each attribute to os, surrounded by `before` and `after` and 1015 // mapped to lower case. 1016 llvm::raw_ostream &ModFileWriter::PutAttrs(llvm::raw_ostream &os, Attrs attrs, 1017 const std::string *bindName, bool isExplicitBindName, std::string before, 1018 std::string after) const { 1019 attrs.set(Attr::PUBLIC, false); // no need to write PUBLIC 1020 attrs.set(Attr::EXTERNAL, false); // no need to write EXTERNAL 1021 if (isSubmodule_) { 1022 attrs.set(Attr::PRIVATE, false); 1023 } 1024 if (bindName || isExplicitBindName) { 1025 os << before << "bind(c"; 1026 if (isExplicitBindName) { 1027 os << ",name=\"" << (bindName ? *bindName : ""s) << '"'; 1028 } 1029 os << ')' << after; 1030 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 1041 llvm::raw_ostream &PutAttr(llvm::raw_ostream &os, Attr attr) { 1042 return PutLower(os, AttrToString(attr)); 1043 } 1044 1045 llvm::raw_ostream &PutType(llvm::raw_ostream &os, const DeclTypeSpec &type) { 1046 return PutLower(os, type.AsFortran()); 1047 } 1048 1049 llvm::raw_ostream &PutLower(llvm::raw_ostream &os, std::string_view str) { 1050 for (char c : str) { 1051 os << parser::ToLowerCaseLetter(c); 1052 } 1053 return os; 1054 } 1055 1056 void PutOpenACCDirective(llvm::raw_ostream &os, const Symbol &symbol) { 1057 if (symbol.test(Symbol::Flag::AccDeclare)) { 1058 os << "!$acc declare "; 1059 if (symbol.test(Symbol::Flag::AccCopy)) { 1060 os << "copy"; 1061 } else if (symbol.test(Symbol::Flag::AccCopyIn) || 1062 symbol.test(Symbol::Flag::AccCopyInReadOnly)) { 1063 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 } 1077 os << "("; 1078 if (symbol.test(Symbol::Flag::AccCopyInReadOnly)) { 1079 os << "readonly: "; 1080 } 1081 os << symbol.name() << ")\n"; 1082 } 1083 } 1084 1085 void 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 1091 void ModFileWriter::PutDirective(llvm::raw_ostream &os, const Symbol &symbol) { 1092 PutOpenACCDirective(os, symbol); 1093 PutOpenMPDirective(os, symbol); 1094 } 1095 1096 struct Temp { 1097 Temp(int fd, std::string path) : fd{fd}, path{path} {} 1098 Temp(Temp &&t) : fd{std::exchange(t.fd, -1)}, path{std::move(t.path)} {} 1099 ~Temp() { 1100 if (fd >= 0) { 1101 llvm::sys::fs::file_t native{llvm::sys::fs::convertFDToNativeFile(fd)}; 1102 llvm::sys::fs::closeFile(native); 1103 llvm::sys::fs::remove(path.c_str()); 1104 } 1105 } 1106 int fd; 1107 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. 1112 static llvm::ErrorOr<Temp> MkTemp(const std::string &path) { 1113 auto length{path.length()}; 1114 auto dot{path.find_last_of("./")}; 1115 std::string suffix{ 1116 dot < length && path[dot] == '.' ? path.substr(dot + 1) : ""}; 1117 CHECK(length > suffix.length() && 1118 path.substr(length - suffix.length()) == suffix); 1119 auto prefix{path.substr(0, length - suffix.length())}; 1120 int fd; 1121 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()}; 1127 } 1128 1129 // Write the module file at path, prepending header. If an error occurs, 1130 // return errno, otherwise 0. 1131 static std::error_code WriteFile(const std::string &path, 1132 const std::string &contents, ModuleCheckSumType &checkSum, bool debug) { 1133 checkSum = ComputeCheckSum(contents); 1134 auto header{std::string{ModHeader::bom} + ModHeader::magic + 1135 CheckSumString(checkSum) + ModHeader::terminator}; 1136 if (debug) { 1137 llvm::dbgs() << "Processing module " << path << ": "; 1138 } 1139 if (FileContentsMatch(path, header, contents)) { 1140 if (debug) { 1141 llvm::dbgs() << "module unchanged, not writing\n"; 1142 } 1143 return {}; 1144 } 1145 llvm::ErrorOr<Temp> temp{MkTemp(path)}; 1146 if (!temp) { 1147 return temp.getError(); 1148 } 1149 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(); 1155 } 1156 if (debug) { 1157 llvm::dbgs() << "module written\n"; 1158 } 1159 return llvm::sys::fs::rename(temp->path, path); 1160 } 1161 1162 // Return true if the stream matches what we would write for the mod file. 1163 static 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()}; 1167 auto buf_or{llvm::MemoryBuffer::getFile(path)}; 1168 if (!buf_or) { 1169 return false; 1170 } 1171 auto buf = std::move(buf_or.get()); 1172 if (buf->getBufferSize() != hsize + csize) { 1173 return false; 1174 } 1175 if (!std::equal(header.begin(), header.end(), buf->getBufferStart(), 1176 buf->getBufferStart() + hsize)) { 1177 return false; 1178 } 1179 1180 return std::equal(contents.begin(), contents.end(), 1181 buf->getBufferStart() + hsize, buf->getBufferEnd()); 1182 } 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. 1187 static ModuleCheckSumType ComputeCheckSum(const std::string_view &contents) { 1188 ModuleCheckSumType hash{0xcbf29ce484222325ull}; 1189 for (char c : contents) { 1190 hash ^= c & 0xff; 1191 hash *= 0x100000001b3; 1192 } 1193 return hash; 1194 } 1195 1196 static std::string CheckSumString(ModuleCheckSumType hash) { 1197 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 1205 std::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; 1220 } 1221 return std::nullopt; 1222 } 1223 1224 static 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 1240 static 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}; 1245 str.substr(j, ModHeader::needLen) == ModHeader::need;) { 1246 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') { 1263 dependences.AddDependence( 1264 std::string{str.substr(start, j - start)}, intrinsic, *checkSum); 1265 } else { 1266 break; 1267 } 1268 } 1269 } 1270 1271 Scope *ModFileReader::Read(SourceName name, std::optional<bool> isIntrinsic, 1272 Scope *ancestor, bool silent) { 1273 std::string ancestorName; // empty for module 1274 Symbol *notAModule{nullptr}; 1275 bool fatalError{false}; 1276 if (ancestor) { 1277 if (auto *scope{ancestor->FindSubmodule(name)}) { 1278 return scope; 1279 } 1280 ancestorName = ancestor->GetName().value().ToString(); 1281 } 1282 auto requiredHash{context_.moduleDependences().GetRequiredHash( 1283 name.ToString(), isIntrinsic.value_or(false))}; 1284 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) { 1290 if (requiredHash) { 1291 if (const Symbol * foundModule{scope->symbol()}) { 1292 if (const auto *module{foundModule->detailsIf<ModuleDetails>()}; 1293 module && module->moduleFileHash() && 1294 *requiredHash != *module->moduleFileHash()) { 1295 Say(name, ancestorName, 1296 "Multiple versions of the module '%s' cannot be required by the same compilation"_err_en_US, 1297 name.ToString()); 1298 return nullptr; 1299 } 1300 } 1301 } 1302 return scope; 1303 } else { 1304 notAModule = scope->symbol(); 1305 // USE, NON_INTRINSIC global name isn't a module? 1306 fatalError = isIntrinsic.has_value(); 1307 } 1308 } 1309 } 1310 auto path{ModFileName(name, ancestorName, context_.moduleFileSuffix())}; 1311 parser::Parsing parsing{context_.allCookedSources()}; 1312 parser::Options options; 1313 options.isModuleFile = true; 1314 options.features.Enable(common::LanguageFeature::BackslashEscapes); 1315 options.features.Enable(common::LanguageFeature::OpenMP); 1316 options.features.Enable(common::LanguageFeature::CUDA); 1317 if (!isIntrinsic.value_or(false) && !notAModule) { 1318 // The search for this module file will scan non-intrinsic module 1319 // directories. If a directory is in both the intrinsic and non-intrinsic 1320 // directory lists, the intrinsic module directory takes precedence. 1321 options.searchDirectories = context_.searchDirectories(); 1322 for (const auto &dir : context_.intrinsicModuleDirectories()) { 1323 options.searchDirectories.erase( 1324 std::remove(options.searchDirectories.begin(), 1325 options.searchDirectories.end(), dir), 1326 options.searchDirectories.end()); 1327 } 1328 options.searchDirectories.insert(options.searchDirectories.begin(), "."s); 1329 } 1330 bool foundNonIntrinsicModuleFile{false}; 1331 if (!isIntrinsic) { 1332 std::list<std::string> searchDirs; 1333 for (const auto &d : options.searchDirectories) { 1334 searchDirs.push_back(d); 1335 } 1336 foundNonIntrinsicModuleFile = 1337 parser::LocateSourceFile(path, searchDirs).has_value(); 1338 } 1339 if (isIntrinsic.value_or(!foundNonIntrinsicModuleFile)) { 1340 // Explicitly intrinsic, or not specified and not found in the search 1341 // path; see whether it's already in the symbol table as an intrinsic 1342 // module. 1343 auto it{context_.intrinsicModulesScope().find(name)}; 1344 if (it != context_.intrinsicModulesScope().end()) { 1345 return it->second->scope(); 1346 } 1347 } 1348 // We don't have this module in the symbol table yet. 1349 // Find its module file and parse it. Define or extend the search 1350 // path with intrinsic module directories, if appropriate. 1351 if (isIntrinsic.value_or(true)) { 1352 for (const auto &dir : context_.intrinsicModuleDirectories()) { 1353 options.searchDirectories.push_back(dir); 1354 } 1355 if (!requiredHash) { 1356 requiredHash = 1357 context_.moduleDependences().GetRequiredHash(name.ToString(), true); 1358 } 1359 } 1360 1361 // Look for the right module file if its hash is known 1362 if (requiredHash && !fatalError) { 1363 std::vector<std::string> misses; 1364 for (const std::string &maybe : 1365 parser::LocateSourceFileAll(path, options.searchDirectories)) { 1366 if (const auto *srcFile{context_.allCookedSources().allSources().OpenPath( 1367 maybe, llvm::errs())}) { 1368 if (auto checkSum{VerifyHeader(srcFile->content())}) { 1369 if (*checkSum == *requiredHash) { 1370 path = maybe; 1371 if (!misses.empty()) { 1372 auto &msg{context_.Say(name, 1373 "Module file for '%s' appears later in the module search path than conflicting modules with different checksums"_warn_en_US, 1374 name.ToString())}; 1375 for (const std::string &m : misses) { 1376 msg.Attach( 1377 name, "Module file with a conflicting name: '%s'"_en_US, m); 1378 } 1379 } 1380 misses.clear(); 1381 break; 1382 } else { 1383 misses.emplace_back(maybe); 1384 } 1385 } 1386 } 1387 } 1388 if (!misses.empty()) { 1389 auto &msg{Say(name, ancestorName, 1390 "Could not find a module file for '%s' in the module search path with the expected checksum"_err_en_US, 1391 name.ToString())}; 1392 for (const std::string &m : misses) { 1393 msg.Attach(name, "Module file with different checksum: '%s'"_en_US, m); 1394 } 1395 return nullptr; 1396 } 1397 } 1398 1399 const auto *sourceFile{fatalError ? nullptr : parsing.Prescan(path, options)}; 1400 if (fatalError || parsing.messages().AnyFatalError()) { 1401 if (!silent) { 1402 if (notAModule) { 1403 // Module is not explicitly INTRINSIC, and there's already a global 1404 // symbol of the same name that is not a module. 1405 context_.SayWithDecl( 1406 *notAModule, name, "'%s' is not a module"_err_en_US, name); 1407 } else { 1408 for (auto &msg : parsing.messages().messages()) { 1409 std::string str{msg.ToString()}; 1410 Say(name, ancestorName, 1411 parser::MessageFixedText{str.c_str(), str.size(), msg.severity()}, 1412 path); 1413 } 1414 } 1415 } 1416 return nullptr; 1417 } 1418 CHECK(sourceFile); 1419 std::optional<ModuleCheckSumType> checkSum{ 1420 VerifyHeader(sourceFile->content())}; 1421 if (!checkSum) { 1422 Say(name, ancestorName, "File has invalid checksum: %s"_warn_en_US, 1423 sourceFile->path()); 1424 return nullptr; 1425 } else if (requiredHash && *requiredHash != *checkSum) { 1426 Say(name, ancestorName, 1427 "File is not the right module file for %s"_warn_en_US, 1428 "'"s + name.ToString() + "': "s + sourceFile->path()); 1429 return nullptr; 1430 } 1431 llvm::raw_null_ostream NullStream; 1432 parsing.Parse(NullStream); 1433 std::optional<parser::Program> &parsedProgram{parsing.parseTree()}; 1434 if (!parsing.messages().empty() || !parsing.consumedWholeFile() || 1435 !parsedProgram) { 1436 Say(name, ancestorName, "Module file is corrupt: %s"_err_en_US, 1437 sourceFile->path()); 1438 return nullptr; 1439 } 1440 parser::Program &parseTree{context_.SaveParseTree(std::move(*parsedProgram))}; 1441 Scope *parentScope; // the scope this module/submodule goes into 1442 if (!isIntrinsic.has_value()) { 1443 for (const auto &dir : context_.intrinsicModuleDirectories()) { 1444 if (sourceFile->path().size() > dir.size() && 1445 sourceFile->path().find(dir) == 0) { 1446 isIntrinsic = true; 1447 break; 1448 } 1449 } 1450 } 1451 Scope &topScope{isIntrinsic.value_or(false) ? context_.intrinsicModulesScope() 1452 : context_.globalScope()}; 1453 Symbol *moduleSymbol{nullptr}; 1454 if (!ancestor) { // module, not submodule 1455 parentScope = &topScope; 1456 auto pair{parentScope->try_emplace(name, UnknownDetails{})}; 1457 if (!pair.second) { 1458 return nullptr; 1459 } 1460 moduleSymbol = &*pair.first->second; 1461 moduleSymbol->set(Symbol::Flag::ModFile); 1462 } else if (std::optional<SourceName> parent{GetSubmoduleParent(parseTree)}) { 1463 // submodule with submodule parent 1464 parentScope = Read(*parent, false /*not intrinsic*/, ancestor, silent); 1465 } else { 1466 // submodule with module parent 1467 parentScope = ancestor; 1468 } 1469 // Process declarations from the module file 1470 bool wasInModuleFile{context_.foldingContext().inModuleFile()}; 1471 context_.foldingContext().set_inModuleFile(true); 1472 GetModuleDependences(context_.moduleDependences(), sourceFile->content()); 1473 ResolveNames(context_, parseTree, topScope); 1474 context_.foldingContext().set_inModuleFile(wasInModuleFile); 1475 if (!moduleSymbol) { 1476 // Submodule symbols' storage are owned by their parents' scopes, 1477 // but their names are not in their parents' dictionaries -- we 1478 // don't want to report bogus errors about clashes between submodule 1479 // names and other objects in the parent scopes. 1480 if (Scope * submoduleScope{ancestor->FindSubmodule(name)}) { 1481 moduleSymbol = submoduleScope->symbol(); 1482 if (moduleSymbol) { 1483 moduleSymbol->set(Symbol::Flag::ModFile); 1484 } 1485 } 1486 } 1487 if (moduleSymbol) { 1488 CHECK(moduleSymbol->test(Symbol::Flag::ModFile)); 1489 moduleSymbol->get<ModuleDetails>().set_moduleFileHash(checkSum.value()); 1490 if (isIntrinsic.value_or(false)) { 1491 moduleSymbol->attrs().set(Attr::INTRINSIC); 1492 } 1493 return moduleSymbol->scope(); 1494 } else { 1495 return nullptr; 1496 } 1497 } 1498 1499 parser::Message &ModFileReader::Say(SourceName name, 1500 const std::string &ancestor, parser::MessageFixedText &&msg, 1501 const std::string &arg) { 1502 return context_.Say(name, "Cannot read module file for %s: %s"_err_en_US, 1503 parser::MessageFormattedText{ancestor.empty() 1504 ? "module '%s'"_en_US 1505 : "submodule '%s' of module '%s'"_en_US, 1506 name, ancestor} 1507 .MoveString(), 1508 parser::MessageFormattedText{std::move(msg), arg}.MoveString()); 1509 } 1510 1511 // program was read from a .mod file for a submodule; return the name of the 1512 // submodule's parent submodule, nullptr if none. 1513 static std::optional<SourceName> GetSubmoduleParent( 1514 const parser::Program &program) { 1515 CHECK(program.v.size() == 1); 1516 auto &unit{program.v.front()}; 1517 auto &submod{std::get<common::Indirection<parser::Submodule>>(unit.u)}; 1518 auto &stmt{ 1519 std::get<parser::Statement<parser::SubmoduleStmt>>(submod.value().t)}; 1520 auto &parentId{std::get<parser::ParentIdentifier>(stmt.statement.t)}; 1521 if (auto &parent{std::get<std::optional<parser::Name>>(parentId.t)}) { 1522 return parent->source; 1523 } else { 1524 return std::nullopt; 1525 } 1526 } 1527 1528 void SubprogramSymbolCollector::Collect() { 1529 const auto &details{symbol_.get<SubprogramDetails>()}; 1530 isInterface_ = details.isInterface(); 1531 for (const Symbol *dummyArg : details.dummyArgs()) { 1532 if (dummyArg) { 1533 DoSymbol(*dummyArg); 1534 } 1535 } 1536 if (details.isFunction()) { 1537 DoSymbol(details.result()); 1538 } 1539 for (const auto &pair : scope_) { 1540 const Symbol &symbol{*pair.second}; 1541 if (const auto *useDetails{symbol.detailsIf<UseDetails>()}) { 1542 const Symbol &ultimate{useDetails->symbol().GetUltimate()}; 1543 bool needed{useSet_.count(ultimate) > 0}; 1544 if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) { 1545 // The generic may not be needed itself, but the specific procedure 1546 // &/or derived type that it shadows may be needed. 1547 const Symbol *spec{generic->specific()}; 1548 const Symbol *dt{generic->derivedType()}; 1549 needed = needed || (spec && useSet_.count(*spec) > 0) || 1550 (dt && useSet_.count(*dt) > 0); 1551 } else if (const auto *subp{ultimate.detailsIf<SubprogramDetails>()}) { 1552 const Symbol *interface { subp->moduleInterface() }; 1553 needed = needed || (interface && useSet_.count(*interface) > 0); 1554 } 1555 if (needed) { 1556 need_.push_back(symbol); 1557 } 1558 } else if (symbol.has<SubprogramDetails>()) { 1559 // An internal subprogram is needed if it is used as interface 1560 // for a dummy or return value procedure. 1561 bool needed{false}; 1562 const auto hasInterface{[&symbol](const Symbol *s) -> bool { 1563 // Is 's' a procedure with interface 'symbol'? 1564 if (s) { 1565 if (const auto *sDetails{s->detailsIf<ProcEntityDetails>()}) { 1566 if (sDetails->procInterface() == &symbol) { 1567 return true; 1568 } 1569 } 1570 } 1571 return false; 1572 }}; 1573 for (const Symbol *dummyArg : details.dummyArgs()) { 1574 needed = needed || hasInterface(dummyArg); 1575 } 1576 needed = 1577 needed || (details.isFunction() && hasInterface(&details.result())); 1578 if (needed && needSet_.insert(symbol).second) { 1579 need_.push_back(symbol); 1580 } 1581 } 1582 } 1583 } 1584 1585 void SubprogramSymbolCollector::DoSymbol(const Symbol &symbol) { 1586 DoSymbol(symbol.name(), symbol); 1587 } 1588 1589 // Do symbols this one depends on; then add to need_ 1590 void SubprogramSymbolCollector::DoSymbol( 1591 const SourceName &name, const Symbol &symbol) { 1592 const auto &scope{symbol.owner()}; 1593 if (scope != scope_ && !scope.IsDerivedType()) { 1594 if (scope != scope_.parent()) { 1595 useSet_.insert(symbol); 1596 } 1597 if (NeedImport(name, symbol)) { 1598 imports_.insert(name); 1599 } 1600 return; 1601 } 1602 if (!needSet_.insert(symbol).second) { 1603 return; // already done 1604 } 1605 common::visit(common::visitors{ 1606 [this](const ObjectEntityDetails &details) { 1607 for (const ShapeSpec &spec : details.shape()) { 1608 DoBound(spec.lbound()); 1609 DoBound(spec.ubound()); 1610 } 1611 for (const ShapeSpec &spec : details.coshape()) { 1612 DoBound(spec.lbound()); 1613 DoBound(spec.ubound()); 1614 } 1615 if (const Symbol * commonBlock{details.commonBlock()}) { 1616 DoSymbol(*commonBlock); 1617 } 1618 }, 1619 [this](const CommonBlockDetails &details) { 1620 for (const auto &object : details.objects()) { 1621 DoSymbol(*object); 1622 } 1623 }, 1624 [this](const ProcEntityDetails &details) { 1625 if (details.rawProcInterface()) { 1626 DoSymbol(*details.rawProcInterface()); 1627 } else { 1628 DoType(details.type()); 1629 } 1630 }, 1631 [this](const ProcBindingDetails &details) { 1632 DoSymbol(details.symbol()); 1633 }, 1634 [](const auto &) {}, 1635 }, 1636 symbol.details()); 1637 if (!symbol.has<UseDetails>()) { 1638 DoType(symbol.GetType()); 1639 } 1640 if (!scope.IsDerivedType()) { 1641 need_.push_back(symbol); 1642 } 1643 } 1644 1645 void SubprogramSymbolCollector::DoType(const DeclTypeSpec *type) { 1646 if (!type) { 1647 return; 1648 } 1649 switch (type->category()) { 1650 case DeclTypeSpec::Numeric: 1651 case DeclTypeSpec::Logical: 1652 break; // nothing to do 1653 case DeclTypeSpec::Character: 1654 DoParamValue(type->characterTypeSpec().length()); 1655 break; 1656 default: 1657 if (const DerivedTypeSpec * derived{type->AsDerived()}) { 1658 const auto &typeSymbol{derived->typeSymbol()}; 1659 for (const auto &pair : derived->parameters()) { 1660 DoParamValue(pair.second); 1661 } 1662 // The components of the type (including its parent component, if 1663 // any) matter to IMPORT symbol collection only for derived types 1664 // defined in the subprogram. 1665 if (typeSymbol.owner() == scope_) { 1666 if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) { 1667 DoSymbol(extends->name(), extends->typeSymbol()); 1668 } 1669 for (const auto &pair : *typeSymbol.scope()) { 1670 DoSymbol(*pair.second); 1671 } 1672 } 1673 DoSymbol(derived->name(), typeSymbol); 1674 } 1675 } 1676 } 1677 1678 void SubprogramSymbolCollector::DoBound(const Bound &bound) { 1679 if (const MaybeSubscriptIntExpr & expr{bound.GetExplicit()}) { 1680 DoExpr(*expr); 1681 } 1682 } 1683 void SubprogramSymbolCollector::DoParamValue(const ParamValue ¶mValue) { 1684 if (const auto &expr{paramValue.GetExplicit()}) { 1685 DoExpr(*expr); 1686 } 1687 } 1688 1689 // Do we need a IMPORT of this symbol into an interface block? 1690 bool SubprogramSymbolCollector::NeedImport( 1691 const SourceName &name, const Symbol &symbol) { 1692 if (!isInterface_) { 1693 return false; 1694 } else if (IsSeparateModuleProcedureInterface(&symbol_)) { 1695 return false; // IMPORT needed only for external and dummy procedure 1696 // interfaces 1697 } else if (&symbol == scope_.symbol()) { 1698 return false; 1699 } else if (symbol.owner().Contains(scope_)) { 1700 return true; 1701 } else if (const Symbol *found{scope_.FindSymbol(name)}) { 1702 // detect import from ancestor of use-associated symbol 1703 return found->has<UseDetails>() && found->owner() != scope_; 1704 } else { 1705 // "found" can be null in the case of a use-associated derived type's 1706 // parent type 1707 CHECK(symbol.has<DerivedTypeDetails>()); 1708 return false; 1709 } 1710 } 1711 1712 } // namespace Fortran::semantics 1713