diff --git a/flang/include/flang/Common/enum-class.h b/flang/include/flang/Common/enum-class.h new file mode 100644 --- /dev/null +++ b/flang/include/flang/Common/enum-class.h @@ -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 +#include + +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 +constexpr std::array EnumNames(const char *p) { + std::array 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(p - start)}; + start = nullptr; + } + } else if (!start) { + start = p; + } + } + if (start) { + result[at] = std::string_view{start, static_cast(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(vaArgs)}; \ + return names[static_cast(e)]; \ + } + +} // namespace Fortran::common +#endif // FORTRAN_COMMON_ENUM_CLASS_H_ diff --git a/flang/include/flang/Common/enum-set.h b/flang/include/flang/Common/enum-set.h --- a/flang/include/flang/Common/enum-set.h +++ b/flang/include/flang/Common/enum-set.h @@ -207,7 +207,7 @@ template 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); diff --git a/flang/include/flang/Common/idioms.h b/flang/include/flang/Common/idioms.h --- a/flang/include/flang/Common/idioms.h +++ b/flang/include/flang/Common/idioms.h @@ -23,6 +23,7 @@ #error g++ >= 7.2 is required #endif +#include "enum-class.h" #include "visit.h" #include #include @@ -125,32 +126,6 @@ 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 _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(e)]; \ - } - // Check that a pointer is non-null and dereference it #define DEREF(p) Fortran::common::Deref(p, __FILE__, __LINE__) diff --git a/flang/include/flang/Evaluate/type.h b/flang/include/flang/Evaluate/type.h --- a/flang/include/flang/Evaluate/type.h +++ b/flang/include/flang/Evaluate/type.h @@ -373,7 +373,7 @@ 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)}; } }; diff --git a/flang/include/flang/Parser/characters.h b/flang/include/flang/Parser/characters.h --- a/flang/include/flang/Parser/characters.h +++ b/flang/include/flang/Parser/characters.h @@ -65,7 +65,7 @@ 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 @@ 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); diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -37,7 +37,7 @@ 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") diff --git a/flang/include/flang/Parser/message.h b/flang/include/flang/Parser/message.h --- a/flang/include/flang/Parser/message.h +++ b/flang/include/flang/Parser/message.h @@ -96,9 +96,9 @@ // 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 @@ -128,10 +128,6 @@ static_assert(!std::is_class_v>); return x; } - template A Convert(A &x) { - static_assert(!std::is_class_v>); - return x; - } template common::IfNoLvalue Convert(A &&x) { static_assert(!std::is_class_v>); return std::move(x); @@ -139,8 +135,9 @@ 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; } diff --git a/flang/lib/Common/idioms.cpp b/flang/lib/Common/idioms.cpp --- a/flang/lib/Common/idioms.cpp +++ b/flang/lib/Common/idioms.cpp @@ -10,7 +10,6 @@ #include #include #include -#include namespace Fortran::common { @@ -24,22 +23,4 @@ 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 diff --git a/flang/lib/Evaluate/characteristics.cpp b/flang/lib/Evaluate/characteristics.cpp --- a/flang/lib/Evaluate/characteristics.cpp +++ b/flang/lib/Evaluate/characteristics.cpp @@ -1000,7 +1000,7 @@ auto sep{": "s}; *whyNot = "incompatible procedure attributes"; differences.IterateOverMembers([&](Attr x) { - *whyNot += sep + EnumToString(x); + *whyNot += sep + std::string{EnumToString(x)}; sep = ", "; }); } diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp --- a/flang/lib/Lower/IO.cpp +++ b/flang/lib/Lower/IO.cpp @@ -2091,10 +2091,10 @@ 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(var.t)) - .c_str())), + std::get(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(loc, specFunc, args).getResult(0); @@ -2128,10 +2128,10 @@ 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(var.t)) - .c_str())), + std::get(var.t))} + .c_str())), builder.createConvert(loc, specFuncTy.getInput(2), addr), builder.createConvert(loc, specFuncTy.getInput(3), kind)}; return builder.create(loc, specFunc, args).getResult(0); @@ -2165,9 +2165,9 @@ 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(loc, specFunc, args); boolRefToLogical(loc, builder, addr); diff --git a/flang/lib/Parser/message.cpp b/flang/lib/Parser/message.cpp --- a/flang/lib/Parser/message.cpp +++ b/flang/lib/Parser/message.cpp @@ -70,13 +70,18 @@ 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(); } diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2621,6 +2621,7 @@ 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 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 void Unparse(llvm::raw_ostream &out, const A &root, Encoding encoding, bool capitalizeKeywords, bool backslashEscapes, diff --git a/flang/lib/Semantics/attr.cpp b/flang/lib/Semantics/attr.cpp --- a/flang/lib/Semantics/attr.cpp +++ b/flang/lib/Semantics/attr.cpp @@ -30,7 +30,7 @@ case Attr::INTENT_OUT: return "INTENT(OUT)"; default: - return EnumToString(attr); + return std::string{EnumToString(attr)}; } } diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp --- a/flang/lib/Semantics/expression.cpp +++ b/flang/lib/Semantics/expression.cpp @@ -43,7 +43,7 @@ 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); } diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp --- a/flang/lib/Semantics/mod-file.cpp +++ b/flang/lib/Semantics/mod-file.cpp @@ -59,7 +59,7 @@ 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 @@ 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); } diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp --- a/flang/lib/Semantics/symbol.cpp +++ b/flang/lib/Semantics/symbol.cpp @@ -671,20 +671,20 @@ 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);