[flang] Refactor GenericKind

Change GenericKind from an enum class to a variant that includes the
`NumericOperator`, `LogicalOperator`, and `RelationalOperator` from `common`.
This allows for better tests like `IsIntrinsicOperator` (which used to
check for being in a range of the `GenericKind` enumeration) and
simplifies mapping the kind to a string representation.

Original-commit: flang-compiler/f18@c74327c393
Reviewed-on: https://github.com/flang-compiler/f18/pull/841
Tree-same-pre-rewrite: false
This commit is contained in:
Tim Keith 2019-11-22 12:40:37 -08:00
parent 4d7b6cf3c1
commit 701a9bd0e5
6 changed files with 95 additions and 67 deletions

View File

@ -358,8 +358,7 @@ void ModFileWriter::PutSubprogram(const Symbol &symbol) {
static bool IsIntrinsicOp(const Symbol &symbol) {
if (const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()}) {
GenericKind kind{details->kind()};
return kind >= GenericKind::OpPower && kind <= GenericKind::OpNEQV;
return details->kind().IsIntrinsicOperator();
} else {
return false;
}

View File

@ -32,6 +32,7 @@ namespace Fortran::semantics {
using common::LanguageFeature;
using common::LogicalOperator;
using common::NumericOperator;
using common::RelationalOperator;
using IntrinsicOperator = parser::DefinedOperator::IntrinsicOperator;
@ -96,20 +97,13 @@ std::forward_list<std::string> GenericSpecInfo::GetAllNames(
}
return result;
}};
switch (kind_) {
case GenericKind::OpGE: return getNames(RelationalOperator::GE);
case GenericKind::OpGT: return getNames(RelationalOperator::GT);
case GenericKind::OpLE: return getNames(RelationalOperator::LE);
case GenericKind::OpLT: return getNames(RelationalOperator::LT);
case GenericKind::OpEQ: return getNames(RelationalOperator::EQ);
case GenericKind::OpNE: return getNames(RelationalOperator::NE);
case GenericKind::OpAND: return getNames(LogicalOperator::And);
case GenericKind::OpOR: return getNames(LogicalOperator::Or);
case GenericKind::OpEQV: return getNames(LogicalOperator::Eqv);
case GenericKind::OpNEQV: return getNames(LogicalOperator::Neqv);
case GenericKind::OpNOT: return getNames(LogicalOperator::Not);
default: return {symbolName_.value().ToString()};
}
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(
@ -136,7 +130,7 @@ void GenericSpecInfo::Resolve(Symbol *symbol) const {
}
void GenericSpecInfo::Analyze(const parser::DefinedOpName &name) {
kind_ = GenericKind::DefinedOp;
kind_ = GenericKind::OtherKind::DefinedOp;
parseName_ = &name.v;
symbolName_ = name.v.source;
}
@ -145,17 +139,17 @@ void GenericSpecInfo::Analyze(const parser::GenericSpec &x) {
symbolName_ = x.source;
kind_ = std::visit(
common::visitors{
[&](const parser::Name &y) {
[&](const parser::Name &y) -> GenericKind {
parseName_ = &y;
symbolName_ = y.source;
return GenericKind::Name;
return GenericKind::OtherKind::Name;
},
[&](const parser::DefinedOperator &y) {
return std::visit(
common::visitors{
[&](const parser::DefinedOpName &z) {
[&](const parser::DefinedOpName &z) -> GenericKind {
Analyze(z);
return GenericKind::DefinedOp;
return GenericKind::OtherKind::DefinedOp;
},
[&](const IntrinsicOperator &z) {
return MapIntrinsicOperator(z);
@ -163,20 +157,20 @@ void GenericSpecInfo::Analyze(const parser::GenericSpec &x) {
},
y.u);
},
[&](const parser::GenericSpec::Assignment &) {
return GenericKind::Assignment;
[&](const parser::GenericSpec::Assignment &) -> GenericKind {
return GenericKind::OtherKind::Assignment;
},
[&](const parser::GenericSpec::ReadFormatted &) {
return GenericKind::ReadFormatted;
[&](const parser::GenericSpec::ReadFormatted &) -> GenericKind {
return GenericKind::DefinedIo::ReadFormatted;
},
[&](const parser::GenericSpec::ReadUnformatted &) {
return GenericKind::ReadUnformatted;
[&](const parser::GenericSpec::ReadUnformatted &) -> GenericKind {
return GenericKind::DefinedIo::ReadUnformatted;
},
[&](const parser::GenericSpec::WriteFormatted &) {
return GenericKind::WriteFormatted;
[&](const parser::GenericSpec::WriteFormatted &) -> GenericKind {
return GenericKind::DefinedIo::WriteFormatted;
},
[&](const parser::GenericSpec::WriteUnformatted &) {
return GenericKind::WriteUnformatted;
[&](const parser::GenericSpec::WriteUnformatted &) -> GenericKind {
return GenericKind::DefinedIo::WriteUnformatted;
},
},
x.u);
@ -186,23 +180,23 @@ void GenericSpecInfo::Analyze(const parser::GenericSpec &x) {
static GenericKind MapIntrinsicOperator(IntrinsicOperator op) {
switch (op) {
SWITCH_COVERS_ALL_CASES
case IntrinsicOperator::Power: return GenericKind::OpPower;
case IntrinsicOperator::Multiply: return GenericKind::OpMultiply;
case IntrinsicOperator::Divide: return GenericKind::OpDivide;
case IntrinsicOperator::Add: return GenericKind::OpAdd;
case IntrinsicOperator::Subtract: return GenericKind::OpSubtract;
case IntrinsicOperator::Concat: return GenericKind::OpConcat;
case IntrinsicOperator::LT: return GenericKind::OpLT;
case IntrinsicOperator::LE: return GenericKind::OpLE;
case IntrinsicOperator::EQ: return GenericKind::OpEQ;
case IntrinsicOperator::NE: return GenericKind::OpNE;
case IntrinsicOperator::GE: return GenericKind::OpGE;
case IntrinsicOperator::GT: return GenericKind::OpGT;
case IntrinsicOperator::NOT: return GenericKind::OpNOT;
case IntrinsicOperator::AND: return GenericKind::OpAND;
case IntrinsicOperator::OR: return GenericKind::OpOR;
case IntrinsicOperator::EQV: return GenericKind::OpEQV;
case IntrinsicOperator::NEQV: return GenericKind::OpNEQV;
case IntrinsicOperator::Concat: return GenericKind::OtherKind::Concat;
case IntrinsicOperator::Power: return NumericOperator::Power;
case IntrinsicOperator::Multiply: return NumericOperator::Multiply;
case IntrinsicOperator::Divide: return NumericOperator::Divide;
case IntrinsicOperator::Add: return NumericOperator::Add;
case IntrinsicOperator::Subtract: return NumericOperator::Subtract;
case IntrinsicOperator::AND: return LogicalOperator::And;
case IntrinsicOperator::OR: return LogicalOperator::Or;
case IntrinsicOperator::EQV: return LogicalOperator::Eqv;
case IntrinsicOperator::NEQV: return LogicalOperator::Neqv;
case IntrinsicOperator::NOT: return LogicalOperator::Not;
case IntrinsicOperator::LT: return RelationalOperator::LT;
case IntrinsicOperator::LE: return RelationalOperator::LE;
case IntrinsicOperator::EQ: return RelationalOperator::EQ;
case IntrinsicOperator::NE: return RelationalOperator::NE;
case IntrinsicOperator::GE: return RelationalOperator::GE;
case IntrinsicOperator::GT: return RelationalOperator::GT;
}
}

View File

@ -2482,7 +2482,7 @@ void InterfaceVisitor::ResolveSpecificsInGeneric(Symbol &generic) {
}
if (!namesSeen.insert(name->source).second) {
Say(*name,
IsDefinedOperator(generic.name())
details.kind().IsDefinedOperator()
? "Procedure '%s' is already specified in generic operator '%s'"_err_en_US
: "Procedure '%s' is already specified in generic '%s'"_err_en_US,
name->source, generic.name());
@ -2567,12 +2567,6 @@ static GenericKind GetGenericKind(const Symbol &generic) {
generic.details());
}
static bool IsOperatorOrAssignment(const Symbol &generic) {
auto kind{GetGenericKind(generic)};
return kind == GenericKind::DefinedOp || kind == GenericKind::Assignment ||
(kind >= GenericKind::OpPower && kind <= GenericKind::OpNEQV);
}
// Check that the specifics of this generic are distinguishable from each other
void InterfaceVisitor::CheckSpecificsAreDistinguishable(
Symbol &generic, const SymbolVector &specifics) {
@ -2580,7 +2574,8 @@ void InterfaceVisitor::CheckSpecificsAreDistinguishable(
if (specifics.size() < 2) {
return;
}
auto distinguishable{IsOperatorOrAssignment(generic)
auto kind{GetGenericKind(generic)};
auto distinguishable{kind.IsAssignment() || kind.IsOperator()
? evaluate::characteristics::DistinguishableOpOrAssign
: evaluate::characteristics::Distinguishable};
using evaluate::characteristics::Procedure;
@ -5621,7 +5616,7 @@ bool ModuleVisitor::Pre(const parser::AccessStmt &x) {
const auto &symbolName{info.symbolName()};
if (auto *symbol{info.FindInScope(context(), currScope())}) {
info.Resolve(&SetAccess(symbolName, accessAttr, symbol));
} else if (info.kind() == GenericKind::Name) {
} else if (info.kind().IsName()) {
info.Resolve(&SetAccess(symbolName, accessAttr));
} else {
Say(symbolName, "Generic spec '%s' not found"_err_en_US);

View File

@ -427,7 +427,7 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
},
[](const HostAssocDetails &) {},
[&](const GenericDetails &x) {
os << ' ' << EnumToString(x.kind());
os << ' ' << x.kind().ToString();
DumpBool(os, "(specific)", x.specific() != nullptr);
DumpBool(os, "(derivedType)", x.derivedType() != nullptr);
os << " procs:";
@ -586,4 +586,28 @@ void TypeParamDetails::set_type(const DeclTypeSpec &type) {
type_ = &type;
}
bool GenericKind::IsIntrinsicOperator() const {
return Is(OtherKind::Concat) || Has<common::LogicalOperator>() ||
Has<common::NumericOperator>() || Has<common::RelationalOperator>();
}
bool GenericKind::IsOperator() const {
return IsDefinedOperator() || IsIntrinsicOperator();
}
std::string GenericKind::ToString() const {
return std::visit(
common::visitors{
[](const OtherKind &x) { return EnumToString(x); },
[](const DefinedIo &x) { return EnumToString(x); },
[](const auto &x) { return common::EnumToString(x); },
},
u);
}
bool GenericKind::Is(GenericKind::OtherKind x) const {
const OtherKind *y{std::get_if<OtherKind>(&u)};
return y && *y == x;
}
}

View File

@ -270,12 +270,28 @@ private:
SymbolRef symbol_; // procedure bound to; may be forward
};
ENUM_CLASS(GenericKind, // Kinds of generic-spec
Name, DefinedOp, // these have a Name associated with them
Assignment, // user-defined assignment
OpPower, OpMultiply, OpDivide, OpAdd, OpSubtract, OpConcat, OpLT, OpLE,
OpEQ, OpNE, OpGE, OpGT, OpNOT, OpAND, OpOR, OpEQV, OpNEQV, //
ReadFormatted, ReadUnformatted, WriteFormatted, WriteUnformatted)
// A GenericKind is one of: generic name, defined operator,
// defined assignment, intrinsic operator, or defined I/O.
struct GenericKind {
ENUM_CLASS(OtherKind, Name, DefinedOp, Assignment, Concat)
ENUM_CLASS(DefinedIo, // defined io
ReadFormatted, ReadUnformatted, WriteFormatted, WriteUnformatted)
GenericKind() : u{OtherKind::Name} {}
template<typename T> GenericKind(const T &x) { u = x; }
bool IsName() const { return Is(OtherKind::Name); }
bool IsAssignment() const { return Is(OtherKind::Assignment); }
bool IsDefinedOperator() const { return Is(OtherKind::DefinedOp); }
bool IsIntrinsicOperator() const;
bool IsOperator() const;
std::string ToString() const;
std::variant<OtherKind, common::NumericOperator, common::LogicalOperator,
common::RelationalOperator, DefinedIo>
u;
private:
template<typename T> bool Has() const { return std::holds_alternative<T>(u); }
bool Is(OtherKind) const;
};
class GenericBindingDetails {
public:
@ -286,7 +302,7 @@ public:
void add_specificProc(const Symbol &proc) { specificProcs_.push_back(proc); }
private:
GenericKind kind_{GenericKind::Name};
GenericKind kind_;
SymbolVector specificProcs_;
};
@ -416,7 +432,7 @@ public:
void set_useDetails(const UseDetails &details) { useDetails_ = details; }
private:
GenericKind kind_{GenericKind::Name};
GenericKind kind_;
// all of the specific procedures for this generic
SymbolVector specificProcs_;
// a specific procedure with the same name as this generic, if any

View File

@ -84,7 +84,7 @@ const Scope *FindPureProcedureContaining(const Scope &start) {
bool IsGenericDefinedOp(const Symbol &symbol) {
const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()};
return details && details->kind() == GenericKind::DefinedOp;
return details && details->kind().IsDefinedOperator();
}
bool IsCommonBlockContaining(const Symbol &block, const Symbol &object) {