Skip to content

Commit

Permalink
[flang] Fix bugs related to merging generics during USE
Browse files Browse the repository at this point in the history
When the same generic name is use-associated from two modules, the
generics are merged into a single one in the current scope. This change
fixes some bugs in that process.

When a generic is merged, it can have two specific procedures with the
same name as the generic (c.f. module m7c in modfile07.f90). We were
disallowing that by checking for duplicate names in the generic rather
than duplicate symbols. Changing `namesSeen` to `symbolsSeen` in
`ResolveSpecificsInGeneric` fixes that.

We weren't including each USE of those generics in the .mod file so in
some cases they were incorrect. Extend GenericDetails to specify all
use-associated symbols that are merged into the generic. This is used to
write out .mod files correctly.

The distinguishability check for specific procedures of a generic
sometimes have to refer to procedures from a use-associated generic in
error messages. In that case we don't have the source location of the
procedure so adapt the message to say where is was use-associated from.
This requires passing the scope through the checks to make that
determination.

Differential Revision: https://reviews.llvm.org/D92492
  • Loading branch information
tskeith committed Dec 2, 2020
1 parent c49e718 commit 86f59de
Show file tree
Hide file tree
Showing 8 changed files with 420 additions and 99 deletions.
12 changes: 6 additions & 6 deletions flang/include/flang/Semantics/symbol.h
Expand Up @@ -155,6 +155,7 @@ class AssocEntityDetails : public EntityDetails {
MaybeExpr expr_;
std::optional<int> rank_;
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const AssocEntityDetails &);

// An entity known to be an object.
class ObjectEntityDetails : public EntityDetails {
Expand Down Expand Up @@ -432,6 +433,7 @@ class GenericDetails {
const SymbolVector &specificProcs() const { return specificProcs_; }
const std::vector<SourceName> &bindingNames() const { return bindingNames_; }
void AddSpecificProc(const Symbol &, SourceName bindingName);
const SymbolVector &uses() const { return uses_; }

// specific and derivedType indicate a specific procedure or derived type
// with the same name as this generic. Only one of them may be set.
Expand All @@ -441,6 +443,7 @@ class GenericDetails {
Symbol *derivedType() { return derivedType_; }
const Symbol *derivedType() const { return derivedType_; }
void set_derivedType(Symbol &derivedType);
void AddUse(const Symbol &);

// Copy in specificProcs, specific, and derivedType from another generic
void CopyFrom(const GenericDetails &);
Expand All @@ -450,22 +453,19 @@ class GenericDetails {
const Symbol *CheckSpecific() const;
Symbol *CheckSpecific();

const std::optional<UseDetails> &useDetails() const { return useDetails_; }
void set_useDetails(const UseDetails &details) { useDetails_ = details; }

private:
GenericKind kind_;
// all of the specific procedures for this generic
SymbolVector specificProcs_;
std::vector<SourceName> bindingNames_;
// Symbols used from other modules merged into this one
SymbolVector uses_;
// a specific procedure with the same name as this generic, if any
Symbol *specific_{nullptr};
// a derived type with the same name as this generic, if any
Symbol *derivedType_{nullptr};
// If two USEs of generics were merged to form this one, this is the
// UseDetails for one of them. Used for reporting USE errors.
std::optional<UseDetails> useDetails_;
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const GenericDetails &);

class UnknownDetails {};

Expand Down
55 changes: 40 additions & 15 deletions flang/lib/Semantics/check-declarations.cpp
Expand Up @@ -120,11 +120,12 @@ class DistinguishabilityHelper {
public:
DistinguishabilityHelper(SemanticsContext &context) : context_{context} {}
void Add(const Symbol &, GenericKind, const Symbol &, const Procedure &);
void Check();
void Check(const Scope &);

private:
void SayNotDistinguishable(
const SourceName &, GenericKind, const Symbol &, const Symbol &);
void SayNotDistinguishable(const Scope &, const SourceName &, GenericKind,
const Symbol &, const Symbol &);
void AttachDeclaration(parser::Message &, const Scope &, const Symbol &);

SemanticsContext &context_;
struct ProcedureInfo {
Expand Down Expand Up @@ -1020,7 +1021,7 @@ void CheckHelper::CheckSpecificsAreDistinguishable(
helper.Add(generic, kind, specific, *procedure);
}
}
helper.Check();
helper.Check(generic.owner());
}

static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) {
Expand Down Expand Up @@ -1637,7 +1638,7 @@ void CheckHelper::CheckGenericOps(const Scope &scope) {
}
}
}
helper.Check();
helper.Check(scope);
}

void SubprogramMatchHelper::Check(
Expand Down Expand Up @@ -1859,7 +1860,7 @@ void DistinguishabilityHelper::Add(const Symbol &generic, GenericKind kind,
}
}

void DistinguishabilityHelper::Check() {
void DistinguishabilityHelper::Check(const Scope &scope) {
for (const auto &[name, info] : nameToInfo_) {
auto count{info.size()};
for (std::size_t i1{0}; i1 < count - 1; ++i1) {
Expand All @@ -1870,15 +1871,17 @@ void DistinguishabilityHelper::Check() {
? evaluate::characteristics::Distinguishable
: evaluate::characteristics::DistinguishableOpOrAssign};
if (!distinguishable(proc1, proc2)) {
SayNotDistinguishable(name, kind1, symbol1, symbol2);
SayNotDistinguishable(
GetTopLevelUnitContaining(scope), name, kind1, symbol1, symbol2);
}
}
}
}
}

