[flang] Handle multiple names for same operator

Some operators have more than one name, e.g. operator(==), operator(.eq).
That was working correctly in generic definitions but they can also
appear in other contexts: USE statements and access statements, for
example.

This changes FindInScope to always look for each of the names for
a symbol. So an operator may be use-associated under one name but
declared private under another name and it will be the same symbol.
This replaces GenericSpecInfo::FindInScope which was only usable in
some cases.

Add a version of FindInScope() that looks in the current scope to
simplify many of the calls.

Differential Revision: https://reviews.llvm.org/D93344
This commit is contained in:
Tim Keith 2020-12-16 07:06:53 -08:00
parent 92d6e8001e
commit 7082de56b7
4 changed files with 126 additions and 57 deletions

View File

@ -29,6 +29,8 @@ using common::NumericOperator;
using common::RelationalOperator;
using IntrinsicOperator = parser::DefinedOperator::IntrinsicOperator;
static constexpr const char *operatorPrefix{"operator("};
static GenericKind MapIntrinsicOperator(IntrinsicOperator);
Symbol *Resolve(const parser::Name &name, Symbol *symbol) {
@ -65,6 +67,37 @@ bool IsIntrinsicOperator(
return false;
}
template <typename E>
std::forward_list<std::string> GetOperatorNames(
const SemanticsContext &context, E opr) {
std::forward_list<std::string> result;
for (const char *name : context.languageFeatures().GetNames(opr)) {
result.emplace_front(std::string{operatorPrefix} + name + ')');
}
return result;
}
std::forward_list<std::string> GetAllNames(
const SemanticsContext &context, const SourceName &name) {
std::string str{name.ToString()};
if (!name.empty() && name.end()[-1] == ')' &&
name.ToString().rfind(std::string{operatorPrefix}, 0) == 0) {
for (int i{0}; i != common::LogicalOperator_enumSize; ++i) {
auto names{GetOperatorNames(context, LogicalOperator{i})};
if (std::find(names.begin(), names.end(), str) != names.end()) {
return names;
}
}
for (int i{0}; i != common::RelationalOperator_enumSize; ++i) {
auto names{GetOperatorNames(context, RelationalOperator{i})};
if (std::find(names.begin(), names.end(), str) != names.end()) {
return names;
}
}
}
return {str};
}
bool IsLogicalConstant(
const SemanticsContext &context, const SourceName &name) {
std::string str{name.ToString()};
@ -73,37 +106,6 @@ bool IsLogicalConstant(
(str == ".t" || str == ".f."));
}
// The operators <, <=, >, >=, ==, and /= always have the same interpretations
// as the operators .LT., .LE., .GT., .GE., .EQ., and .NE., respectively.
std::forward_list<std::string> GenericSpecInfo::GetAllNames(
SemanticsContext &context) const {
auto getNames{[&](auto opr) {
std::forward_list<std::string> result;
for (const char *name : context.languageFeatures().GetNames(opr)) {
result.emplace_front("operator("s + name + ')');
}
return result;
}};
return std::visit(
common::visitors{[&](const LogicalOperator &x) { return getNames(x); },
[&](const RelationalOperator &x) { return getNames(x); },
[&](const auto &) -> std::forward_list<std::string> {
return {symbolName_.value().ToString()};
}},
kind_.u);
}
Symbol *GenericSpecInfo::FindInScope(
SemanticsContext &context, const Scope &scope) const {
for (const auto &name : GetAllNames(context)) {
auto iter{scope.find(SourceName{name})};
if (iter != scope.end()) {
return &*iter->second;
}
}
return nullptr;
}
void GenericSpecInfo::Resolve(Symbol *symbol) const {
if (symbol) {
if (auto *details{symbol->detailsIf<GenericDetails>()}) {
@ -162,6 +164,16 @@ void GenericSpecInfo::Analyze(const parser::GenericSpec &x) {
x.u);
}
llvm::raw_ostream &operator<<(
llvm::raw_ostream &os, const GenericSpecInfo &info) {
os << "GenericSpecInfo: kind=" << info.kind_.ToString();
os << " parseName="
<< (info.parseName_ ? info.parseName_->ToString() : "null");
os << " symbolName="
<< (info.symbolName_ ? info.symbolName_->ToString() : "null");
return os;
}
// parser::DefinedOperator::IntrinsicOperator -> GenericKind
static GenericKind MapIntrinsicOperator(IntrinsicOperator op) {
switch (op) {

View File

@ -19,6 +19,7 @@
#include "flang/Semantics/semantics.h"
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/type.h"
#include "llvm/Support/raw_ostream.h"
#include <forward_list>
namespace Fortran::parser {
@ -50,6 +51,11 @@ parser::MessageFixedText WithIsFatal(
bool IsIntrinsicOperator(const SemanticsContext &, const SourceName &);
bool IsLogicalConstant(const SemanticsContext &, const SourceName &);
// Some intrinsic operators have more than one name (e.g. `operator(.eq.)` and
// `operator(==)`). GetAllNames() returns them all, including symbolName.
std::forward_list<std::string> GetAllNames(
const SemanticsContext &, const SourceName &);
template <typename T>
MaybeIntExpr EvaluateIntExpr(SemanticsContext &context, const T &expr) {
if (MaybeExpr maybeExpr{
@ -75,13 +81,11 @@ public:
GenericKind kind() const { return kind_; }
const SourceName &symbolName() const { return symbolName_.value(); }
// Some intrinsic operators have more than one name (e.g. `operator(.eq.)` and
// `operator(==)`). GetAllNames() returns them all, including symbolName.
std::forward_list<std::string> GetAllNames(SemanticsContext &) const;
// Set the GenericKind in this symbol and resolve the corresponding
// name if there is one
void Resolve(Symbol *) const;
Symbol *FindInScope(SemanticsContext &, const Scope &) const;
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const GenericSpecInfo &);
private:
GenericKind kind_;

View File

@ -502,6 +502,9 @@ public:
// Search for name only in scope, not in enclosing scopes.
Symbol *FindInScope(const Scope &, const parser::Name &);
Symbol *FindInScope(const Scope &, const SourceName &);
template <typename T> Symbol *FindInScope(const T &name) {
return FindInScope(currScope(), name);
}
// Search for name in a derived type scope and its parents.
Symbol *FindInTypeOrParents(const Scope &, const parser::Name &);
Symbol *FindInTypeOrParents(const parser::Name &);
@ -533,7 +536,7 @@ public:
const SourceName &name, const Attrs &attrs, D &&details) {
// Note: don't use FindSymbol here. If this is a derived type scope,
// we want to detect whether the name is already declared as a component.
auto *symbol{FindInScope(currScope(), name)};
auto *symbol{FindInScope(name)};
if (!symbol) {
symbol = &MakeSymbol(name, attrs);
symbol->set_details(std::move(details));
@ -2048,7 +2051,7 @@ Symbol &ScopeHandler::MakeHostAssocSymbol(
return symbol;
}
Symbol &ScopeHandler::CopySymbol(const SourceName &name, const Symbol &symbol) {
CHECK(!FindInScope(currScope(), name));
CHECK(!FindInScope(name));
return MakeSymbol(currScope(), name, symbol.attrs());
}
@ -2058,11 +2061,14 @@ Symbol *ScopeHandler::FindInScope(
return Resolve(name, FindInScope(scope, name.source));
}
Symbol *ScopeHandler::FindInScope(const Scope &scope, const SourceName &name) {
if (auto it{scope.find(name)}; it != scope.end()) {
return &*it->second;
} else {
return nullptr;
// all variants of names, e.g. "operator(.ne.)" for "operator(/=)"
for (const std::string &n : GetAllNames(context(), name)) {
auto it{scope.find(SourceName{n})};
if (it != scope.end()) {
return &*it->second;
}
}
return nullptr;
}
// Find a component or type parameter by name in a derived type or its parents.
@ -2318,7 +2324,7 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
!symbol->attrs().test(Attr::INTRINSIC) &&
!symbol->has<MiscDetails>() && useNames.count(name) == 0) {
SourceName location{x.moduleName.source};
if (auto *localSymbol{FindInScope(currScope(), name)}) {
if (auto *localSymbol{FindInScope(name)}) {
DoAddUse(location, localSymbol->name(), *localSymbol, *symbol);
} else {
DoAddUse(location, location, CopySymbol(name, *symbol), *symbol);
@ -2397,8 +2403,7 @@ void ModuleVisitor::DoAddUse(const SourceName &location,
generic1.CopyFrom(generic2);
}
EraseSymbol(localSymbol);
MakeSymbol(
localSymbol.name(), localUltimate.attrs(), std::move(generic1));
MakeSymbol(localSymbol.name(), localSymbol.attrs(), std::move(generic1));
} else {
ConvertToUseError(localSymbol, location, *useModuleScope_);
}
@ -2435,8 +2440,7 @@ void ModuleVisitor::DoAddUse(const SourceName &location,
void ModuleVisitor::AddUse(const GenericSpecInfo &info) {
if (useModuleScope_) {
const auto &name{info.symbolName()};
auto rename{
AddUse(name, name, info.FindInScope(context(), *useModuleScope_))};
auto rename{AddUse(name, name, FindInScope(*useModuleScope_, name))};
info.Resolve(rename.use);
}
}
@ -2523,7 +2527,7 @@ void InterfaceVisitor::Post(const parser::EndInterfaceStmt &) {
// Create a symbol in genericSymbol_ for this GenericSpec.
bool InterfaceVisitor::Pre(const parser::GenericSpec &x) {
if (auto *symbol{GenericSpecInfo{x}.FindInScope(context(), currScope())}) {
if (auto *symbol{FindInScope(GenericSpecInfo{x}.symbolName())}) {
SetGenericSymbol(*symbol);
}
return false;
@ -3402,7 +3406,7 @@ Symbol &DeclarationVisitor::HandleAttributeStmt(
if (attr == Attr::INTRINSIC && !IsIntrinsic(name.source, std::nullopt)) {
Say(name.source, "'%s' is not a known intrinsic procedure"_err_en_US);
}
auto *symbol{FindInScope(currScope(), name)};
auto *symbol{FindInScope(name)};
if (attr == Attr::ASYNCHRONOUS || attr == Attr::VOLATILE) {
// these can be set on a symbol that is host-assoc or use-assoc
if (!symbol &&
@ -4065,7 +4069,7 @@ void DeclarationVisitor::CheckBindings(
CHECK(currScope().IsDerivedType());
for (auto &declaration : tbps.declarations) {
auto &bindingName{std::get<parser::Name>(declaration.t)};
if (Symbol * binding{FindInScope(currScope(), bindingName)}) {
if (Symbol * binding{FindInScope(bindingName)}) {
if (auto *details{binding->detailsIf<ProcBindingDetails>()}) {
const Symbol *procedure{FindSubprogram(details->symbol())};
if (!CanBeTypeBoundProc(procedure)) {
@ -4134,7 +4138,7 @@ bool DeclarationVisitor::Pre(const parser::TypeBoundGenericStmt &x) {
SourceName symbolName{info.symbolName()};
bool isPrivate{accessSpec ? accessSpec->v == parser::AccessSpec::Kind::Private
: derivedTypeInfo_.privateBindings};
auto *genericSymbol{info.FindInScope(context(), currScope())};
auto *genericSymbol{FindInScope(symbolName)};
if (genericSymbol) {
if (!genericSymbol->has<GenericDetails>()) {
genericSymbol = nullptr; // MakeTypeSymbol will report the error below
@ -4142,7 +4146,7 @@ bool DeclarationVisitor::Pre(const parser::TypeBoundGenericStmt &x) {
} else {
// look in parent types:
Symbol *inheritedSymbol{nullptr};
for (const auto &name : info.GetAllNames(context())) {
for (const auto &name : GetAllNames(context(), symbolName)) {
inheritedSymbol = currScope().FindComponent(SourceName{name});
if (inheritedSymbol) {
break;
@ -4298,7 +4302,7 @@ bool DeclarationVisitor::Pre(const parser::NamelistStmt::Group &x) {
}
const auto &groupName{std::get<parser::Name>(x.t)};
auto *groupSymbol{FindInScope(currScope(), groupName)};
auto *groupSymbol{FindInScope(groupName)};
if (!groupSymbol || !groupSymbol->has<NamelistDetails>()) {
groupSymbol = &MakeSymbol(groupName, std::move(details));
groupSymbol->ReplaceName(groupName.source);
@ -4397,7 +4401,7 @@ bool DeclarationVisitor::Pre(const parser::SaveStmt &x) {
void DeclarationVisitor::CheckSaveStmts() {
for (const SourceName &name : saveInfo_.entities) {
auto *symbol{FindInScope(currScope(), name)};
auto *symbol{FindInScope(name)};
if (!symbol) {
// error was reported
} else if (saveInfo_.saveAll) {
@ -5159,7 +5163,7 @@ bool ConstructVisitor::Pre(const parser::ChangeTeamStmt &x) {
void ConstructVisitor::Post(const parser::CoarrayAssociation &x) {
const auto &decl{std::get<parser::CodimensionDecl>(x.t)};
const auto &name{std::get<parser::Name>(decl.t)};
if (auto *symbol{FindInScope(currScope(), name)}) {
if (auto *symbol{FindInScope(name)}) {
const auto &selector{std::get<parser::Selector>(x.t)};
if (auto sel{ResolveSelector(selector)}) {
const Symbol *whole{UnwrapWholeSymbolDataRef(sel.expr)};
@ -5962,7 +5966,7 @@ bool ModuleVisitor::Pre(const parser::AccessStmt &x) {
[=](const Indirection<parser::GenericSpec> &y) {
auto info{GenericSpecInfo{y.value()}};
const auto &symbolName{info.symbolName()};
if (auto *symbol{info.FindInScope(context(), currScope())}) {
if (auto *symbol{FindInScope(symbolName)}) {
info.Resolve(&SetAccess(symbolName, accessAttr, symbol));
} else if (info.kind().IsName()) {
info.Resolve(&SetAccess(symbolName, accessAttr));
@ -6084,7 +6088,7 @@ void ResolveNamesVisitor::CreateGeneric(const parser::GenericSpec &x) {
return;
}
GenericDetails genericDetails;
if (Symbol * existing{info.FindInScope(context(), currScope())}) {
if (Symbol * existing{FindInScope(symbolName)}) {
if (existing->has<GenericDetails>()) {
info.Resolve(existing);
return; // already have generic, add to it
@ -6204,7 +6208,7 @@ void ResolveNamesVisitor::CheckImports() {
void ResolveNamesVisitor::CheckImport(
const SourceName &location, const SourceName &name) {
if (auto *symbol{FindInScope(currScope(), name)}) {
if (auto *symbol{FindInScope(name)}) {
Say(location, "'%s' from host is not accessible"_err_en_US, name)
.Attach(symbol->name(), "'%s' is hidden by this entity"_en_US,
symbol->name());

View File

@ -549,3 +549,52 @@ end
! end
!end
! Verify that equivalent names are used when generic operators are merged
module m10a
interface operator(.ne.)
end interface
end
!Expect: m10a.mod
!module m10a
! interface operator(.ne.)
! end interface
!end
module m10b
interface operator(<>)
end interface
end
!Expect: m10b.mod
!module m10b
! interface operator(<>)
! end interface
!end
module m10c
use m10a
use m10b
interface operator(/=)
end interface
end
!Expect: m10c.mod
!module m10c
! use m10b,only:operator(.ne.)
! use m10a,only:operator(.ne.)
! interface operator(.ne.)
! end interface
!end
module m10d
use m10a
use m10c
private :: operator(<>)
end
!Expect: m10d.mod
!module m10d
! use m10c,only:operator(.ne.)
! use m10a,only:operator(.ne.)
! interface operator(.ne.)
! end interface
! private::operator(.ne.)
!end