[flang-commits] [flang] [flang] Don't emit needless symbols to hermetic module files (PR #144765)
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Mon Jul 14 08:29:40 PDT 2025
https://github.com/klausler updated https://github.com/llvm/llvm-project/pull/144765
>From de069ae733d7b002a7317f6288bdf49bd6b86267 Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
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 <typename FeatureOrUsageWarning, typename... A>
parser::Message *Warn(FeatureOrUsageWarning warning, A &&...x) {
if (!context_.ShouldWarn(warning) || InModuleFile()) {
@@ -139,8 +136,7 @@ class CheckHelper {
template <typename FeatureOrUsageWarning, typename... A>
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<A>(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<ModuleDetails>()};
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<std::string> 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<ModuleDetails>()};
- if (!details.isSubmodule()) {
- all << "module " << symbol.name();
+ const ModuleDetails *details{
+ symbol ? &symbol->get<ModuleDetails>() : 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<DerivedTypeDetails>()) {
- if (symbol.scope()) {
- HarvestSymbolsNeededFromOtherModules(set, *symbol.scope());
- }
- } else if (const auto &generic{symbol.detailsIf<GenericDetails>()};
- generic && generic->derivedType()) {
- const Symbol &dtSym{*generic->derivedType()};
- if (dtSym.has<DerivedTypeDetails>()) {
- if (dtSym.scope()) {
- HarvestSymbolsNeededFromOtherModules(set, *dtSym.scope());
- }
- } else {
- CHECK(dtSym.has<UseDetails>() || dtSym.has<UseErrorDetails>());
- }
- } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
- 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<ProcEntityDetails>()}) {
- if (proc->init() && *proc->init() && scope.IsDerivedType()) {
- set.emplace(**proc->init());
- }
- } else if (const auto *subp{symbol.detailsIf<SubprogramDetails>()}) {
- 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<const Symbol *, const Symbol *> 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<const Symbol *, const Symbol *>;
+template <typename SYMBOLS>
+UseRenameMap GetUseRenamings(const SYMBOLS &symbols) {
+ UseRenameMap useMap;
+ for (const Symbol &sym : symbols) {
+ const Symbol *symbol{&sym};
while (const auto *hostAssoc{symbol->detailsIf<HostAssocDetails>()}) {
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<GenericDetails>()}) {
- if (generic->derivedType() == &*s || generic->specific() == &*s) {
- continue;
- }
- } else if (found->has<UseDetails>()) {
- 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<SourceName> 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<GenericDetails>()}) {
+ 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<UseDetails>()) {
+ 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<SourceName, const Symbol *> 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<GenericDetails>()) {
+ 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<std::string, SymbolVector> 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<std::string> 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<CommonBlockDetails>()) {
+ if (commonNames.find(symbolName) == commonNames.end()) {
+ order.push_back(symbol);
+ commonNames.insert(symbolName);
+ }
+ } else if (const auto *generic{symbol.detailsIf<GenericDetails>()}) {
+ 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<UseDetails>()}) {
+ if (use->symbol().GetUltimate().has<GenericDetails>()) {
+ order.push_back(symbol); // pmk duplicates?
+ }
+ }
+ } else if (symbol.has<NamelistDetails>()) {
+ 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<bool> 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<ModuleDetails>());
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<SourceName> 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<ObjectEntityDetails>()}) {
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<DerivedTypeSpec> 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<GenericDetails>() : nullptr};
if (generic) {
@@ -7437,11 +7438,12 @@ std::optional<DerivedTypeSpec> 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<DerivedTypeSpec> DeclarationVisitor::ResolveDerivedType(
if (CheckUseError(name)) {
return std::nullopt;
} else if (symbol->GetUltimate().has<DerivedTypeDetails>()) {
- 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 <queue>
+
+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<SubprogramDetails>()};
+ if ((subp && subp->isInterface()) || IsDummy(x) ||
+ x.has<CommonBlockDetails>() || x.has<NamelistDetails>()) {
+ // can be forward-referenced
+ } else {
+ MustFollow(x);
+ }
+ }
+ void Collect(SymbolRef x) { Collect(*x); }
+ template <typename A> void Collect(const std::optional<A> &x) {
+ if (x) {
+ Collect(*x);
+ }
+ }
+ template <typename A> 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 <typename T> void Collect(const evaluate::Expr<T> &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<ProcBindingDetails>() || symbol.has<SubprogramDetails>()) {
+ // type will be picked up later for the function result, if any
+ } else if (symbol.has<UseDetails>() || symbol.has<UseErrorDetails>() ||
+ symbol.has<HostAssocDetails>()) {
+ } 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<const Symbol *> 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<const Symbol *, UnorderedSymbolSet> topology;
+ std::vector<const Symbol *> visited;
+ visited.reserve(2 * original.size());
+ std::optional<SourceName> 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<const Symbol *, SymbolVector> invertedTopology;
+ for (const Symbol *symbol : visited) {
+ invertedTopology[symbol] = SymbolVector{};
+ }
+ std::map<const Symbol *, std::size_t> 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<parser::MessageFormattedText> 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<GenericDetails>()}) {
+ 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<DerivedTypeDetails>());
}
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 <pklausler at nvidia.com>
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 <variant>
#include <vector>
+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<std::string> 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<UseDetails>()}) {
if (use->symbol().GetUltimate().has<GenericDetails>()) {
- order.push_back(symbol); // pmk duplicates?
+ order.push_back(symbol);
}
}
} else if (symbol.has<NamelistDetails>()) {
@@ -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<SourceName> 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());
More information about the flang-commits
mailing list