From de069ae733d7b002a7317f6288bdf49bd6b86267 Mon Sep 17 00:00:00 2001 From: Peter Klausler Date: Tue, 17 Jun 2025 11:51:02 -0700 Subject: [PATCH 1/2] [flang] Don't emit needless symbols to hermetic module files When emitting the dependent modules for a hermetic module file, omit symbols that are not necessary (directly or otherwise) for the declarations and definitions in the main module. --- .../flang/Semantics/symbol-dependence.h | 36 ++ flang/include/flang/Semantics/tools.h | 2 +- flang/lib/Semantics/CMakeLists.txt | 1 + flang/lib/Semantics/check-declarations.cpp | 12 +- flang/lib/Semantics/expression.cpp | 3 +- flang/lib/Semantics/mod-file.cpp | 377 +++++++++++------- flang/lib/Semantics/mod-file.h | 11 +- flang/lib/Semantics/resolve-names.cpp | 22 +- flang/lib/Semantics/semantics.cpp | 12 +- flang/lib/Semantics/symbol-dependence.cpp | 356 +++++++++++++++++ flang/lib/Semantics/symbol.cpp | 28 +- flang/lib/Semantics/tools.cpp | 33 +- flang/lib/Semantics/type.cpp | 26 +- flang/test/Semantics/modfile03.f90 | 8 +- flang/test/Semantics/modfile65.f90 | 6 +- flang/test/Semantics/modfile78.F90 | 1 - 16 files changed, 718 insertions(+), 216 deletions(-) create mode 100644 flang/include/flang/Semantics/symbol-dependence.h create mode 100644 flang/lib/Semantics/symbol-dependence.cpp diff --git a/flang/include/flang/Semantics/symbol-dependence.h b/flang/include/flang/Semantics/symbol-dependence.h new file mode 100644 index 0000000000000..9bcff564a4c04 --- /dev/null +++ b/flang/include/flang/Semantics/symbol-dependence.h @@ -0,0 +1,36 @@ +//===-- include/flang/Semantics/symbol-dependence.h -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_SEMANTICS_SYMBOL_DEPENDENCE_H_ +#define FORTRAN_SEMANTICS_SYMBOL_DEPENDENCE_H_ + +#include "flang/Semantics/symbol.h" + +namespace Fortran::semantics { + +// For a set or scope of symbols, computes the transitive closure of their +// dependences due to their types, bounds, specific procedures, interfaces, +// initialization, storage association, &c. Includes the original symbol +// or members of the original set. Does not include dependences from +// subprogram definitions, only their interfaces. +enum DependenceCollectionFlags { + NoDependenceCollectionFlags = 0, + IncludeOriginalSymbols = 1 << 0, + FollowUseAssociations = 1 << 1, + IncludeSpecificsOfGenerics = 1 << 2, + IncludeUsesOfGenerics = 1 << 3, + NotJustForOneModule = 1 << 4, +}; + +SymbolVector CollectAllDependences(const SymbolVector &, + int = NoDependenceCollectionFlags, const Scope * = nullptr); +SymbolVector CollectAllDependences( + const Scope &, int = NoDependenceCollectionFlags); + +} // namespace Fortran::semantics +#endif // FORTRAN_SEMANTICS_SYMBOL_DEPENDENCE_H_ diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h index fb670528f3ce4..2050d23870657 100644 --- a/flang/include/flang/Semantics/tools.h +++ b/flang/include/flang/Semantics/tools.h @@ -43,7 +43,7 @@ const Scope &GetProgramUnitOrBlockConstructContaining(const Symbol &); const Scope *FindModuleContaining(const Scope &); const Scope *FindModuleOrSubmoduleContaining(const Scope &); -const Scope *FindModuleFileContaining(const Scope &); +bool IsInModuleFile(const Scope &); const Scope *FindPureProcedureContaining(const Scope &); const Scope *FindOpenACCConstructContaining(const Scope *); diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt index 109bc2dbb8569..c711103e793d8 100644 --- a/flang/lib/Semantics/CMakeLists.txt +++ b/flang/lib/Semantics/CMakeLists.txt @@ -48,6 +48,7 @@ add_flang_library(FortranSemantics runtime-type-info.cpp scope.cpp semantics.cpp + symbol-dependence.cpp symbol.cpp tools.cpp type.cpp diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp index f9d64485f1407..36e47f7999538 100644 --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -124,10 +124,7 @@ class CheckHelper { } return msg; } - bool InModuleFile() const { - return FindModuleFileContaining(context_.FindScope(messages_.at())) != - nullptr; - } + bool InModuleFile() const { return context_.IsInModuleFile(messages_.at()); } template parser::Message *Warn(FeatureOrUsageWarning warning, A &&...x) { if (!context_.ShouldWarn(warning) || InModuleFile()) { @@ -139,8 +136,7 @@ class CheckHelper { template parser::Message *Warn( FeatureOrUsageWarning warning, parser::CharBlock source, A &&...x) { - if (!context_.ShouldWarn(warning) || - FindModuleFileContaining(context_.FindScope(source))) { + if (!context_.ShouldWarn(warning) || context_.IsInModuleFile(source)) { return nullptr; } else { return messages_.Say(warning, source, std::forward(x)...); @@ -4050,7 +4046,7 @@ void DistinguishabilityHelper::Add(const Symbol &generic, GenericKind kind, } void DistinguishabilityHelper::Check(const Scope &scope) { - if (FindModuleFileContaining(scope)) { + if (IsInModuleFile(scope)) { // Distinguishability was checked when the module was created; // don't let optional warnings then become errors now. return; @@ -4109,7 +4105,7 @@ void DistinguishabilityHelper::SayNotDistinguishable(const Scope &scope, if (isWarning && (!context_.ShouldWarn( common::LanguageFeature::IndistinguishableSpecifics) || - FindModuleFileContaining(scope))) { + IsInModuleFile(scope))) { return; } std::string name1{proc1.name().ToString()}; diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp index f4af738284ed7..50c6d51abebdf 100644 --- a/flang/lib/Semantics/expression.cpp +++ b/flang/lib/Semantics/expression.cpp @@ -1852,8 +1852,7 @@ void ArrayConstructorContext::Push(MaybeExpr &&x) { } else { if (!(messageDisplayedSet_ & 2)) { exprAnalyzer_.Say( - "Values in array constructor must have the same declared type " - "when no explicit type appears"_err_en_US); // C7110 + "Values in array constructor must have the same declared type when no explicit type appears"_err_en_US); // C7110 messageDisplayedSet_ |= 2; } } diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp index 82c8536902eb2..1a27449e5a786 100644 --- a/flang/lib/Semantics/mod-file.cpp +++ b/flang/lib/Semantics/mod-file.cpp @@ -15,6 +15,7 @@ #include "flang/Parser/unparse.h" #include "flang/Semantics/scope.h" #include "flang/Semantics/semantics.h" +#include "flang/Semantics/symbol-dependence.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" #include "llvm/Support/FileSystem.h" @@ -130,7 +131,7 @@ static std::string ModFileName(const SourceName &name, return ancestorName.empty() ? result : ancestorName + '-' + result; } -// Write the module file for symbol, which must be a module or submodule. +// Writes the module file for symbol, which must be a module or submodule. void ModFileWriter::Write(const Symbol &symbol) { const auto &module{symbol.get()}; if (symbol.test(Symbol::Flag::ModFile) || module.moduleFileHash()) { @@ -142,26 +143,15 @@ void ModFileWriter::Write(const Symbol &symbol) { std::string path{context_.moduleDirectory() + '/' + ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())}; - std::set hermeticModuleNames; - hermeticModuleNames.insert(symbol.name().ToString()); - UnorderedSymbolSet additionalModules; - PutSymbols(DEREF(symbol.scope()), - hermeticModuleFileOutput_ ? &additionalModules : nullptr); - auto asStr{GetAsString(symbol)}; - while (!additionalModules.empty()) { - UnorderedSymbolSet nextPass{std::move(additionalModules)}; - additionalModules.clear(); - for (const Symbol &modSym : nextPass) { - if (!modSym.owner().IsIntrinsicModules() && - hermeticModuleNames.find(modSym.name().ToString()) == - hermeticModuleNames.end()) { - hermeticModuleNames.insert(modSym.name().ToString()); - PutSymbols(DEREF(modSym.scope()), &additionalModules); - asStr += GetAsString(modSym); - } - } + SymbolVector dependenceClosure; + if (hermeticModuleFileOutput_ && !isSubmodule_) { + dependenceClosure = CollectAllDependences(DEREF(symbol.scope()), + FollowUseAssociations | IncludeUsesOfGenerics | + IncludeSpecificsOfGenerics | NotJustForOneModule); } - + PutSymbols(DEREF(symbol.scope()), hermeticModuleFileOutput_); + auto asStr{GetAsString(&symbol, symbol.name().ToString())}; + asStr += PutDependencyModules(symbol.name().ToString(), dependenceClosure); ModuleCheckSumType checkSum; if (std::error_code error{ WriteFile(path, asStr, checkSum, context_.debugModuleWriter())}) { @@ -177,9 +167,9 @@ void ModFileWriter::WriteClosure(llvm::raw_ostream &out, const Symbol &symbol, !nonIntrinsicModulesWritten.insert(symbol).second) { return; } - PutSymbols(DEREF(symbol.scope()), /*hermeticModules=*/nullptr); + PutSymbols(DEREF(symbol.scope()), /*omitModules=*/false); needsBuf_.clear(); // omit module checksums - auto str{GetAsString(symbol)}; + auto str{GetAsString(&symbol, symbol.name().ToString())}; for (auto depRef : std::move(usedNonIntrinsicModules_)) { WriteClosure(out, *depRef, nonIntrinsicModulesWritten); } @@ -188,22 +178,23 @@ void ModFileWriter::WriteClosure(llvm::raw_ostream &out, const Symbol &symbol, // Return the entire body of the module file // and clear saved uses, decls, and contains. -std::string ModFileWriter::GetAsString(const Symbol &symbol) { +std::string ModFileWriter::GetAsString(const Symbol *symbol, std::string name) { std::string buf; llvm::raw_string_ostream all{buf}; all << needs_.str(); needs_.str().clear(); - auto &details{symbol.get()}; - if (!details.isSubmodule()) { - all << "module " << symbol.name(); + const ModuleDetails *details{ + symbol ? &symbol->get() : nullptr}; + if (!details || !details->isSubmodule()) { + all << "module " << name; } else { - auto *parent{details.parent()->symbol()}; - auto *ancestor{details.ancestor()->symbol()}; + auto *parent{details->parent()->symbol()}; + auto *ancestor{details->ancestor()->symbol()}; all << "submodule(" << ancestor->name(); if (parent != ancestor) { all << ':' << parent->name(); } - all << ") " << symbol.name(); + all << ") " << name; } all << '\n' << uses_.str(); uses_.str().clear(); @@ -223,79 +214,22 @@ std::string ModFileWriter::GetAsString(const Symbol &symbol) { // Collect symbols from constant and specification expressions that are being // referenced directly from other modules; they may require new USE // associations. -static void HarvestSymbolsNeededFromOtherModules( - SourceOrderedSymbolSet &, const Scope &); -static void HarvestSymbolsNeededFromOtherModules( - SourceOrderedSymbolSet &set, const Symbol &symbol, const Scope &scope) { - auto HarvestBound{[&](const Bound &bound) { - if (const auto &expr{bound.GetExplicit()}) { - for (SymbolRef ref : evaluate::CollectSymbols(*expr)) { - set.emplace(*ref); - } - } - }}; - auto HarvestShapeSpec{[&](const ShapeSpec &shapeSpec) { - HarvestBound(shapeSpec.lbound()); - HarvestBound(shapeSpec.ubound()); - }}; - auto HarvestArraySpec{[&](const ArraySpec &arraySpec) { - for (const auto &shapeSpec : arraySpec) { - HarvestShapeSpec(shapeSpec); - } - }}; - - if (symbol.has()) { - if (symbol.scope()) { - HarvestSymbolsNeededFromOtherModules(set, *symbol.scope()); - } - } else if (const auto &generic{symbol.detailsIf()}; - generic && generic->derivedType()) { - const Symbol &dtSym{*generic->derivedType()}; - if (dtSym.has()) { - if (dtSym.scope()) { - HarvestSymbolsNeededFromOtherModules(set, *dtSym.scope()); - } - } else { - CHECK(dtSym.has() || dtSym.has()); - } - } else if (const auto *object{symbol.detailsIf()}) { - HarvestArraySpec(object->shape()); - HarvestArraySpec(object->coshape()); - if (IsNamedConstant(symbol) || scope.IsDerivedType()) { - if (object->init()) { - for (SymbolRef ref : evaluate::CollectSymbols(*object->init())) { - set.emplace(*ref); - } - } - } - } else if (const auto *proc{symbol.detailsIf()}) { - if (proc->init() && *proc->init() && scope.IsDerivedType()) { - set.emplace(**proc->init()); - } - } else if (const auto *subp{symbol.detailsIf()}) { - for (const Symbol *dummy : subp->dummyArgs()) { - if (dummy) { - HarvestSymbolsNeededFromOtherModules(set, *dummy, scope); - } - } - if (subp->isFunction()) { - HarvestSymbolsNeededFromOtherModules(set, subp->result(), scope); - } - } -} - -static void HarvestSymbolsNeededFromOtherModules( - SourceOrderedSymbolSet &set, const Scope &scope) { - for (const auto &[_, symbol] : scope) { - HarvestSymbolsNeededFromOtherModules(set, *symbol, scope); +static SourceOrderedSymbolSet HarvestSymbolsNeededFromOtherModules( + const Scope &scope) { + SourceOrderedSymbolSet set; + for (const Symbol &symbol : CollectAllDependences(scope)) { + set.insert(symbol); } + return set; } -void ModFileWriter::PrepareRenamings(const Scope &scope) { - // Identify use-associated symbols already in scope under some name - std::map useMap; - for (const auto &[name, symbolRef] : scope) { - const Symbol *symbol{&*symbolRef}; +// Identifies use-associated symbols already in scope under some name +using UseRenameMap = std::map; +template +UseRenameMap GetUseRenamings(const SYMBOLS &symbols) { + UseRenameMap useMap; + for (const Symbol &sym : symbols) { + const Symbol *symbol{&sym}; while (const auto *hostAssoc{symbol->detailsIf()}) { symbol = &hostAssoc->symbol(); } @@ -303,65 +237,80 @@ void ModFileWriter::PrepareRenamings(const Scope &scope) { useMap.emplace(&use->symbol(), symbol); } } + return useMap; +} + +void ModFileWriter::PrepareRenamings(const Scope &scope) { // Collect symbols needed from other modules - SourceOrderedSymbolSet symbolsNeeded; - HarvestSymbolsNeededFromOtherModules(symbolsNeeded, scope); + SourceOrderedSymbolSet symbolsNeeded{ + HarvestSymbolsNeededFromOtherModules(scope)}; // Establish any necessary renamings of symbols in other modules // to their names in this scope, creating those new names when needed. + UseRenameMap useMap{GetUseRenamings(symbolsNeeded)}; auto &renamings{context_.moduleFileOutputRenamings()}; - for (SymbolRef s : symbolsNeeded) { - if (s->owner().kind() != Scope::Kind::Module) { - // Not a USE'able name from a module's top scope; - // component, binding, dummy argument, &c. - continue; - } - const Scope *sMod{FindModuleContaining(s->owner())}; - if (!sMod || sMod == &scope) { - continue; - } - if (auto iter{useMap.find(&*s)}; iter != useMap.end()) { - renamings.emplace(&*s, iter->second->name()); - continue; - } - SourceName rename{s->name()}; - if (const Symbol * found{scope.FindSymbol(s->name())}) { - if (found == &*s) { - continue; // available in scope - } - if (const auto *generic{found->detailsIf()}) { - if (generic->derivedType() == &*s || generic->specific() == &*s) { - continue; - } - } else if (found->has()) { - if (&found->GetUltimate() == &*s) { - continue; // already use-associated with same name - } + for (const Symbol &sym : symbolsNeeded) { + if (auto iter{useMap.find(&sym)}; iter != useMap.end()) { + renamings.emplace(&sym, iter->second->name()); + } else { + PutRenamedSymbolUse(scope, sym); + } + } +} + +std::optional ModFileWriter::GetUseName( + const Scope &scope, const Symbol &sym, const Scope &symMod) { + if (sym.owner().kind() != Scope::Kind::Module) { + // Not a USE'able name from a module's top scope; + // component, binding, dummy argument, &c. + return std::nullopt; + } + if (&symMod == &scope) { + return std::nullopt; // already here + } + if (const Symbol *found{scope.FindSymbol(sym.name())}) { + if (found == &sym) { + return std::nullopt; // available in scope + } + if (const auto *generic{found->detailsIf()}) { + if (generic->derivedType() == &sym || generic->specific() == &sym) { + return std::nullopt; } - if (&s->owner() != &found->owner()) { // Symbol needs renaming - rename = scope.context().SaveTempName( - DEREF(sMod->symbol()).name().ToString() + "$" + - s->name().ToString()); + } else if (found->has()) { + if (&found->GetUltimate() == &sym) { + return std::nullopt; // already use-associated with same name } } - // Symbol is used in this scope but not visible under its name - if (sMod->parent().IsIntrinsicModules()) { - uses_ << "use,intrinsic::"; - } else { - uses_ << "use "; + if (&sym.owner() != &found->owner()) { // Symbol needs renaming + return context_.SaveTempName(DEREF(symMod.symbol()).name().ToString() + + "$" + sym.name().ToString()); } - uses_ << DEREF(sMod->symbol()).name() << ",only:"; - if (rename != s->name()) { - uses_ << rename << "=>"; - renamings.emplace(&s->GetUltimate(), rename); + } + return sym.name(); // needs USE, no renaming +} + +void ModFileWriter::PutRenamedSymbolUse(const Scope &scope, const Symbol &sym) { + if (const Scope *symMod{FindModuleContaining(sym.owner())}) { + if (auto rename{GetUseName(scope, sym, *symMod)}) { + // Symbol is used in this scope but not visible under its name + if (symMod->parent().IsIntrinsicModules()) { + uses_ << "use,intrinsic::"; + } else { + uses_ << "use "; + } + uses_ << DEREF(symMod->symbol()).name() << ",only:"; + if (*rename != sym.name()) { + uses_ << *rename << "=>"; + context_.moduleFileOutputRenamings().emplace( + &sym.GetUltimate(), *rename); + } + uses_ << sym.name() << '\n'; + useExtraAttrs_ << "private::" << *rename << '\n'; } - uses_ << s->name() << '\n'; - useExtraAttrs_ << "private::" << rename << '\n'; } } // Put out the visible symbols from scope. -void ModFileWriter::PutSymbols( - const Scope &scope, UnorderedSymbolSet *hermeticModules) { +void ModFileWriter::PutSymbols(const Scope &scope, bool omitModules) { SymbolVector sorted; SymbolVector uses; auto &renamings{context_.moduleFileOutputRenamings()}; @@ -369,12 +318,10 @@ void ModFileWriter::PutSymbols( PrepareRenamings(scope); SourceOrderedSymbolSet modules; CollectSymbols(scope, sorted, uses, modules); - // Write module files for dependencies first so that their + // Write module files for compiled dependency modules first so that their // hashes are known. - for (const Symbol &mod : modules) { - if (hermeticModules) { - hermeticModules->insert(mod); - } else { + if (!omitModules) { + for (const Symbol &mod : modules) { Write(mod); // It's possible that the module's file already existed and // without its own hash due to being embedded in a hermetic @@ -412,6 +359,127 @@ void ModFileWriter::PutSymbols( renamings = std::move(previousRenamings); } +// Combines duplicate top-level symbols, which can arise when +// merging dependency modules from hermetic module files. +static SymbolVector CombineDuplicateSymbols(const SymbolVector &originals) { + std::map distinct; + SymbolVector symbols; + symbols.reserve(originals.size()); + for (const Symbol &symbol : originals) { + if (symbol.owner().IsModule()) { + if (auto pair{distinct.emplace(symbol.name(), &symbol)}; !pair.second) { + // name already present + if (symbol.has()) { + pair.first->second = &symbol; + } else { + continue; + } + } + } + symbols.emplace_back(symbol); + } + return symbols; +} + +std::string ModFileWriter::PutDependencyModules( + std::string originalModuleName, const SymbolVector &order) { + // Partition symbols by module name. + // Ignore symbols from intrinsic modules and the original module. + std::map perModuleName; + for (const Symbol &symbol : order) { + if (const Scope *module{FindModuleContaining(symbol.owner())}) { + if (!module->parent().IsIntrinsicModules()) { + if (auto name{module->GetName()}) { + if (*name != originalModuleName) { + perModuleName[name->ToString()].emplace_back(symbol); + } + } + } + } + } + std::string result; + for (const auto &[moduleName, symbols] : perModuleName) { + SymbolVector distinct{CombineDuplicateSymbols(symbols)}; + result += PutDependencyModule(moduleName, distinct); + } + return result; +} + +std::string ModFileWriter::PutDependencyModule( + const std::string &moduleName, const SymbolVector &needed) { + const Scope &scope{DEREF(FindModuleContaining(needed.front()->owner()))}; + CHECK(scope.IsModule()); + // The needed symbols for this module may depend on use-associated + // symbols from other dependency modules, so collect them. + SymbolVector symbols{CollectAllDependences( + needed, IncludeOriginalSymbols | IncludeUsesOfGenerics, &scope)}; + SymbolVector order, namelists, generics; + std::set names, commonNames, genericNames; + order.reserve(symbols.size()); + for (const Symbol &symbol : symbols) { + std::string symbolName{symbol.name().ToString()}; + if (symbol.test(Symbol::Flag::ParentComp) || + symbol.test(Symbol::Flag::CompilerCreated) || + !symbol.owner().IsModule()) { + } else if (symbol.has()) { + if (commonNames.find(symbolName) == commonNames.end()) { + order.push_back(symbol); + commonNames.insert(symbolName); + } + } else if (const auto *generic{symbol.detailsIf()}) { + if (names.find(symbolName) == names.end()) { + if (generic->specific() && + &generic->specific()->owner() == &symbol.owner()) { + order.push_back(*generic->specific()); + names.insert(symbolName); + } else if (generic->derivedType() && + &generic->derivedType()->owner() == &symbol.owner()) { + order.push_back(*generic->derivedType()); + names.insert(symbolName); + } + } + if (genericNames.find(symbolName) == genericNames.end()) { + generics.push_back(symbol); + genericNames.insert(symbolName); + } + } else if (names.find(symbolName) != names.end()) { + if (const auto *use{symbol.detailsIf()}) { + if (use->symbol().GetUltimate().has()) { + order.push_back(symbol); // pmk duplicates? + } + } + } else if (symbol.has()) { + namelists.push_back(symbol); + names.insert(symbolName); + } else { + order.push_back(symbol); + names.insert(symbolName); + } + } + order.insert(order.end(), generics.begin(), generics.end()); + order.insert(order.end(), namelists.begin(), namelists.end()); + // Emit the symbols + std::string buf; + llvm::raw_string_ostream typeBindings{buf}; + auto &renamings{context_.moduleFileOutputRenamings()}; + auto previousRenamings{std::move(renamings)}; + UseRenameMap useMap{GetUseRenamings(order)}; + for (const Symbol &sym : order) { + if (auto iter{useMap.find(&sym)}; iter != useMap.end()) { + renamings.emplace(&sym, iter->second->name()); + } else if (const Scope *from{FindModuleContaining(sym.owner())}; + from && from->GetName().value().ToString() != moduleName) { + PutRenamedSymbolUse(scope, sym); + } else { + PutSymbol(typeBindings, sym); + } + } + // pmk TODO: equivalence sets + CHECK(typeBindings.str().empty()); + renamings = std::move(previousRenamings); + return GetAsString(nullptr, moduleName); +} + // Emit components in order bool ModFileWriter::PutComponents(const Symbol &typeSymbol) { const auto &scope{DEREF(typeSymbol.scope())}; @@ -1578,14 +1646,15 @@ Scope *ModFileReader::Read(SourceName name, std::optional isIntrinsic, // their own nested scope that will be visible only to USE statements // within the module file. Scope *previousHermetic{context_.currentHermeticModuleFileScope()}; + Scope *hermeticScope{nullptr}; if (parseTree.v.size() > 1) { parser::Program hermeticModules{std::move(parseTree.v)}; parseTree.v.emplace_back(std::move(hermeticModules.v.front())); hermeticModules.v.pop_front(); - Scope &hermeticScope{topScope.MakeScope(Scope::Kind::Global)}; - context_.set_currentHermeticModuleFileScope(&hermeticScope); - ResolveNames(context_, hermeticModules, hermeticScope); - for (auto &[_, ref] : hermeticScope) { + hermeticScope = &topScope.MakeScope(Scope::Kind::Global); + context_.set_currentHermeticModuleFileScope(hermeticScope); + ResolveNames(context_, hermeticModules, *hermeticScope); + for (auto &[_, ref] : *hermeticScope) { CHECK(ref->has()); ref->set(Symbol::Flag::ModFile); } diff --git a/flang/lib/Semantics/mod-file.h b/flang/lib/Semantics/mod-file.h index 9e5724089b3c5..309c23f78f44e 100644 --- a/flang/lib/Semantics/mod-file.h +++ b/flang/lib/Semantics/mod-file.h @@ -66,9 +66,16 @@ class ModFileWriter { void WriteAll(const Scope &); void WriteOne(const Scope &); void Write(const Symbol &); - std::string GetAsString(const Symbol &); + std::string GetAsString(const Symbol *, std::string); void PrepareRenamings(const Scope &); - void PutSymbols(const Scope &, UnorderedSymbolSet *hermetic); + std::optional GetUseName( + const Scope &, const Symbol &, const Scope &symMod); + void PutRenamedSymbolUse(const Scope &, const Symbol &); + void PutSymbols(const Scope &, bool omitModules); + std::string PutDependencyModules( + std::string originalModuleName, const SymbolVector &); + std::string PutDependencyModule( + const std::string &modName, const SymbolVector &); // Returns true if a derived type with bindings and "contains" was emitted bool PutComponents(const Symbol &); void PutSymbol(llvm::raw_ostream &, const Symbol &); diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index d0336c9cb661d..b3bbd682bde60 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -3504,8 +3504,7 @@ ModuleVisitor::SymbolRename ModuleVisitor::AddUse( useModuleScope_->GetName().value()); return {}; } - if (useSymbol->attrs().test(Attr::PRIVATE) && - !FindModuleFileContaining(currScope())) { + if (useSymbol->attrs().test(Attr::PRIVATE) && !IsInModuleFile(currScope())) { // Privacy is not enforced in module files so that generic interfaces // can be resolved to private specific procedures in specification // expressions. @@ -3617,10 +3616,11 @@ static bool CheckCompatibleDistinctUltimates(SemanticsContext &context, if (const auto *useObject{useUltimate.detailsIf()}) { auto localType{evaluate::DynamicType::From(localUltimate)}; auto useType{evaluate::DynamicType::From(useUltimate)}; - if (localUltimate.size() != useUltimate.size() || - (localType && - (!useType || !localType->IsTkLenCompatibleWith(*useType) || - !useType->IsTkLenCompatibleWith(*localType))) || + if (localUltimate.size() != useUltimate.size()) { + isError = true; + } else if ((localType && + (!useType || !localType->IsTkLenCompatibleWith(*useType) || + !useType->IsTkLenCompatibleWith(*localType))) || (!localType && useType)) { isError = true; } else if (IsNamedConstant(localUltimate)) { @@ -7424,7 +7424,8 @@ void DeclarationVisitor::SetType( std::optional DeclarationVisitor::ResolveDerivedType( const parser::Name &name) { Scope &outer{NonDerivedTypeScope()}; - Symbol *symbol{FindSymbol(outer, name)}; + Symbol *original{FindSymbol(outer, name)}; + Symbol *symbol{original}; Symbol *ultimate{symbol ? &symbol->GetUltimate() : nullptr}; auto *generic{ultimate ? ultimate->detailsIf() : nullptr}; if (generic) { @@ -7437,11 +7438,12 @@ std::optional DeclarationVisitor::ResolveDerivedType( (generic && &ultimate->owner() == &outer)) { if (allowForwardReferenceToDerivedType()) { if (!symbol) { - symbol = &MakeSymbol(outer, name.source, Attrs{}); + symbol = original = &MakeSymbol(outer, name.source, Attrs{}); Resolve(name, *symbol); } else if (generic) { // forward ref to type with later homonymous generic - symbol = &outer.MakeSymbol(name.source, Attrs{}, UnknownDetails{}); + symbol = original = + &outer.MakeSymbol(name.source, Attrs{}, UnknownDetails{}); generic->set_derivedType(*symbol); name.symbol = symbol; } @@ -7461,7 +7463,7 @@ std::optional DeclarationVisitor::ResolveDerivedType( if (CheckUseError(name)) { return std::nullopt; } else if (symbol->GetUltimate().has()) { - return DerivedTypeSpec{name.source, *symbol}; + return DerivedTypeSpec{name.source, *original}; } else { Say(name, "'%s' is not a derived type"_err_en_US); return std::nullopt; diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp index ab78605d01f4c..ad901e75820f4 100644 --- a/flang/lib/Semantics/semantics.cpp +++ b/flang/lib/Semantics/semantics.cpp @@ -42,6 +42,7 @@ #include "flang/Semantics/expression.h" #include "flang/Semantics/scope.h" #include "flang/Semantics/symbol.h" +#include "flang/Semantics/tools.h" #include "flang/Support/default-kinds.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" @@ -456,13 +457,7 @@ void SemanticsContext::UpdateScopeIndex( } bool SemanticsContext::IsInModuleFile(parser::CharBlock source) const { - for (const Scope *scope{&FindScope(source)}; !scope->IsGlobal(); - scope = &scope->parent()) { - if (scope->IsModuleFile()) { - return true; - } - } - return false; + return semantics::IsInModuleFile(FindScope(source)); } void SemanticsContext::PopConstruct() { @@ -648,7 +643,8 @@ bool Semantics::Perform() { PerformStatementSemantics(context_, program_) && CanonicalizeDirectives(context_.messages(), program_) && ModFileWriter{context_} - .set_hermeticModuleFileOutput(hermeticModuleFileOutput_) + .set_hermeticModuleFileOutput( + hermeticModuleFileOutput_ || getenv("PMK_HERMETIC")) .WriteAll(); } diff --git a/flang/lib/Semantics/symbol-dependence.cpp b/flang/lib/Semantics/symbol-dependence.cpp new file mode 100644 index 0000000000000..2591f609f3d00 --- /dev/null +++ b/flang/lib/Semantics/symbol-dependence.cpp @@ -0,0 +1,356 @@ +//===-- lib/Semantics/symbol-dependence.cpp -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "flang/Semantics/symbol-dependence.h" +#include "flang/Common/idioms.h" +#include "flang/Common/restorer.h" +#include "flang/Common/visit.h" +#include + +static constexpr bool EnableDebugging{false}; + +namespace Fortran::semantics { + +// Helper class that collects all of the symbol dependences for a +// given symbol. +class Collector { +public: + explicit Collector(int flags) : flags_{flags} {} + + void CollectSymbolDependences(const Symbol &); + UnorderedSymbolSet MustFollowDependences() { return std::move(dependences_); } + SymbolVector AllDependences() { return std::move(mentions_); } + +private: + // This symbol is depended upon and its declaration must precede + // the symbol of interest. + void MustFollow(const Symbol &x) { + if (!possibleImports_ || !DoesScopeContain(possibleImports_, x)) { + dependences_.insert(x); + } + } + // This symbol is depended upon, but is not necessarily a dependence + // that must precede the symbol of interest in the output of the + // topological sort. + void Need(const Symbol &x) { + if (mentioned_.insert(x).second) { + mentions_.emplace_back(x); + } + } + void Need(const Symbol *x) { + if (x) { + Need(*x); + } + } + + // These overloads of Collect() are mutally recursive, so they're + // packaged as member functions of a class. + void Collect(const Symbol &x) { + Need(x); + const auto *subp{x.detailsIf()}; + if ((subp && subp->isInterface()) || IsDummy(x) || + x.has() || x.has()) { + // can be forward-referenced + } else { + MustFollow(x); + } + } + void Collect(SymbolRef x) { Collect(*x); } + template void Collect(const std::optional &x) { + if (x) { + Collect(*x); + } + } + template void Collect(const A *x) { + if (x) { + Collect(*x); + } + } + void Collect(const UnorderedSymbolSet &x) { + for (const Symbol &symbol : x) { + Collect(symbol); + } + } + void Collect(const SourceOrderedSymbolSet &x) { + for (const Symbol &symbol : x) { + Collect(symbol); + } + } + void Collect(const SymbolVector &x) { + for (const Symbol &symbol : x) { + Collect(symbol); + } + } + void Collect(const Scope &x) { Collect(x.GetSymbols()); } + template void Collect(const evaluate::Expr &x) { + UnorderedSymbolSet exprSyms{evaluate::CollectSymbols(x)}; + for (const Symbol &sym : exprSyms) { + if (!sym.owner().IsDerivedType()) { + Collect(sym); + } + } + } + void Collect(const DeclTypeSpec &type) { + if (type.category() == DeclTypeSpec::Category::Character) { + Collect(type.characterTypeSpec().length()); + } else { + Collect(type.AsDerived()); + } + } + void Collect(const DerivedTypeSpec &type) { + const Symbol &typeSym{type.originalTypeSymbol()}; + if (!derivedTypeReferenceCanBeForward_ || !type.parameters().empty()) { + MustFollow(typeSym); + } + Need(typeSym); + for (const auto &[_, value] : type.parameters()) { + Collect(value); + } + } + void Collect(const ParamValue &x) { Collect(x.GetExplicit()); } + void Collect(const Bound &x) { Collect(x.GetExplicit()); } + void Collect(const ShapeSpec &x) { + Collect(x.lbound()); + Collect(x.ubound()); + } + void Collect(const ArraySpec &x) { + for (const ShapeSpec &shapeSpec : x) { + Collect(shapeSpec); + } + } + + UnorderedSymbolSet mentioned_, dependences_; + SymbolVector mentions_; + int flags_{NoDependenceCollectionFlags}; + bool derivedTypeReferenceCanBeForward_{false}; + const Scope *possibleImports_{nullptr}; +}; + +void Collector::CollectSymbolDependences(const Symbol &symbol) { + if (symbol.has() || symbol.has()) { + // type will be picked up later for the function result, if any + } else if (symbol.has() || symbol.has() || + symbol.has()) { + } else if (IsAllocatableOrPointer(symbol) && symbol.owner().IsDerivedType()) { + bool saveCanBeForward{derivedTypeReferenceCanBeForward_}; + derivedTypeReferenceCanBeForward_ = true; + Collect(symbol.GetType()); + derivedTypeReferenceCanBeForward_ = saveCanBeForward; + } else { + Collect(symbol.GetType()); + } + common::visit( + common::visitors{ + [this, &symbol](const ObjectEntityDetails &x) { + Collect(x.shape()); + Collect(x.coshape()); + if (IsNamedConstant(symbol) || symbol.owner().IsDerivedType()) { + Collect(x.init()); + } + Need(x.commonBlock()); + if (const auto *set{FindEquivalenceSet(symbol)}) { + for (const EquivalenceObject &equivObject : *set) { + Need(equivObject.symbol); + } + } + }, + [this, &symbol](const ProcEntityDetails &x) { + Collect(x.rawProcInterface()); + if (symbol.owner().IsDerivedType()) { + Collect(x.init()); + } + }, + [this](const ProcBindingDetails &x) { Need(x.symbol()); }, + [this, &symbol](const SubprogramDetails &x) { + // Note dummy arguments & result symbol without dependence, unless + // the subprogram is an interface block that might need to IMPORT + // a type. + bool needImports{x.isInterface()}; + auto restorer{common::ScopedSet( + possibleImports_, needImports ? symbol.scope() : nullptr)}; + for (const Symbol *dummy : x.dummyArgs()) { + if (dummy) { + Need(*dummy); + if (needImports) { + CollectSymbolDependences(*dummy); + } + } + } + if (x.isFunction()) { + Need(x.result()); + if (needImports) { + CollectSymbolDependences(x.result()); + } + } + }, + [this, &symbol](const DerivedTypeDetails &x) { + Collect(symbol.scope()); + for (const auto &[_, symbolRef] : x.finals()) { + Need(*symbolRef); + } + }, + [this](const GenericDetails &x) { + Collect(x.derivedType()); + Collect(x.specific()); + if (flags_ & IncludeUsesOfGenerics) { + for (const Symbol &use : x.uses()) { + Collect(use); + } + } + if (flags_ & IncludeSpecificsOfGenerics) { + for (const Symbol &specific : x.specificProcs()) { + Collect(specific); + } + } + }, + [this](const NamelistDetails &x) { + for (const Symbol &symbol : x.objects()) { + Collect(symbol); + } + }, + [this](const CommonBlockDetails &x) { + for (auto ref : x.objects()) { + Collect(*ref); + } + }, + [this](const UseDetails &x) { + if (flags_ & FollowUseAssociations) { + Need(x.symbol()); + } + }, + [this](const HostAssocDetails &x) { Need(x.symbol()); }, + [](const auto &) {}, + }, + symbol.details()); +} + +SymbolVector CollectAllDependences(const Scope &scope, int flags) { + SymbolVector basis{scope.GetSymbols()}; + return CollectAllDependences(basis, flags, &scope); +} + +// Returns a vector of symbols, topologically sorted by dependence +SymbolVector CollectAllDependences( + const SymbolVector &original, int flags, const Scope *forScope) { + std::queue work; + UnorderedSymbolSet enqueued; + for (const Symbol &symbol : original) { + if (!symbol.test(Symbol::Flag::CompilerCreated)) { + work.push(&symbol); + enqueued.insert(symbol); + } + } + // For each symbol, collect its dependences into "topology". + // The "visited" vector and "enqueued" set hold all of the + // symbols considered. + std::map topology; + std::vector visited; + visited.reserve(2 * original.size()); + std::optional forModuleName; + if (forScope && !(flags & NotJustForOneModule)) { + if (const Scope *forModule{FindModuleContaining(*forScope)}) { + forModuleName = forModule->GetName(); + } + } + while (!work.empty()) { + const Symbol &symbol{*work.front()}; + work.pop(); + visited.push_back(&symbol); + Collector collector{flags}; + bool doCollection{true}; + if (forModuleName) { + if (const Scope *symModule{FindModuleContaining(symbol.owner())}) { + if (auto symModName{symModule->GetName()}) { + doCollection = *forModuleName == *symModName; + } + } + } + if (doCollection) { + collector.CollectSymbolDependences(symbol); + } + auto dependences{collector.MustFollowDependences()}; + auto mentions{collector.AllDependences()}; + if constexpr (EnableDebugging) { + for (const Symbol &need : dependences) { + llvm::errs() << "symbol " << symbol << " must follow " << need << '\n'; + } + for (const Symbol &need : mentions) { + llvm::errs() << "symbol " << symbol << " needs " << need << '\n'; + } + } + CHECK(topology.find(&symbol) == topology.end()); + topology.emplace(&symbol, std::move(dependences)); + for (const Symbol &symbol : mentions) { + if (!symbol.test(Symbol::Flag::CompilerCreated)) { + if (enqueued.insert(symbol).second) { + work.push(&symbol); + } + } + } + } + CHECK(enqueued.size() == visited.size()); + // Topological sorting + // Subtle: This inverted topology map uses a SymbolVector, not a set + // of symbols, so that the order of symbols in the final output remains + // deterministic. + std::map invertedTopology; + for (const Symbol *symbol : visited) { + invertedTopology[symbol] = SymbolVector{}; + } + std::map numWaitingFor; + for (const Symbol *symbol : visited) { + auto topoIter{topology.find(symbol)}; + CHECK(topoIter != topology.end()); + const auto &needs{topoIter->second}; + if (needs.empty()) { + work.push(symbol); + } else { + numWaitingFor[symbol] = needs.size(); + for (const Symbol &need : needs) { + invertedTopology[&need].push_back(*symbol); + } + } + } + CHECK(visited.size() == work.size() + numWaitingFor.size()); + SymbolVector resultVector; + while (!work.empty()) { + const Symbol &symbol{*work.front()}; + work.pop(); + resultVector.push_back(symbol); + auto enqueuedIter{enqueued.find(symbol)}; + CHECK(enqueuedIter != enqueued.end()); + enqueued.erase(enqueuedIter); + if (auto invertedIter{invertedTopology.find(&symbol)}; + invertedIter != invertedTopology.end()) { + for (const Symbol &neededBy : invertedIter->second) { + std::size_t stillAwaiting{numWaitingFor[&neededBy] - 1}; + if (stillAwaiting == 0) { + work.push(&neededBy); + } else { + numWaitingFor[&neededBy] = stillAwaiting; + } + } + } + } + if constexpr (EnableDebugging) { + llvm::errs() << "Topological sort failed in CollectAllDependences\n"; + for (const Symbol &remnant : enqueued) { + auto topoIter{topology.find(&remnant)}; + CHECK(topoIter != topology.end()); + llvm::errs() << " remnant symbol " << remnant << " needs:\n"; + for (const Symbol &n : topoIter->second) { + llvm::errs() << " " << n << '\n'; + } + } + } + CHECK(enqueued.empty()); + CHECK(resultVector.size() == visited.size()); + return resultVector; +} + +} // namespace Fortran::semantics diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp index 0380207927ad3..23bb16ad30106 100644 --- a/flang/lib/Semantics/symbol.cpp +++ b/flang/lib/Semantics/symbol.cpp @@ -277,13 +277,27 @@ void GenericDetails::CopyFrom(const GenericDetails &from) { CHECK(!derivedType_ || derivedType_ == from.derivedType_); derivedType_ = from.derivedType_; } - for (std::size_t i{0}; i < from.specificProcs_.size(); ++i) { - if (llvm::none_of(specificProcs_, [&](const Symbol &mySymbol) { - return &mySymbol.GetUltimate() == - &from.specificProcs_[i]->GetUltimate(); - })) { - specificProcs_.push_back(from.specificProcs_[i]); - bindingNames_.push_back(from.bindingNames_[i]); + for (std::size_t j{0}; j < from.specificProcs_.size(); ++j) { + auto fromSpecific{from.specificProcs_[j]}; + SourceName fromBinding{from.bindingNames_[j]}; + const Symbol &fromUltimate{fromSpecific->GetUltimate()}; + const Scope *fromModule{FindModuleContaining(fromUltimate.owner())}; + auto fromModuleName{fromModule ? fromModule->GetName() : std::nullopt}; + bool addit{true}; + for (std::size_t k{0}; addit && k < specificProcs_.size(); ++k) { + const Symbol &ultimate{specificProcs_[k]->GetUltimate()}; + if (&fromUltimate == &ultimate) { + addit = false; + } else if (fromBinding == bindingNames_[k]) { + const Scope *module{FindModuleContaining(ultimate.owner())}; + auto moduleName{module ? module->GetName() : std::nullopt}; + addit = + !(fromModuleName && moduleName && *fromModuleName == *moduleName); + } + } + if (addit) { + specificProcs_.push_back(fromSpecific); + bindingNames_.push_back(fromBinding); } } } diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp index d27d250b3f11e..17a76becc1306 100644 --- a/flang/lib/Semantics/tools.cpp +++ b/flang/lib/Semantics/tools.cpp @@ -6,15 +6,15 @@ // //===----------------------------------------------------------------------===// -#include "flang/Parser/tools.h" +#include "flang/Semantics/tools.h" #include "flang/Common/indirection.h" #include "flang/Parser/dump-parse-tree.h" #include "flang/Parser/message.h" #include "flang/Parser/parse-tree.h" +#include "flang/Parser/tools.h" #include "flang/Semantics/scope.h" #include "flang/Semantics/semantics.h" #include "flang/Semantics/symbol.h" -#include "flang/Semantics/tools.h" #include "flang/Semantics/type.h" #include "flang/Support/Fortran.h" #include "llvm/Support/raw_ostream.h" @@ -58,9 +58,16 @@ const Scope *FindModuleOrSubmoduleContaining(const Scope &start) { }); } -const Scope *FindModuleFileContaining(const Scope &start) { - return FindScopeContaining( - start, [](const Scope &scope) { return scope.IsModuleFile(); }); +bool IsInModuleFile(const Scope &start) { + for (const Scope *scope{&start};; scope = &scope->parent()) { + if (scope->IsModuleFile() || + scope == scope->context().currentHermeticModuleFileScope()) { + return true; + } else if (scope->IsTopLevel()) { + break; + } + } + return false; } const Scope &GetProgramUnitContaining(const Scope &start) { @@ -1158,7 +1165,7 @@ std::optional CheckAccessibleSymbol( const Scope &scope, const Symbol &symbol) { if (IsAccessible(symbol, scope)) { return std::nullopt; - } else if (FindModuleFileContaining(scope)) { + } else if (IsInModuleFile(scope)) { // Don't enforce component accessibility checks in module files; // there may be forward-substituted named constants of derived type // whose structure constructors reference private components. @@ -1845,4 +1852,18 @@ bool HadUseError( } } +bool CheckForSymbolMatch(const SomeExpr *lhs, const SomeExpr *rhs) { + if (lhs && rhs) { + if (SymbolVector lhsSymbols{evaluate::GetSymbolVector(*lhs)}; + !lhsSymbols.empty()) { + const Symbol &first{*lhsSymbols.front()}; + for (const Symbol &symbol : evaluate::GetSymbolVector(*rhs)) { + if (first == symbol) { + return true; + } + } + } + } + return false; +} } // namespace Fortran::semantics diff --git a/flang/lib/Semantics/type.cpp b/flang/lib/Semantics/type.cpp index 964a37e1c822b..b8f35c9bf4507 100644 --- a/flang/lib/Semantics/type.cpp +++ b/flang/lib/Semantics/type.cpp @@ -22,9 +22,19 @@ namespace Fortran::semantics { +static const Symbol &ResolveOriginalTypeSymbol(const Symbol *symbol) { + symbol = &symbol->GetUltimate(); + if (const auto *generic{symbol->detailsIf()}) { + CHECK(generic->derivedType() != nullptr); + return generic->derivedType()->GetUltimate(); + } else { + return *symbol; + } +} + DerivedTypeSpec::DerivedTypeSpec(SourceName name, const Symbol &typeSymbol) : name_{name}, originalTypeSymbol_{typeSymbol}, - typeSymbol_{typeSymbol.GetUltimate()} { + typeSymbol_{ResolveOriginalTypeSymbol(&typeSymbol)} { CHECK(typeSymbol_.has()); } DerivedTypeSpec::DerivedTypeSpec(const DerivedTypeSpec &that) = default; @@ -252,16 +262,12 @@ static bool MatchKindParams(const Symbol &typeSymbol, } bool DerivedTypeSpec::MatchesOrExtends(const DerivedTypeSpec &that) const { - const Symbol *typeSymbol{&typeSymbol_}; - while (typeSymbol != &that.typeSymbol_) { - if (const DerivedTypeSpec * - parent{typeSymbol->GetParentTypeSpec(typeSymbol->scope())}) { - typeSymbol = &parent->typeSymbol_; - } else { - return false; - } + const DerivedTypeSpec *type{this}; + while (type && + !evaluate::AreSameDerivedTypeIgnoringTypeParameters(*type, that)) { + type = type->typeSymbol_.GetParentTypeSpec(type->typeSymbol_.scope()); } - return MatchKindParams(*typeSymbol, *this, that); + return type && MatchKindParams(type->typeSymbol_, *this, that); } class InstantiateHelper { diff --git a/flang/test/Semantics/modfile03.f90 b/flang/test/Semantics/modfile03.f90 index eb3136f0aa8bc..399547ca41958 100644 --- a/flang/test/Semantics/modfile03.f90 +++ b/flang/test/Semantics/modfile03.f90 @@ -69,7 +69,7 @@ pure integer function f1(i) module m5b use m5a, only: k2 => k1, l2 => l1, f2 => f1 interface - subroutine s(x, y) + subroutine s5b(x, y) import f2, l2 character(l2, k2) :: x character(f2(l2)) :: y @@ -82,7 +82,7 @@ subroutine s(x, y) ! use m5a,only:l2=>l1 ! use m5a,only:f2=>f1 ! interface -! subroutine s(x,y) +! subroutine s5b(x,y) ! import::f2 ! import::l2 ! character(l2,4)::x @@ -142,7 +142,7 @@ module m6d module m6e use m6a, only: t2 => t1 interface - subroutine s(x) + subroutine s6e(x) import t2 type(t2) :: x end subroutine @@ -152,7 +152,7 @@ subroutine s(x) !module m6e ! use m6a,only:t2=>t1 ! interface -! subroutine s(x) +! subroutine s6e(x) ! import::t2 ! type(t2)::x ! end diff --git a/flang/test/Semantics/modfile65.f90 b/flang/test/Semantics/modfile65.f90 index 249255e02129f..3eabf47d4d55e 100644 --- a/flang/test/Semantics/modfile65.f90 +++ b/flang/test/Semantics/modfile65.f90 @@ -42,12 +42,12 @@ module m4 !use m2,only:n !use m3,only:m !end +!module m1 +!integer(4),parameter::n=123_4 +!end !module m2 !use m1,only:n !end !module m3 !use m1,only:m=>n !end -!module m1 -!integer(4),parameter::n=123_4 -!end diff --git a/flang/test/Semantics/modfile78.F90 b/flang/test/Semantics/modfile78.F90 index 19b9ac39de934..f612c2fa30d64 100644 --- a/flang/test/Semantics/modfile78.F90 +++ b/flang/test/Semantics/modfile78.F90 @@ -27,7 +27,6 @@ module modfile78c !CHECK: integer(4)::global_variable !CHECK: end !CHECK: module modfile78b -!CHECK: use modfile78a,only:global_variable !CHECK: contains !CHECK: subroutine test() !CHECK: end From 28d4df0ce012af443c05a7e9b754123dc99db58f Mon Sep 17 00:00:00 2001 From: Peter Klausler Date: Mon, 7 Jul 2025 08:29:55 -0700 Subject: [PATCH 2/2] more --- flang/lib/Semantics/mod-file.cpp | 49 +++++++++++++++++++---- flang/lib/Semantics/mod-file.h | 3 +- flang/lib/Semantics/symbol-dependence.cpp | 7 ++++ 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp index 1a27449e5a786..f9b0c61fe0696 100644 --- a/flang/lib/Semantics/mod-file.cpp +++ b/flang/lib/Semantics/mod-file.cpp @@ -28,6 +28,8 @@ #include #include +static constexpr bool pruneHermeticModuleFiles{true}; + namespace Fortran::semantics { using namespace parser::literals; @@ -144,14 +146,40 @@ void ModFileWriter::Write(const Symbol &symbol) { ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())}; SymbolVector dependenceClosure; - if (hermeticModuleFileOutput_ && !isSubmodule_) { + if (hermeticModuleFileOutput_ && !isSubmodule_ && pruneHermeticModuleFiles) { dependenceClosure = CollectAllDependences(DEREF(symbol.scope()), FollowUseAssociations | IncludeUsesOfGenerics | IncludeSpecificsOfGenerics | NotJustForOneModule); } - PutSymbols(DEREF(symbol.scope()), hermeticModuleFileOutput_); + UnorderedSymbolSet fullHermeticModules; + PutSymbols(DEREF(symbol.scope()), hermeticModuleFileOutput_, + hermeticModuleFileOutput_ && !pruneHermeticModuleFiles + ? &fullHermeticModules + : nullptr); auto asStr{GetAsString(&symbol, symbol.name().ToString())}; - asStr += PutDependencyModules(symbol.name().ToString(), dependenceClosure); + if (!dependenceClosure.empty()) { + // Emit minimal modules on which this module depends, if emitting a + // hermetic module file + asStr += PutDependencyModules(symbol.name().ToString(), dependenceClosure); + } else if (!fullHermeticModules.empty()) { + // Emit full (complete) modules on which this module depends + std::set hermeticModuleNames; + hermeticModuleNames.insert(symbol.name().ToString()); + while (!fullHermeticModules.empty()) { + UnorderedSymbolSet nextPass{std::move(fullHermeticModules)}; + fullHermeticModules.clear(); + for (const Symbol &modSym : nextPass) { + if (!modSym.owner().IsIntrinsicModules() && + hermeticModuleNames.find(modSym.name().ToString()) == + hermeticModuleNames.end()) { + hermeticModuleNames.insert(modSym.name().ToString()); + PutSymbols(DEREF(modSym.scope()), /*omitModules=*/false, + &fullHermeticModules); + asStr += GetAsString(&modSym, modSym.name().ToString()); + } + } + } + } ModuleCheckSumType checkSum; if (std::error_code error{ WriteFile(path, asStr, checkSum, context_.debugModuleWriter())}) { @@ -167,7 +195,8 @@ void ModFileWriter::WriteClosure(llvm::raw_ostream &out, const Symbol &symbol, !nonIntrinsicModulesWritten.insert(symbol).second) { return; } - PutSymbols(DEREF(symbol.scope()), /*omitModules=*/false); + PutSymbols(DEREF(symbol.scope()), /*omitModules=*/false, + /*fullHermeticModules=*/nullptr); needsBuf_.clear(); // omit module checksums auto str{GetAsString(&symbol, symbol.name().ToString())}; for (auto depRef : std::move(usedNonIntrinsicModules_)) { @@ -310,7 +339,8 @@ void ModFileWriter::PutRenamedSymbolUse(const Scope &scope, const Symbol &sym) { } // Put out the visible symbols from scope. -void ModFileWriter::PutSymbols(const Scope &scope, bool omitModules) { +void ModFileWriter::PutSymbols(const Scope &scope, bool omitModules, + UnorderedSymbolSet *fullHermeticModules) { SymbolVector sorted; SymbolVector uses; auto &renamings{context_.moduleFileOutputRenamings()}; @@ -320,7 +350,11 @@ void ModFileWriter::PutSymbols(const Scope &scope, bool omitModules) { CollectSymbols(scope, sorted, uses, modules); // Write module files for compiled dependency modules first so that their // hashes are known. - if (!omitModules) { + if (fullHermeticModules) { + for (const Symbol &mod : modules) { + fullHermeticModules->insert(mod); + } + } else if (!omitModules) { for (const Symbol &mod : modules) { Write(mod); // It's possible that the module's file already existed and @@ -445,7 +479,7 @@ std::string ModFileWriter::PutDependencyModule( } else if (names.find(symbolName) != names.end()) { if (const auto *use{symbol.detailsIf()}) { if (use->symbol().GetUltimate().has()) { - order.push_back(symbol); // pmk duplicates? + order.push_back(symbol); } } } else if (symbol.has()) { @@ -474,7 +508,6 @@ std::string ModFileWriter::PutDependencyModule( PutSymbol(typeBindings, sym); } } - // pmk TODO: equivalence sets CHECK(typeBindings.str().empty()); renamings = std::move(previousRenamings); return GetAsString(nullptr, moduleName); diff --git a/flang/lib/Semantics/mod-file.h b/flang/lib/Semantics/mod-file.h index 309c23f78f44e..6b25542c138ca 100644 --- a/flang/lib/Semantics/mod-file.h +++ b/flang/lib/Semantics/mod-file.h @@ -71,7 +71,8 @@ class ModFileWriter { std::optional GetUseName( const Scope &, const Symbol &, const Scope &symMod); void PutRenamedSymbolUse(const Scope &, const Symbol &); - void PutSymbols(const Scope &, bool omitModules); + void PutSymbols( + const Scope &, bool omitModules, UnorderedSymbolSet *fullHermeticModules); std::string PutDependencyModules( std::string originalModuleName, const SymbolVector &); std::string PutDependencyModule( diff --git a/flang/lib/Semantics/symbol-dependence.cpp b/flang/lib/Semantics/symbol-dependence.cpp index 2591f609f3d00..83ed2991203ea 100644 --- a/flang/lib/Semantics/symbol-dependence.cpp +++ b/flang/lib/Semantics/symbol-dependence.cpp @@ -158,6 +158,13 @@ void Collector::CollectSymbolDependences(const Symbol &symbol) { Need(equivObject.symbol); } } + if (symbol.owner().IsModule()) { + if (const EquivalenceSet *equiv{FindEquivalenceSet(symbol)}) { + for (const EquivalenceObject &eqObj : *equiv) { + Need(eqObj.symbol); + } + } + } }, [this, &symbol](const ProcEntityDetails &x) { Collect(x.rawProcInterface());