void DistinguishabilityHelper::SayNotDistinguishable(const SourceName &name,
GenericKind kind, const Symbol &proc1, const Symbol &proc2) {
void DistinguishabilityHelper::SayNotDistinguishable(const Scope &scope,
const SourceName &name, GenericKind kind, const Symbol &proc1,
const Symbol &proc2) {
std::string name1{proc1.name().ToString()};
std::string name2{proc2.name().ToString()};
if (kind.IsOperator() || kind.IsAssignment()) {
Expand All @@ -1890,12 +1893,34 @@ void DistinguishabilityHelper::SayNotDistinguishable(const SourceName &name,
name2 = proc2.owner().GetName()->ToString() + '%' + name2;
}
}
auto &msg{context_.Say(name,
"Generic '%s' may not have specific procedures '%s' and '%s'"
" as their interfaces are not distinguishable"_err_en_US,
MakeOpName(name), name1, name2)};
evaluate::AttachDeclaration(msg, proc1);
evaluate::AttachDeclaration(msg, proc2);
parser::Message *msg;
if (scope.sourceRange().Contains(name)) {
msg = &context_.Say(name,
"Generic '%s' may not have specific procedures '%s' and"
" '%s' as their interfaces are not distinguishable"_err_en_US,
MakeOpName(name), name1, name2);
} else {
msg = &context_.Say(*GetTopLevelUnitContaining(proc1).GetName(),
"USE-associated generic '%s' may not have specific procedures '%s' and"
" '%s' as their interfaces are not distinguishable"_err_en_US,
MakeOpName(name), name1, name2);
}
AttachDeclaration(*msg, scope, proc1);
AttachDeclaration(*msg, scope, proc2);
}

// `evaluate::AttachDeclaration` doesn't handle the generic case where `proc`
// comes from a different module but is not necessarily use-associated.
void DistinguishabilityHelper::AttachDeclaration(
parser::Message &msg, const Scope &scope, const Symbol &proc) {
const Scope &unit{GetTopLevelUnitContaining(proc)};
if (unit == scope) {
evaluate::AttachDeclaration(msg, proc);
} else {
msg.Attach(unit.GetName().value(),
"'%s' is USE-associated from module '%s'"_en_US, proc.name(),
unit.GetName().value());
}
}

void CheckDeclarations(SemanticsContext &context) {
Expand Down
28 changes: 19 additions & 9 deletions flang/lib/Semantics/mod-file.cpp
Expand Up @@ -43,7 +43,7 @@ struct ModHeader {
};

static std::optional<SourceName> GetSubmoduleParent(const parser::Program &);
static SymbolVector CollectSymbols(const Scope &);
static void CollectSymbols(const Scope &, SymbolVector &, SymbolVector &);
static void PutEntity(llvm::raw_ostream &, const Symbol &);
static void PutObjectEntity(llvm::raw_ostream &, const Symbol &);
static void PutProcEntity(llvm::raw_ostream &, const Symbol &);
Expand Down Expand Up @@ -178,12 +178,17 @@ std::string ModFileWriter::GetAsString(const Symbol &symbol) {

// Put out the visible symbols from scope.
bool ModFileWriter::PutSymbols(const Scope &scope) {
std::string buf;
llvm::raw_string_ostream typeBindings{
buf}; // stuff after CONTAINS in derived type
for (const Symbol &symbol : CollectSymbols(scope)) {
SymbolVector sorted;
SymbolVector uses;
CollectSymbols(scope, sorted, uses);
std::string buf; // stuff after CONTAINS in derived type
llvm::raw_string_ostream typeBindings{buf};
for (const Symbol &symbol : sorted) {
PutSymbol(typeBindings, symbol);
}
for (const Symbol &symbol : uses) {
PutUse(symbol);
}
if (auto str{typeBindings.str()}; !str.empty()) {
CHECK(scope.IsDerivedType());
decls_ << "contains\n" << str;
Expand Down Expand Up @@ -393,10 +398,13 @@ static llvm::raw_ostream &PutGenericName(
}

void ModFileWriter::PutGeneric(const Symbol &symbol) {
const auto &genericOwner{symbol.owner()};
auto &details{symbol.get<GenericDetails>()};
PutGenericName(decls_ << "interface ", symbol) << '\n';
for (const Symbol &specific : details.specificProcs()) {
decls_ << "procedure::" << specific.name() << '\n';
if (specific.owner() == genericOwner) {
decls_ << "procedure::" << specific.name() << '\n';
}
}
decls_ << "end interface\n";
if (symbol.attrs().test(Attr::PRIVATE)) {
Expand Down Expand Up @@ -431,8 +439,8 @@ void ModFileWriter::PutUseExtraAttr(

// Collect the symbols of this scope sorted by their original order, not name.
// Namelists are an exception: they are sorted after other symbols.
SymbolVector CollectSymbols(const Scope &scope) {
SymbolVector sorted;
void CollectSymbols(
const Scope &scope, SymbolVector &sorted, SymbolVector &uses) {
SymbolVector namelist;
std::size_t commonSize{scope.commonBlocks().size()};
auto symbols{scope.GetSymbols()};
Expand All @@ -444,14 +452,16 @@ SymbolVector CollectSymbols(const Scope &scope) {
} else {
sorted.push_back(symbol);
}
if (const auto *details{symbol->detailsIf<GenericDetails>()}) {
uses.insert(uses.end(), details->uses().begin(), details->uses().end());
}
}
}
sorted.insert(sorted.end(), namelist.begin(), namelist.end());
for (const auto &pair : scope.commonBlocks()) {
sorted.push_back(*pair.second);
}
std::sort(sorted.end() - commonSize, sorted.end());
return sorted;
}

void PutEntity(llvm::raw_ostream &os, const Symbol &symbol) {
Expand Down

0 comments on commit 86f59de

Please sign in to comment.