[flang] Better renaming in module files (#93106)

When a symbol from one module is used in another without an explicit USE
association, the module file output code may need to use another name
for it -- either with a name that is already available via USE
association with renaming, or by means of a new private USE association,
possibly with renaming to avoid a clash.

Module file output was dealing properly with names of derived types, but
wasn't accounting for symbols that appear in expressions other than
initializations. This was specifically a problem with an application
module that had a call to a NOPASS type-bound procedure in an array
bound specification expression, which semantics had resolved to the name
of a private module function.

This patch implements renaming, when necessary, for all symbols
appearing in expressions and type names, and replaces the previous
implementation of derived type renaming. It also gets a little smarter
about avoiding the creation of compiler-generated names when a name from
another module has been brought into scope already by means of USE
association with renaming.
This commit is contained in:
Peter Klausler 2024-05-23 16:20:51 -07:00 committed by GitHub
parent 47b485c052
commit 9ce8e63ceb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 261 additions and 169 deletions

View File

@ -126,8 +126,7 @@ public:
constexpr Result result() const { return result_; }
constexpr DynamicType GetType() const { return result_.GetType(); }
llvm::raw_ostream &AsFortran(llvm::raw_ostream &,
const parser::CharBlock *derivedTypeRename = nullptr) const;
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
protected:
std::vector<Element> Reshape(const ConstantSubscripts &) const;

View File

@ -735,8 +735,7 @@ public:
StructureConstructor &Add(const semantics::Symbol &, Expr<SomeType> &&);
int Rank() const { return 0; }
DynamicType GetType() const;
llvm::raw_ostream &AsFortran(llvm::raw_ostream &,
const parser::CharBlock *derivedTypeRename = nullptr) const;
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
private:
std::optional<Expr<SomeType>> CreateParentComponent(const Symbol &) const;

View File

@ -272,9 +272,6 @@ const semantics::DerivedTypeSpec *GetDerivedTypeSpec(
const semantics::DerivedTypeSpec *GetParentTypeSpec(
const semantics::DerivedTypeSpec &);
std::string DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec &,
const parser::CharBlock *derivedTypeRename = nullptr);
template <TypeCategory CATEGORY, int KIND = 0> struct TypeBase {
static constexpr TypeCategory category{CATEGORY};
static constexpr int kind{KIND};

View File

@ -110,6 +110,9 @@ public:
evaluate::FoldingContext &foldingContext() { return foldingContext_; }
parser::AllCookedSources &allCookedSources() { return allCookedSources_; }
ModuleDependences &moduleDependences() { return moduleDependences_; }
std::map<const Symbol *, SourceName> &moduleFileOutputRenamings() {
return moduleFileOutputRenamings_;
}
SemanticsContext &set_location(
const std::optional<parser::CharBlock> &location) {
@ -299,6 +302,7 @@ private:
std::list<parser::Program> modFileParseTrees_;
std::unique_ptr<CommonBlockMap> commonBlockMap_;
ModuleDependences moduleDependences_;
std::map<const Symbol *, SourceName> moduleFileOutputRenamings_;
};
class Semantics {

View File

@ -14,6 +14,7 @@
#include "flang/Evaluate/fold.h"
#include "flang/Evaluate/tools.h"
#include "flang/Parser/characters.h"
#include "flang/Semantics/semantics.h"
#include "flang/Semantics/symbol.h"
#include "llvm/Support/raw_ostream.h"
@ -53,7 +54,7 @@ static void ShapeAsFortran(llvm::raw_ostream &o,
template <typename RESULT, typename VALUE>
llvm::raw_ostream &ConstantBase<RESULT, VALUE>::AsFortran(
llvm::raw_ostream &o, const parser::CharBlock *derivedTypeRename) const {
llvm::raw_ostream &o) const {
bool hasNonDefaultLowerBound{printLbounds && HasNonDefaultLowerBound()};
if (Rank() > 1 || hasNonDefaultLowerBound) {
o << "reshape(";
@ -85,8 +86,7 @@ llvm::raw_ostream &ConstantBase<RESULT, VALUE>::AsFortran(
o << ".false." << '_' << Result::kind;
}
} else {
StructureConstructor{result_.derivedTypeSpec(), value}.AsFortran(
o, derivedTypeRename);
StructureConstructor{result_.derivedTypeSpec(), value}.AsFortran(o);
}
}
if (Rank() > 0) {
@ -124,9 +124,89 @@ llvm::raw_ostream &Constant<Type<TypeCategory::Character, KIND>>::AsFortran(
return o;
}
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const Symbol &symbol,
std::optional<parser::CharBlock> name = std::nullopt) {
const auto &renamings{symbol.owner().context().moduleFileOutputRenamings()};
if (auto iter{renamings.find(&symbol)}; iter != renamings.end()) {
return o << iter->second.ToString();
} else if (name) {
return o << name->ToString();
} else {
return o << symbol.name().ToString();
}
}
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::string &lit) {
return o << parser::QuoteCharacterLiteral(lit);
}
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u16string &lit) {
return o << parser::QuoteCharacterLiteral(lit);
}
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u32string &lit) {
return o << parser::QuoteCharacterLiteral(lit);
}
template <typename A>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const A &x) {
return x.AsFortran(o);
}
template <typename A>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, common::Reference<A> x) {
return EmitVar(o, *x);
}
template <typename A>
llvm::raw_ostream &EmitVar(
llvm::raw_ostream &o, const A *p, const char *kw = nullptr) {
if (p) {
if (kw) {
o << kw;
}
EmitVar(o, *p);
}
return o;
}
template <typename A>
llvm::raw_ostream &EmitVar(
llvm::raw_ostream &o, const std::optional<A> &x, const char *kw = nullptr) {
if (x) {
if (kw) {
o << kw;
}
EmitVar(o, *x);
}
return o;
}
template <typename A, bool COPY>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o,
const common::Indirection<A, COPY> &p, const char *kw = nullptr) {
if (kw) {
o << kw;
}
EmitVar(o, p.value());
return o;
}
template <typename A>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::shared_ptr<A> &p) {
CHECK(p);
return EmitVar(o, *p);
}
template <typename... A>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::variant<A...> &u) {
common::visit([&](const auto &x) { EmitVar(o, x); }, u);
return o;
}
llvm::raw_ostream &ActualArgument::AssumedType::AsFortran(
llvm::raw_ostream &o) const {
return o << symbol_->name().ToString();
return EmitVar(o, *symbol_);
}
llvm::raw_ostream &ActualArgument::AsFortran(llvm::raw_ostream &o) const {
@ -504,15 +584,37 @@ llvm::raw_ostream &ExpressionBase<RESULT>::AsFortran(
return o;
}
llvm::raw_ostream &StructureConstructor::AsFortran(
llvm::raw_ostream &o, const parser::CharBlock *derivedTypeRename) const {
o << DerivedTypeSpecAsFortran(result_.derivedTypeSpec(), derivedTypeRename);
static std::string DerivedTypeSpecAsFortran(
const semantics::DerivedTypeSpec &spec) {
std::string buf;
llvm::raw_string_ostream ss{buf};
EmitVar(ss, spec.typeSymbol(), spec.name());
char ch{'('};
for (const auto &[name, value] : spec.parameters()) {
ss << ch << name.ToString() << '=';
ch = ',';
if (value.isAssumed()) {
ss << '*';
} else if (value.isDeferred()) {
ss << ':';
} else {
value.GetExplicit()->AsFortran(ss);
}
}
if (ch != '(') {
ss << ')';
}
return ss.str();
}
llvm::raw_ostream &StructureConstructor::AsFortran(llvm::raw_ostream &o) const {
o << DerivedTypeSpecAsFortran(result_.derivedTypeSpec());
if (values_.empty()) {
o << '(';
} else {
char ch{'('};
for (const auto &[symbol, value] : values_) {
value.value().AsFortran(o << ch << symbol->name().ToString() << '=');
value.value().AsFortran(EmitVar(o << ch, *symbol) << '=');
ch = ',';
}
}
@ -568,101 +670,6 @@ std::string SomeDerived::AsFortran() const {
}
}
std::string DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec &spec,
const parser::CharBlock *derivedTypeRename) {
std::string buf;
llvm::raw_string_ostream ss{buf};
ss << (derivedTypeRename ? *derivedTypeRename : spec.name()).ToString();
char ch{'('};
for (const auto &[name, value] : spec.parameters()) {
ss << ch << name.ToString() << '=';
ch = ',';
if (value.isAssumed()) {
ss << '*';
} else if (value.isDeferred()) {
ss << ':';
} else {
value.GetExplicit()->AsFortran(ss);
}
}
if (ch != '(') {
ss << ')';
}
return ss.str();
}
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const Symbol &symbol) {
return o << symbol.name().ToString();
}
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::string &lit) {
return o << parser::QuoteCharacterLiteral(lit);
}
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u16string &lit) {
return o << parser::QuoteCharacterLiteral(lit);
}
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u32string &lit) {
return o << parser::QuoteCharacterLiteral(lit);
}
template <typename A>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const A &x) {
return x.AsFortran(o);
}
template <typename A>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, common::Reference<A> x) {
return EmitVar(o, *x);
}
template <typename A>
llvm::raw_ostream &EmitVar(
llvm::raw_ostream &o, const A *p, const char *kw = nullptr) {
if (p) {
if (kw) {
o << kw;
}
EmitVar(o, *p);
}
return o;
}
template <typename A>
llvm::raw_ostream &EmitVar(
llvm::raw_ostream &o, const std::optional<A> &x, const char *kw = nullptr) {
if (x) {
if (kw) {
o << kw;
}
EmitVar(o, *x);
}
return o;
}
template <typename A, bool COPY>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o,
const common::Indirection<A, COPY> &p, const char *kw = nullptr) {
if (kw) {
o << kw;
}
EmitVar(o, p.value());
return o;
}
template <typename A>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::shared_ptr<A> &p) {
CHECK(p);
return EmitVar(o, *p);
}
template <typename... A>
llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::variant<A...> &u) {
common::visit([&](const auto &x) { EmitVar(o, x); }, u);
return o;
}
llvm::raw_ostream &BaseObject::AsFortran(llvm::raw_ostream &o) const {
return EmitVar(o, u);
}

