[flang] Restore ENUM_CLASS() to be compilation-time code

Rework some recent changes to the ENUM_CLASS() macro so that
all of the construction of enumerator-to-name string mapping
data structures is again performed at compilation time.

Differential Revision: https://reviews.llvm.org/D137859
This commit is contained in:
Peter Klausler
2022-11-10 17:29:29 -08:00
parent 64e4d03c68
commit bcba39a56f
16 changed files with 118 additions and 80 deletions

View File

@@ -0,0 +1,73 @@
//===-- include/flang/Common/enum-class.h -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// The macro
// ENUM_CLASS(className, enum1, enum2, ..., enumN)
// defines
// enum class className { enum1, enum2, ... , enumN };
// as well as the introspective utilities
// static constexpr std::size_t className_enumSize{N};
// static inline const std::string &EnumToString(className);
#ifndef FORTRAN_COMMON_ENUM_CLASS_H_
#define FORTRAN_COMMON_ENUM_CLASS_H_
#include <array>
#include <string>
namespace Fortran::common {
constexpr std::size_t CountEnumNames(const char *p) {
std::size_t n{0};
std::size_t any{0};
for (; *p; ++p) {
if (*p == ',') {
n += any;
any = 0;
} else if (*p != ' ') {
any = 1;
}
}
return n + any;
}
template <std::size_t ITEMS>
constexpr std::array<std::string_view, ITEMS> EnumNames(const char *p) {
std::array<std::string_view, ITEMS> result{""};
std::size_t at{0};
const char *start{nullptr};
for (; *p; ++p) {
if (*p == ',' || *p == ' ') {
if (start) {
result[at++] =
std::string_view{start, static_cast<std::size_t>(p - start)};
start = nullptr;
}
} else if (!start) {
start = p;
}
}
if (start) {
result[at] = std::string_view{start, static_cast<std::size_t>(p - start)};
}
return result;
}
#define ENUM_CLASS(NAME, ...) \
enum class NAME { __VA_ARGS__ }; \
[[maybe_unused]] static constexpr std::size_t NAME##_enumSize{ \
::Fortran::common::CountEnumNames(#__VA_ARGS__)}; \
[[maybe_unused]] static inline std::string_view EnumToString(NAME e) { \
static const constexpr char vaArgs[]{#__VA_ARGS__}; \
static const constexpr auto names{ \
::Fortran::common::EnumNames<NAME##_enumSize>(vaArgs)}; \
return names[static_cast<std::size_t>(e)]; \
}
} // namespace Fortran::common
#endif // FORTRAN_COMMON_ENUM_CLASS_H_

View File

@@ -207,7 +207,7 @@ public:
template <typename STREAM>
STREAM &Dump(
STREAM &o, const std::string &EnumToString(enumerationType)) const {
STREAM &o, std::string_view EnumToString(enumerationType)) const {
char sep{'{'};
IterateOverMembers([&](auto e) {
o << sep << EnumToString(e);

View File

@@ -23,6 +23,7 @@
#error g++ >= 7.2 is required
#endif
#include "enum-class.h"
#include "visit.h"
#include <array>
#include <functional>
@@ -125,32 +126,6 @@ template <typename A> struct ListItemCount {
const std::size_t value;
};
#define ENUM_CLASS(NAME, ...) \
enum class NAME { __VA_ARGS__ }; \
[[maybe_unused]] static constexpr std::size_t NAME##_enumSize{[] { \
enum { __VA_ARGS__ }; \
return Fortran::common::ListItemCount{__VA_ARGS__}.value; \
}()}; \
struct NAME##_struct { \
NAME##_struct(const NAME##_struct &) = delete; \
NAME##_struct &operator=(const NAME##_struct &) = delete; \
static NAME##_struct &instance() { \
static NAME##_struct s; \
return s; \
} \
std::array<std::string, NAME##_enumSize> _enumNames; \
\
private: \
NAME##_struct() { \
Fortran::common::BuildIndexToString( \
#__VA_ARGS__, _enumNames.data(), NAME##_enumSize); \
} \
~NAME##_struct() {} \
}; \
[[maybe_unused]] static inline const std::string &EnumToString(NAME e) { \
return NAME##_struct::instance()._enumNames[static_cast<int>(e)]; \
}
// Check that a pointer is non-null and dereference it
#define DEREF(p) Fortran::common::Deref(p, __FILE__, __LINE__)

View File

@@ -373,7 +373,7 @@ template <TypeCategory CATEGORY> struct SomeKind {
static constexpr TypeCategory category{CATEGORY};
constexpr bool operator==(const SomeKind &) const { return true; }
static std::string AsFortran() {
return "Some"s + common::EnumToString(category);
return "Some"s + std::string{common::EnumToString(category)};
}
};

View File

@@ -65,7 +65,7 @@ inline constexpr char ToLowerCaseLetter(char &&ch) {
return IsUpperCaseLetter(ch) ? ch - 'A' + 'a' : ch;
}
inline std::string ToLowerCaseLetters(const std::string &str) {
inline std::string ToLowerCaseLetters(std::string_view str) {
std::string lowered{str};
for (char &ch : lowered) {
ch = ToLowerCaseLetter(ch);
@@ -81,7 +81,7 @@ inline constexpr char ToUpperCaseLetter(char &&ch) {
return IsLowerCaseLetter(ch) ? ch - 'a' + 'A' : ch;
}
inline std::string ToUpperCaseLetters(const std::string &str) {
inline std::string ToUpperCaseLetters(std::string_view str) {
std::string raised{str};
for (char &ch : raised) {
ch = ToUpperCaseLetter(ch);

View File

@@ -37,7 +37,7 @@ public:
static constexpr const char *GetNodeName(const T &) { return N; }
#define NODE_ENUM(T, E) \
static std::string GetNodeName(const T::E &x) { \
return #E " = "s + T::EnumToString(x); \
return #E " = "s + std::string{T::EnumToString(x)}; \
}
#define NODE(T1, T2) NODE_NAME(T1::T2, #T2)
NODE_NAME(bool, "bool")

View File

@@ -96,9 +96,9 @@ constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) {
// The construction of a MessageFormattedText uses a MessageFixedText
// as a vsnprintf() formatting string that is applied to the
// following arguments. CharBlock and std::string argument
// values are also supported; they are automatically converted into
// char pointers that are suitable for '%s' formatting.
// following arguments. CharBlock, std::string, and std::string_view
// argument values are also supported; they are automatically converted
// into char pointers that are suitable for '%s' formatting.
class MessageFormattedText {
public:
template <typename... A>
@@ -128,10 +128,6 @@ private:
static_assert(!std::is_class_v<std::decay_t<A>>);
return x;
}
template <typename A> A Convert(A &x) {
static_assert(!std::is_class_v<std::decay_t<A>>);
return x;
}
template <typename A> common::IfNoLvalue<A, A> Convert(A &&x) {
static_assert(!std::is_class_v<std::decay_t<A>>);
return std::move(x);
@@ -139,8 +135,9 @@ private:
const char *Convert(const char *s) { return s; }
const char *Convert(char *s) { return s; }
const char *Convert(const std::string &);
const char *Convert(std::string &);
const char *Convert(std::string &&);
const char *Convert(const std::string_view &);
const char *Convert(std::string_view &&);
const char *Convert(CharBlock);
std::intmax_t Convert(std::int64_t x) { return x; }
std::uintmax_t Convert(std::uint64_t x) { return x; }

View File

@@ -10,7 +10,6 @@
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <regex>
namespace Fortran::common {
@@ -24,22 +23,4 @@ namespace Fortran::common {
std::abort();
}
// Converts the comma separated list of enumerators into tokens which are then
// stored into the provided array of strings. This is intended for use from the
// expansion of ENUM_CLASS.
void BuildIndexToString(
const char *commaSeparated, std::string enumNames[], int enumSize) {
std::string input(commaSeparated);
std::regex reg("\\s*,\\s*");
std::sregex_token_iterator iter(input.begin(), input.end(), reg, -1);
std::sregex_token_iterator end;
int index = 0;
while (iter != end) {
enumNames[index] = *iter;
iter++;
index++;
}
CHECK(index == enumSize);
}
} // namespace Fortran::common

View File

@@ -1000,7 +1000,7 @@ bool Procedure::IsCompatibleWith(const Procedure &actual, std::string *whyNot,
auto sep{": "s};
*whyNot = "incompatible procedure attributes";
differences.IterateOverMembers([&](Attr x) {
*whyNot += sep + EnumToString(x);
*whyNot += sep + std::string{EnumToString(x)};
sep = ", ";
});
}

View File

@@ -2091,10 +2091,10 @@ mlir::Value genInquireSpec<Fortran::parser::InquireSpec::CharVar>(
builder.createConvert(loc, specFuncTy.getInput(0), cookie),
builder.createIntegerConstant(
loc, specFuncTy.getInput(1),
Fortran::runtime::io::HashInquiryKeyword(
Fortran::runtime::io::HashInquiryKeyword(std::string{
Fortran::parser::InquireSpec::CharVar::EnumToString(
std::get<Fortran::parser::InquireSpec::CharVar::Kind>(var.t))
.c_str())),
std::get<Fortran::parser::InquireSpec::CharVar::Kind>(var.t))}
.c_str())),
builder.createConvert(loc, specFuncTy.getInput(2), fir::getBase(str)),
builder.createConvert(loc, specFuncTy.getInput(3), fir::getLen(str))};
return builder.create<fir::CallOp>(loc, specFunc, args).getResult(0);
@@ -2128,10 +2128,10 @@ mlir::Value genInquireSpec<Fortran::parser::InquireSpec::IntVar>(
builder.createConvert(loc, specFuncTy.getInput(0), cookie),
builder.createIntegerConstant(
loc, specFuncTy.getInput(1),
Fortran::runtime::io::HashInquiryKeyword(
Fortran::runtime::io::HashInquiryKeyword(std::string{
Fortran::parser::InquireSpec::IntVar::EnumToString(
std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t))
.c_str())),
std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t))}
.c_str())),
builder.createConvert(loc, specFuncTy.getInput(2), addr),
builder.createConvert(loc, specFuncTy.getInput(3), kind)};
return builder.create<fir::CallOp>(loc, specFunc, args).getResult(0);
@@ -2165,9 +2165,9 @@ mlir::Value genInquireSpec<Fortran::parser::InquireSpec::LogVar>(
else
args.push_back(builder.createIntegerConstant(
loc, specFuncTy.getInput(1),
Fortran::runtime::io::HashInquiryKeyword(
Fortran::parser::InquireSpec::LogVar::EnumToString(logVarKind)
.c_str())));
Fortran::runtime::io::HashInquiryKeyword(std::string{
Fortran::parser::InquireSpec::LogVar::EnumToString(logVarKind)}
.c_str())));
args.push_back(builder.createConvert(loc, specFuncTy.getInput(2), addr));
auto call = builder.create<fir::CallOp>(loc, specFunc, args);
boolRefToLogical(loc, builder, addr);

View File

@@ -70,13 +70,18 @@ const char *MessageFormattedText::Convert(const std::string &s) {
return conversions_.front().c_str();
}
const char *MessageFormattedText::Convert(std::string &s) {
const char *MessageFormattedText::Convert(std::string &&s) {
conversions_.emplace_front(std::move(s));
return conversions_.front().c_str();
}
const char *MessageFormattedText::Convert(const std::string_view &s) {
conversions_.emplace_front(s);
return conversions_.front().c_str();
}
const char *MessageFormattedText::Convert(std::string &&s) {
conversions_.emplace_front(std::move(s));
const char *MessageFormattedText::Convert(std::string_view &&s) {
conversions_.emplace_front(s);
return conversions_.front().c_str();
}

View File

@@ -2621,6 +2621,7 @@ private:
void PutKeywordLetter(char);
void Word(const char *);
void Word(const std::string &);
void Word(const std::string_view &);
void Indent() { indent_ += indentationAmount_; }
void Outdent() {
CHECK(indent_ >= indentationAmount_);
@@ -2777,6 +2778,12 @@ void UnparseVisitor::Word(const char *str) {
void UnparseVisitor::Word(const std::string &str) { Word(str.c_str()); }
void UnparseVisitor::Word(const std::string_view &str) {
for (std::size_t j{0}; j < str.length(); ++j) {
PutKeywordLetter(str[j]);
}
}
template <typename A>
void Unparse(llvm::raw_ostream &out, const A &root, Encoding encoding,
bool capitalizeKeywords, bool backslashEscapes,

View File

@@ -30,7 +30,7 @@ std::string AttrToString(Attr attr) {
case Attr::INTENT_OUT:
return "INTENT(OUT)";
default:
return EnumToString(attr);
return std::string{EnumToString(attr)};
}
}

View File

@@ -43,7 +43,7 @@ using common::LanguageFeature;
using common::NumericOperator;
using common::TypeCategory;
static inline std::string ToUpperCase(const std::string &str) {
static inline std::string ToUpperCase(std::string_view str) {
return parser::ToUpperCaseLetters(str);
}

View File

@@ -59,7 +59,7 @@ llvm::raw_ostream &PutAttrs(llvm::raw_ostream &, Attrs,
static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr);
static llvm::raw_ostream &PutType(llvm::raw_ostream &, const DeclTypeSpec &);
static llvm::raw_ostream &PutLower(llvm::raw_ostream &, const std::string &);
static llvm::raw_ostream &PutLower(llvm::raw_ostream &, std::string_view);
static std::error_code WriteFile(
const std::string &, const std::string &, bool = true);
static bool FileContentsMatch(
@@ -797,7 +797,7 @@ llvm::raw_ostream &PutType(llvm::raw_ostream &os, const DeclTypeSpec &type) {
return PutLower(os, type.AsFortran());
}
llvm::raw_ostream &PutLower(llvm::raw_ostream &os, const std::string &str) {
llvm::raw_ostream &PutLower(llvm::raw_ostream &os, std::string_view str) {
for (char c : str) {
os << parser::ToLowerCaseLetter(c);
}

View File

@@ -671,20 +671,20 @@ bool GenericKind::IsOperator() const {
std::string GenericKind::ToString() const {
return common::visit(
common::visitors {
[](const OtherKind &x) { return EnumToString(x); },
[](const OtherKind &x) { return std::string{EnumToString(x)}; },
[](const DefinedIo &x) { return AsFortran(x).ToString(); },
#if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
[](const common::NumericOperator &x) {
return common::EnumToString(x);
return std::string{common::EnumToString(x)};
},
[](const common::LogicalOperator &x) {
return common::EnumToString(x);
return std::string{common::EnumToString(x)};
},
[](const common::RelationalOperator &x) {
return common::EnumToString(x);
return std::string{common::EnumToString(x)};
},
#else
[](const auto &x) { return common::EnumToString(x); },
[](const auto &x) { return std::string{common::EnumToString(x)}; },
#endif
},
u);