From 1c6c01fbde87171457a5bce7b147fa5bcbaddae7 Mon Sep 17 00:00:00 2001 From: Justin Bogner Date: Wed, 1 Nov 2023 11:24:48 -0700 Subject: [PATCH] [Attributes][HLSL] Teach EnumArgument to refer to an external enum (#70835) Rather than write a bunch of logic to shepherd between enums with the same sets of values, add the ability for EnumArgument to refer to an external enum in the first place. --- .../modularize/ModularizeUtilities.cpp | 6 +- clang/include/clang/AST/Attr.h | 3 +- clang/include/clang/Basic/Attr.td | 35 ++-- clang/lib/CodeGen/CGHLSLRuntime.cpp | 59 +----- clang/lib/Sema/HLSLExternalSemaSource.cpp | 8 +- clang/unittests/Sema/SemaNoloadLookupTest.cpp | 2 +- clang/utils/TableGen/ClangAttrEmitter.cpp | 178 ++++++++++-------- 7 files changed, 136 insertions(+), 155 deletions(-) diff --git a/clang-tools-extra/modularize/ModularizeUtilities.cpp b/clang-tools-extra/modularize/ModularizeUtilities.cpp index 043f6f5b20b80..089f52f52ec4d 100644 --- a/clang-tools-extra/modularize/ModularizeUtilities.cpp +++ b/clang-tools-extra/modularize/ModularizeUtilities.cpp @@ -322,7 +322,7 @@ std::error_code ModularizeUtilities::loadModuleMap( // Walks the modules and collects referenced headers into // HeaderFileNames. bool ModularizeUtilities::collectModuleMapHeaders(clang::ModuleMap *ModMap) { - SmallVector, 0> Vec; + SmallVector, 0> Vec; for (auto &M : ModMap->modules()) Vec.emplace_back(M.first(), M.second); llvm::sort(Vec, llvm::less_first()); @@ -349,14 +349,14 @@ bool ModularizeUtilities::collectModuleHeaders(const clang::Module &Mod) { for (auto *Submodule : Mod.submodules()) collectModuleHeaders(*Submodule); - if (std::optional UmbrellaHeader = + if (std::optional UmbrellaHeader = Mod.getUmbrellaHeaderAsWritten()) { std::string HeaderPath = getCanonicalPath(UmbrellaHeader->Entry.getName()); // Collect umbrella header. HeaderFileNames.push_back(HeaderPath); // FUTURE: When needed, umbrella header header collection goes here. - } else if (std::optional UmbrellaDir = + } else if (std::optional UmbrellaDir = Mod.getUmbrellaDirAsWritten()) { // If there normal headers, assume these are umbrellas and skip collection. if (Mod.Headers->size() == 0) { diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 793732cd26b02..8884bd5a90de1 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -19,11 +19,12 @@ #include "clang/AST/Type.h" #include "clang/Basic/AttrKinds.h" #include "clang/Basic/AttributeCommonInfo.h" -#include "clang/Basic/LangOptions.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/Frontend/HLSL/HLSLResource.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/VersionTuple.h" #include "llvm/Support/raw_ostream.h" diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 25231c5b82b90..60b549999c155 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -277,23 +277,28 @@ class DefaultIntArgument : IntArgument { int Default = default; } -// This argument is more complex, it includes the enumerator type name, -// a list of strings to accept, and a list of enumerators to map them to. +// This argument is more complex, it includes the enumerator type +// name, whether the enum type is externally defined, a list of +// strings to accept, and a list of enumerators to map them to. class EnumArgument values, - list enums, bit opt = 0, bit fake = 0> + list enums, bit opt = 0, bit fake = 0, + bit isExternalType = 0> : Argument { string Type = type; list Values = values; list Enums = enums; + bit IsExternalType = isExternalType; } // FIXME: There should be a VariadicArgument type that takes any other type // of argument and generates the appropriate type. class VariadicEnumArgument values, - list enums> : Argument { + list enums, bit isExternalType = 0> + : Argument { string Type = type; list Values = values; list Enums = enums; + bit IsExternalType = isExternalType; } // This handles one spelling of an attribute. @@ -4182,26 +4187,26 @@ def HLSLResource : InheritableAttr { let Spellings = []; let Subjects = SubjectList<[Struct]>; let LangOpts = [HLSL]; - let Args = [EnumArgument<"ResourceType", "ResourceClass", + let Args = [EnumArgument<"ResourceClass", "llvm::hlsl::ResourceClass", ["SRV", "UAV", "CBuffer", "Sampler"], - ["SRV", "UAV", "CBuffer", "Sampler"] - >, - EnumArgument<"ResourceShape", "ResourceKind", + ["SRV", "UAV", "CBuffer", "Sampler"], + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1>, + EnumArgument<"ResourceKind", "llvm::hlsl::ResourceKind", ["Texture1D", "Texture2D", "Texture2DMS", "Texture3D", "TextureCube", "Texture1DArray", "Texture2DArray", "Texture2DMSArray", "TextureCubeArray", "TypedBuffer", "RawBuffer", - "StructuredBuffer", "CBufferKind", "SamplerKind", - "TBuffer", "RTAccelerationStructure", "FeedbackTexture2D", - "FeedbackTexture2DArray"], + "StructuredBuffer", "CBuffer", "Sampler", + "TBuffer", "RTAccelerationStructure", + "FeedbackTexture2D", "FeedbackTexture2DArray"], ["Texture1D", "Texture2D", "Texture2DMS", "Texture3D", "TextureCube", "Texture1DArray", "Texture2DArray", "Texture2DMSArray", "TextureCubeArray", "TypedBuffer", "RawBuffer", - "StructuredBuffer", "CBufferKind", "SamplerKind", - "TBuffer", "RTAccelerationStructure", "FeedbackTexture2D", - "FeedbackTexture2DArray"] - > + "StructuredBuffer", "CBuffer", "Sampler", + "TBuffer", "RTAccelerationStructure", + "FeedbackTexture2D", "FeedbackTexture2DArray"], + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1> ]; let Documentation = [InternalOnly]; } diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index e9fa273f21cc8..c239bc17ef267 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -223,56 +223,6 @@ void CGHLSLRuntime::addBufferResourceAnnotation(llvm::GlobalVariable *GV, ResourceMD->addOperand(Res.getMetadata()); } -static llvm::hlsl::ResourceKind -castResourceShapeToResourceKind(HLSLResourceAttr::ResourceKind RK) { - switch (RK) { - case HLSLResourceAttr::ResourceKind::Texture1D: - return llvm::hlsl::ResourceKind::Texture1D; - case HLSLResourceAttr::ResourceKind::Texture2D: - return llvm::hlsl::ResourceKind::Texture2D; - case HLSLResourceAttr::ResourceKind::Texture2DMS: - return llvm::hlsl::ResourceKind::Texture2DMS; - case HLSLResourceAttr::ResourceKind::Texture3D: - return llvm::hlsl::ResourceKind::Texture3D; - case HLSLResourceAttr::ResourceKind::TextureCube: - return llvm::hlsl::ResourceKind::TextureCube; - case HLSLResourceAttr::ResourceKind::Texture1DArray: - return llvm::hlsl::ResourceKind::Texture1DArray; - case HLSLResourceAttr::ResourceKind::Texture2DArray: - return llvm::hlsl::ResourceKind::Texture2DArray; - case HLSLResourceAttr::ResourceKind::Texture2DMSArray: - return llvm::hlsl::ResourceKind::Texture2DMSArray; - case HLSLResourceAttr::ResourceKind::TextureCubeArray: - return llvm::hlsl::ResourceKind::TextureCubeArray; - case HLSLResourceAttr::ResourceKind::TypedBuffer: - return llvm::hlsl::ResourceKind::TypedBuffer; - case HLSLResourceAttr::ResourceKind::RawBuffer: - return llvm::hlsl::ResourceKind::RawBuffer; - case HLSLResourceAttr::ResourceKind::StructuredBuffer: - return llvm::hlsl::ResourceKind::StructuredBuffer; - case HLSLResourceAttr::ResourceKind::CBufferKind: - return llvm::hlsl::ResourceKind::CBuffer; - case HLSLResourceAttr::ResourceKind::SamplerKind: - return llvm::hlsl::ResourceKind::Sampler; - case HLSLResourceAttr::ResourceKind::TBuffer: - return llvm::hlsl::ResourceKind::TBuffer; - case HLSLResourceAttr::ResourceKind::RTAccelerationStructure: - return llvm::hlsl::ResourceKind::RTAccelerationStructure; - case HLSLResourceAttr::ResourceKind::FeedbackTexture2D: - return llvm::hlsl::ResourceKind::FeedbackTexture2D; - case HLSLResourceAttr::ResourceKind::FeedbackTexture2DArray: - return llvm::hlsl::ResourceKind::FeedbackTexture2DArray; - } - // Make sure to update HLSLResourceAttr::ResourceKind when add new Kind to - // hlsl::ResourceKind. Assume FeedbackTexture2DArray is the last enum for - // HLSLResourceAttr::ResourceKind. - static_assert( - static_cast( - HLSLResourceAttr::ResourceKind::FeedbackTexture2DArray) == - (static_cast(llvm::hlsl::ResourceKind::NumEntries) - 2)); - llvm_unreachable("all switch cases should be covered"); -} - void CGHLSLRuntime::annotateHLSLResource(const VarDecl *D, GlobalVariable *GV) { const Type *Ty = D->getType()->getPointeeOrArrayElementType(); if (!Ty) @@ -284,15 +234,12 @@ void CGHLSLRuntime::annotateHLSLResource(const VarDecl *D, GlobalVariable *GV) { if (!Attr) return; - HLSLResourceAttr::ResourceClass RC = Attr->getResourceType(); - llvm::hlsl::ResourceKind RK = - castResourceShapeToResourceKind(Attr->getResourceShape()); + llvm::hlsl::ResourceClass RC = Attr->getResourceClass(); + llvm::hlsl::ResourceKind RK = Attr->getResourceKind(); QualType QT(Ty, 0); BufferResBinding Binding(D->getAttr()); - addBufferResourceAnnotation(GV, QT.getAsString(), - static_cast(RC), RK, - Binding); + addBufferResourceAnnotation(GV, QT.getAsString(), RC, RK, Binding); } CGHLSLRuntime::BufferResBinding::BufferResBinding( diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index fd86a5f8b49c0..0be76d4b36e04 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -115,9 +115,8 @@ struct BuiltinTypeDeclBuilder { return addMemberVariable("h", Ty, Access); } - BuiltinTypeDeclBuilder & - annotateResourceClass(HLSLResourceAttr::ResourceClass RC, - HLSLResourceAttr::ResourceKind RK) { + BuiltinTypeDeclBuilder &annotateResourceClass(ResourceClass RC, + ResourceKind RK) { if (Record->isCompleteDefinition()) return *this; Record->addAttr( @@ -503,7 +502,6 @@ void HLSLExternalSemaSource::completeBufferType(CXXRecordDecl *Record) { .addHandleMember() .addDefaultHandleConstructor(*SemaPtr, ResourceClass::UAV) .addArraySubscriptOperators() - .annotateResourceClass(HLSLResourceAttr::UAV, - HLSLResourceAttr::TypedBuffer) + .annotateResourceClass(ResourceClass::UAV, ResourceKind::TypedBuffer) .completeDefinition(); } diff --git a/clang/unittests/Sema/SemaNoloadLookupTest.cpp b/clang/unittests/Sema/SemaNoloadLookupTest.cpp index 2e74ad3a30a93..b24c72cba407f 100644 --- a/clang/unittests/Sema/SemaNoloadLookupTest.cpp +++ b/clang/unittests/Sema/SemaNoloadLookupTest.cpp @@ -118,7 +118,7 @@ class NoloadLookupConsumer : public SemaConsumer { if (!ID) return true; - Module *M = ID->getImportedModule(); + clang::Module *M = ID->getImportedModule(); assert(M); if (M->Name != "R") return true; diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 45e2fa7b283e1..4231bcbb36253 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -898,15 +898,25 @@ namespace { } class EnumArgument : public Argument { - std::string type; + std::string fullType; + StringRef shortType; std::vector values, enums, uniques; + bool isExternal; public: EnumArgument(const Record &Arg, StringRef Attr) - : Argument(Arg, Attr), type(std::string(Arg.getValueAsString("Type"))), - values(Arg.getValueAsListOfStrings("Values")), + : Argument(Arg, Attr), values(Arg.getValueAsListOfStrings("Values")), enums(Arg.getValueAsListOfStrings("Enums")), - uniques(uniqueEnumsInOrder(enums)) { + uniques(uniqueEnumsInOrder(enums)), + isExternal(Arg.getValueAsBit("IsExternalType")) { + StringRef Type = Arg.getValueAsString("Type"); + shortType = isExternal ? Type.rsplit("::").second : Type; + // If shortType didn't contain :: at all rsplit will give us an empty + // string. + if (shortType.empty()) + shortType = Type; + fullType = isExternal ? Type : (getAttrName() + "Attr::" + Type).str(); + // FIXME: Emit a proper error assert(!uniques.empty()); } @@ -914,7 +924,7 @@ namespace { bool isEnumArg() const override { return true; } void writeAccessors(raw_ostream &OS) const override { - OS << " " << type << " get" << getUpperName() << "() const {\n"; + OS << " " << fullType << " get" << getUpperName() << "() const {\n"; OS << " return " << getLowerName() << ";\n"; OS << " }"; } @@ -930,30 +940,32 @@ namespace { OS << getLowerName() << "(" << getUpperName() << ")"; } void writeCtorDefaultInitializers(raw_ostream &OS) const override { - OS << getLowerName() << "(" << type << "(0))"; + OS << getLowerName() << "(" << fullType << "(0))"; } void writeCtorParameters(raw_ostream &OS) const override { - OS << type << " " << getUpperName(); + OS << fullType << " " << getUpperName(); } void writeDeclarations(raw_ostream &OS) const override { - auto i = uniques.cbegin(), e = uniques.cend(); - // The last one needs to not have a comma. - --e; + if (!isExternal) { + auto i = uniques.cbegin(), e = uniques.cend(); + // The last one needs to not have a comma. + --e; + + OS << "public:\n"; + OS << " enum " << shortType << " {\n"; + for (; i != e; ++i) + OS << " " << *i << ",\n"; + OS << " " << *e << "\n"; + OS << " };\n"; + } - OS << "public:\n"; - OS << " enum " << type << " {\n"; - for (; i != e; ++i) - OS << " " << *i << ",\n"; - OS << " " << *e << "\n"; - OS << " };\n"; OS << "private:\n"; - OS << " " << type << " " << getLowerName() << ";"; + OS << " " << fullType << " " << getLowerName() << ";"; } void writePCHReadDecls(raw_ostream &OS) const override { - OS << " " << getAttrName() << "Attr::" << type << " " << getLowerName() - << "(static_cast<" << getAttrName() << "Attr::" << type - << ">(Record.readInt()));\n"; + OS << " " << fullType << " " << getLowerName() << "(static_cast<" + << fullType << ">(Record.readInt()));\n"; } void writePCHReadArgs(raw_ostream &OS) const override { @@ -961,46 +973,50 @@ namespace { } void writePCHWrite(raw_ostream &OS) const override { - OS << "Record.push_back(SA->get" << getUpperName() << "());\n"; + OS << "Record.push_back(static_cast(SA->get" << getUpperName() + << "()));\n"; } void writeValue(raw_ostream &OS) const override { // FIXME: this isn't 100% correct -- some enum arguments require printing // as a string literal, while others require printing as an identifier. // Tablegen currently does not distinguish between the two forms. - OS << "\\\"\" << " << getAttrName() << "Attr::Convert" << type << "ToStr(get" - << getUpperName() << "()) << \"\\\""; + OS << "\\\"\" << " << getAttrName() << "Attr::Convert" << shortType + << "ToStr(get" << getUpperName() << "()) << \"\\\""; } void writeDump(raw_ostream &OS) const override { OS << " switch(SA->get" << getUpperName() << "()) {\n"; for (const auto &I : uniques) { - OS << " case " << getAttrName() << "Attr::" << I << ":\n"; + OS << " case " << fullType << "::" << I << ":\n"; OS << " OS << \" " << I << "\";\n"; OS << " break;\n"; } + if (isExternal) { + OS << " default:\n"; + OS << " llvm_unreachable(\"Invalid attribute value\");\n"; + } OS << " }\n"; } void writeConversion(raw_ostream &OS, bool Header) const { if (Header) { - OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type - << " &Out);\n"; - OS << " static const char *Convert" << type << "ToStr(" << type - << " Val);\n"; + OS << " static bool ConvertStrTo" << shortType << "(StringRef Val, " + << fullType << " &Out);\n"; + OS << " static const char *Convert" << shortType << "ToStr(" + << fullType << " Val);\n"; return; } - OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type - << "(StringRef Val, " << type << " &Out) {\n"; - OS << " std::optional<" << type - << "> R = llvm::StringSwitch>(Val)\n"; + OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << shortType + << "(StringRef Val, " << fullType << " &Out) {\n"; + OS << " std::optional<" << fullType << "> " + << "R = llvm::StringSwitch>(Val)\n"; for (size_t I = 0; I < enums.size(); ++I) { OS << " .Case(\"" << values[I] << "\", "; - OS << getAttrName() << "Attr::" << enums[I] << ")\n"; + OS << fullType << "::" << enums[I] << ")\n"; } - OS << " .Default(std::optional<" << type << ">());\n"; + OS << " .Default(std::optional<" << fullType << ">());\n"; OS << " if (R) {\n"; OS << " Out = *R;\n return true;\n }\n"; OS << " return false;\n"; @@ -1010,14 +1026,17 @@ namespace { // trivial because some enumeration values have multiple named // enumerators, such as type_visibility(internal) and // type_visibility(hidden) both mapping to TypeVisibilityAttr::Hidden. - OS << "const char *" << getAttrName() << "Attr::Convert" << type - << "ToStr(" << type << " Val) {\n" + OS << "const char *" << getAttrName() << "Attr::Convert" << shortType + << "ToStr(" << fullType << " Val) {\n" << " switch(Val) {\n"; SmallDenseSet Uniques; for (size_t I = 0; I < enums.size(); ++I) { if (Uniques.insert(enums[I]).second) - OS << " case " << getAttrName() << "Attr::" << enums[I] - << ": return \"" << values[I] << "\";\n"; + OS << " case " << fullType << "::" << enums[I] << ": return \"" + << values[I] << "\";\n"; + } + if (isExternal) { + OS << " default: llvm_unreachable(\"Invalid attribute value\");\n"; } OS << " }\n" << " llvm_unreachable(\"No enumerator with that value\");\n" @@ -1026,27 +1045,36 @@ namespace { }; class VariadicEnumArgument: public VariadicArgument { - std::string type, QualifiedTypeName; + std::string fullType; + StringRef shortType; std::vector values, enums, uniques; + bool isExternal; protected: void writeValueImpl(raw_ostream &OS) const override { // FIXME: this isn't 100% correct -- some enum arguments require printing // as a string literal, while others require printing as an identifier. // Tablegen currently does not distinguish between the two forms. - OS << " OS << \"\\\"\" << " << getAttrName() << "Attr::Convert" << type - << "ToStr(Val)" << "<< \"\\\"\";\n"; + OS << " OS << \"\\\"\" << " << getAttrName() << "Attr::Convert" + << shortType << "ToStr(Val)" + << "<< \"\\\"\";\n"; } public: VariadicEnumArgument(const Record &Arg, StringRef Attr) : VariadicArgument(Arg, Attr, std::string(Arg.getValueAsString("Type"))), - type(std::string(Arg.getValueAsString("Type"))), values(Arg.getValueAsListOfStrings("Values")), enums(Arg.getValueAsListOfStrings("Enums")), - uniques(uniqueEnumsInOrder(enums)) { - QualifiedTypeName = getAttrName().str() + "Attr::" + type; + uniques(uniqueEnumsInOrder(enums)), + isExternal(Arg.getValueAsBit("IsExternalType")) { + StringRef Type = Arg.getValueAsString("Type"); + shortType = isExternal ? Type.rsplit("::").second : Type; + // If shortType didn't contain :: at all rsplit will give us an empty + // string. + if (shortType.empty()) + shortType = Type; + fullType = isExternal ? Type : (getAttrName() + "Attr::" + Type).str(); // FIXME: Emit a proper error assert(!uniques.empty()); @@ -1055,16 +1083,18 @@ namespace { bool isVariadicEnumArg() const override { return true; } void writeDeclarations(raw_ostream &OS) const override { - auto i = uniques.cbegin(), e = uniques.cend(); - // The last one needs to not have a comma. - --e; - - OS << "public:\n"; - OS << " enum " << type << " {\n"; - for (; i != e; ++i) - OS << " " << *i << ",\n"; - OS << " " << *e << "\n"; - OS << " };\n"; + if (!isExternal) { + auto i = uniques.cbegin(), e = uniques.cend(); + // The last one needs to not have a comma. + --e; + + OS << "public:\n"; + OS << " enum " << shortType << " {\n"; + for (; i != e; ++i) + OS << " " << *i << ",\n"; + OS << " " << *e << "\n"; + OS << " };\n"; + } OS << "private:\n"; VariadicArgument::writeDeclarations(OS); @@ -1076,7 +1106,7 @@ namespace { << getLowerName() << "_end(); I != E; ++I) {\n"; OS << " switch(*I) {\n"; for (const auto &UI : uniques) { - OS << " case " << getAttrName() << "Attr::" << UI << ":\n"; + OS << " case " << fullType << "::" << UI << ":\n"; OS << " OS << \" " << UI << "\";\n"; OS << " break;\n"; } @@ -1086,13 +1116,13 @@ namespace { void writePCHReadDecls(raw_ostream &OS) const override { OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n"; - OS << " SmallVector<" << QualifiedTypeName << ", 4> " << getLowerName() + OS << " SmallVector<" << fullType << ", 4> " << getLowerName() << ";\n"; OS << " " << getLowerName() << ".reserve(" << getLowerName() << "Size);\n"; OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n"; - OS << " " << getLowerName() << ".push_back(" << "static_cast<" - << QualifiedTypeName << ">(Record.readInt()));\n"; + OS << " " << getLowerName() << ".push_back(" + << "static_cast<" << fullType << ">(Record.readInt()));\n"; } void writePCHWrite(raw_ostream &OS) const override { @@ -1100,42 +1130,42 @@ namespace { OS << " for (" << getAttrName() << "Attr::" << getLowerName() << "_iterator i = SA->" << getLowerName() << "_begin(), e = SA->" << getLowerName() << "_end(); i != e; ++i)\n"; - OS << " " << WritePCHRecord(QualifiedTypeName, "(*i)"); + OS << " " << WritePCHRecord(fullType, "(*i)"); } void writeConversion(raw_ostream &OS, bool Header) const { if (Header) { - OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type - << " &Out);\n"; - OS << " static const char *Convert" << type << "ToStr(" << type - << " Val);\n"; + OS << " static bool ConvertStrTo" << shortType << "(StringRef Val, " + << fullType << " &Out);\n"; + OS << " static const char *Convert" << shortType << "ToStr(" + << fullType << " Val);\n"; return; } - OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type + OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << shortType << "(StringRef Val, "; - OS << type << " &Out) {\n"; - OS << " std::optional<" << type + OS << fullType << " &Out) {\n"; + OS << " std::optional<" << fullType << "> R = llvm::StringSwitch>(Val)\n"; + OS << fullType << ">>(Val)\n"; for (size_t I = 0; I < enums.size(); ++I) { OS << " .Case(\"" << values[I] << "\", "; - OS << getAttrName() << "Attr::" << enums[I] << ")\n"; + OS << fullType << "::" << enums[I] << ")\n"; } - OS << " .Default(std::optional<" << type << ">());\n"; + OS << " .Default(std::optional<" << fullType << ">());\n"; OS << " if (R) {\n"; OS << " Out = *R;\n return true;\n }\n"; OS << " return false;\n"; OS << "}\n\n"; - OS << "const char *" << getAttrName() << "Attr::Convert" << type - << "ToStr(" << type << " Val) {\n" + OS << "const char *" << getAttrName() << "Attr::Convert" << shortType + << "ToStr(" << fullType << " Val) {\n" << " switch(Val) {\n"; SmallDenseSet Uniques; for (size_t I = 0; I < enums.size(); ++I) { if (Uniques.insert(enums[I]).second) - OS << " case " << getAttrName() << "Attr::" << enums[I] - << ": return \"" << values[I] << "\";\n"; + OS << " case " << fullType << "::" << enums[I] << ": return \"" + << values[I] << "\";\n"; } OS << " }\n" << " llvm_unreachable(\"No enumerator with that value\");\n"