View File

@ -46,11 +46,11 @@ struct ModHeader {
};
static std::optional<SourceName> GetSubmoduleParent(const parser::Program &);
static void CollectSymbols(const Scope &, SymbolVector &, SymbolVector &,
std::map<const Symbol *, SourceName> &, UnorderedSymbolSet &);
static void CollectSymbols(
const Scope &, SymbolVector &, SymbolVector &, UnorderedSymbolSet &);
static void PutPassName(llvm::raw_ostream &, const std::optional<SourceName> &);
static void PutInit(llvm::raw_ostream &, const Symbol &, const MaybeExpr &,
const parser::Expr *, const std::map<const Symbol *, SourceName> &);
const parser::Expr *);
static void PutInit(llvm::raw_ostream &, const MaybeIntExpr &);
static void PutBound(llvm::raw_ostream &, const Bound &);
static void PutShapeSpec(llvm::raw_ostream &, const ShapeSpec &);
@ -200,47 +200,102 @@ std::string ModFileWriter::GetAsString(const Symbol &symbol) {
return all.str();
}
// Collect symbols from initializations that are being referenced directly
// from other modules; they may require new USE associations.
static void HarvestInitializerSymbols(
SourceOrderedSymbolSet &set, const Scope &scope) {
for (const auto &[_, symbol] : scope) {
if (symbol->has<DerivedTypeDetails>()) {
if (symbol->scope()) {
HarvestInitializerSymbols(set, *symbol->scope());
// 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);
}
} else if (const auto &generic{symbol->detailsIf<GenericDetails>()};
generic && generic->derivedType()) {
const Symbol &dtSym{*generic->derivedType()};
if (dtSym.has<DerivedTypeDetails>()) {
if (dtSym.scope()) {
HarvestInitializerSymbols(set, *dtSym.scope());
}
} else {
CHECK(dtSym.has<UseDetails>() || dtSym.has<UseErrorDetails>());
}
}};
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 if (IsNamedConstant(*symbol) || scope.IsDerivedType()) {
if (const auto *object{symbol->detailsIf<ObjectEntityDetails>()}) {
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()) {
set.emplace(**proc->init());
} 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);
}
}
void ModFileWriter::PrepareRenamings(const Scope &scope) {
SourceOrderedSymbolSet symbolsInInits;
HarvestInitializerSymbols(symbolsInInits, scope);
for (SymbolRef s : symbolsInInits) {
// 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};
while (const auto *hostAssoc{symbol->detailsIf<HostAssocDetails>()}) {
symbol = &hostAssoc->symbol();
}
if (const auto *use{symbol->detailsIf<UseDetails>()}) {
useMap.emplace(&use->symbol(), symbol);
}
}
// Collect symbols needed from other modules
SourceOrderedSymbolSet symbolsNeeded;
HarvestSymbolsNeededFromOtherModules(symbolsNeeded, scope);
// Establish any necessary renamings of symbols in other modules
// to their names in this scope, creating those new names when needed.
auto &renamings{context_.moduleFileOutputRenamings()};
for (SymbolRef s : symbolsNeeded) {
const Scope *sMod{FindModuleContaining(s->owner())};
if (!sMod) {
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()};
@ -272,10 +327,10 @@ void ModFileWriter::PrepareRenamings(const Scope &scope) {
uses_ << DEREF(sMod->symbol()).name() << ",only:";
if (rename != s->name()) {
uses_ << rename << "=>";
renamings.emplace(&*s, rename);
}
uses_ << s->name() << '\n';
useExtraAttrs_ << "private::" << rename << '\n';
renamings_.emplace(&*s, rename);
}
}
@ -283,9 +338,11 @@ void ModFileWriter::PrepareRenamings(const Scope &scope) {
void ModFileWriter::PutSymbols(const Scope &scope) {
SymbolVector sorted;
SymbolVector uses;
auto &renamings{context_.moduleFileOutputRenamings()};
auto previousRenamings{std::move(renamings)};
PrepareRenamings(scope);
UnorderedSymbolSet modules;
CollectSymbols(scope, sorted, uses, renamings_, modules);
CollectSymbols(scope, sorted, uses, modules);
// Write module files for dependencies first so that their
// hashes are known.
for (auto ref : modules) {
@ -318,6 +375,7 @@ void ModFileWriter::PutSymbols(const Scope &scope) {
}
}
CHECK(typeBindings.str().empty());
renamings = std::move(previousRenamings);
}
// Emit components in order
@ -521,7 +579,7 @@ void ModFileWriter::PutDECStructure(
}
decls_ << ref->name();
PutShape(decls_, object->shape(), '(', ')');
PutInit(decls_, *ref, object->init(), nullptr, renamings_);
PutInit(decls_, *ref, object->init(), nullptr);
emittedDECFields_.insert(*ref);
} else if (any) {
break; // any later use of this structure will use RECORD/str/
@ -767,8 +825,7 @@ static inline SourceName NameInModuleFile(const Symbol &symbol) {
// Collect the symbols of this scope sorted by their original order, not name.
// Generics and namelists are exceptions: they are sorted after other symbols.
void CollectSymbols(const Scope &scope, SymbolVector &sorted,
SymbolVector &uses, std::map<const Symbol *, SourceName> &renamings,
UnorderedSymbolSet &modules) {
SymbolVector &uses, UnorderedSymbolSet &modules) {
SymbolVector namelist, generics;
auto symbols{scope.GetSymbols()};
std::size_t commonSize{scope.commonBlocks().size()};
@ -878,8 +935,7 @@ void ModFileWriter::PutObjectEntity(
getSymbolAttrsToWrite(symbol));
PutShape(os, details.shape(), '(', ')');
PutShape(os, details.coshape(), '[', ']');
PutInit(os, symbol, details.init(), details.unanalyzedPDTComponentInit(),
renamings_);
PutInit(os, symbol, details.init(), details.unanalyzedPDTComponentInit());
os << '\n';
if (auto tkr{GetIgnoreTKR(symbol)}; !tkr.empty()) {
os << "!dir$ ignore_tkr(";
@ -973,25 +1029,12 @@ void ModFileWriter::PutTypeParam(llvm::raw_ostream &os, const Symbol &symbol) {
}
void PutInit(llvm::raw_ostream &os, const Symbol &symbol, const MaybeExpr &init,
const parser::Expr *unanalyzed,
const std::map<const Symbol *, SourceName> &renamings) {
const parser::Expr *unanalyzed) {
if (IsNamedConstant(symbol) || symbol.owner().IsDerivedType()) {
const char *assign{symbol.attrs().test(Attr::POINTER) ? "=>" : "="};
if (unanalyzed) {
parser::Unparse(os << assign, *unanalyzed);
} else if (init) {
if (const auto *dtConst{
evaluate::UnwrapExpr<evaluate::Constant<evaluate::SomeDerived>>(
*init)}) {
const Symbol &dtSym{dtConst->result().derivedTypeSpec().typeSymbol()};
if (auto iter{renamings.find(&dtSym)}; iter != renamings.end()) {
// Initializer is a constant whose derived type's name has
// been brought into scope from a module under a new name
// to avoid a conflict.
dtConst->AsFortran(os << assign, &iter->second);
return;
}
}
init->AsFortran(os << assign);
}
}

View File

@ -57,7 +57,6 @@ private:
llvm::raw_string_ostream decls_{declsBuf_};
llvm::raw_string_ostream contains_{containsBuf_};
bool isSubmodule_{false};
std::map<const Symbol *, SourceName> renamings_;
void WriteAll(const Scope &);
void WriteOne(const Scope &);

View File

@ -135,10 +135,8 @@ module m6d
end
!Expect: m6d.mod
!module m6d
! use m6a,only:t1
! use m6a,only:t2=>t1
! private::t1
! type(t2),parameter::p=t1()
! type(t2),parameter::p=t2()
!end
module m6e
@ -178,3 +176,49 @@ end
! use m7a,only:x
! private::x
!end
module m8a
private foo
type t
contains
procedure, nopass :: foo
end type
contains
pure integer function foo(n)
integer, intent(in) :: n
foo = n
end
end
!Expect: m8a.mod
!module m8a
!type::t
!contains
!procedure,nopass::foo
!end type
!private::foo
!contains
!pure function foo(n)
!integer(4),intent(in)::n
!integer(4)::foo
!end
!end
module m8b
use m8a
contains
subroutine foo(x,a)
type(t), intent(in) :: x
real a(x%foo(10))
end
end
!Expect: m8b.mod
!module m8b
!use m8a,only:m8a$foo=>foo
!use m8a,only:t
!private::m8a$foo
!contains
!subroutine foo(x,a)
!type(t),intent(in)::x
!real(4)::a(1_8:int(m8a$foo(10_4),kind=8))
!end
!end