forked from OSchip/llvm-project
[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:
parent
92d6e8001e
commit
7082de56b7
|
@ -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) {
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue