| //===-- ClangAttrEmitter.cpp - Generate Clang attribute handling ----------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // These tablegen backends emit Clang attribute processing code | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "TableGenBackends.h" | |
| #include "ASTTableGen.h" | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/DenseSet.h" | |
| #include "llvm/ADT/MapVector.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/SmallString.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/ADT/StringMap.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/ADT/StringSwitch.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "llvm/TableGen/StringMatcher.h" | |
| #include "llvm/TableGen/TableGenBackend.h" | |
| #include <cassert> | |
| #include <cctype> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <map> | |
| #include <memory> | |
| #include <optional> | |
| #include <set> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| using namespace llvm; | |
| namespace { | |
| class FlattenedSpelling { | |
| StringRef V, N, NS; | |
| bool K = false; | |
| const Record &OriginalSpelling; | |
| public: | |
| FlattenedSpelling(StringRef Variety, StringRef Name, StringRef Namespace, | |
| bool KnownToGCC, const Record &OriginalSpelling) | |
| : V(Variety), N(Name), NS(Namespace), K(KnownToGCC), | |
| OriginalSpelling(OriginalSpelling) {} | |
| explicit FlattenedSpelling(const Record &Spelling) | |
| : V(Spelling.getValueAsString("Variety")), | |
| N(Spelling.getValueAsString("Name")), OriginalSpelling(Spelling) { | |
| assert(V != "GCC" && V != "Clang" && | |
| "Given a GCC spelling, which means this hasn't been flattened!"); | |
| if (V == "CXX11" || V == "C23" || V == "Pragma") | |
| NS = Spelling.getValueAsString("Namespace"); | |
| } | |
| StringRef variety() const { return V; } | |
| StringRef name() const { return N; } | |
| StringRef nameSpace() const { return NS; } | |
| bool knownToGCC() const { return K; } | |
| const Record &getSpellingRecord() const { return OriginalSpelling; } | |
| }; | |
| struct FlattenedSpellingInfo { | |
| FlattenedSpellingInfo(StringRef Syntax, StringRef Scope, | |
| const std::string &TargetTest, uint32_t ArgMask) | |
| : Syntax(Syntax), Scope(Scope), TargetTest(TargetTest), ArgMask(ArgMask) { | |
| } | |
| StringRef Syntax; | |
| StringRef Scope; | |
| std::string TargetTest; | |
| uint32_t ArgMask; | |
| }; | |
| using FSIVecTy = std::vector<FlattenedSpellingInfo>; | |
| } // end anonymous namespace | |
| static bool GenerateTargetSpecificAttrChecks(const Record *R, | |
| std::vector<StringRef> &Arches, | |
| std::string &Test, | |
| std::string *FnName); | |
| static bool isStringLiteralArgument(const Record *Arg); | |
| static bool isVariadicStringLiteralArgument(const Record *Arg); | |
| static std::vector<FlattenedSpelling> | |
| GetFlattenedSpellings(const Record &Attr) { | |
| std::vector<FlattenedSpelling> Ret; | |
| for (const auto &Spelling : Attr.getValueAsListOfDefs("Spellings")) { | |
| StringRef Variety = Spelling->getValueAsString("Variety"); | |
| StringRef Name = Spelling->getValueAsString("Name"); | |
| if (Variety == "GCC") { | |
| Ret.emplace_back("GNU", Name, "", true, *Spelling); | |
| Ret.emplace_back("CXX11", Name, "gnu", true, *Spelling); | |
| if (Spelling->getValueAsBit("AllowInC")) | |
| Ret.emplace_back("C23", Name, "gnu", true, *Spelling); | |
| } else if (Variety == "Clang") { | |
| Ret.emplace_back("GNU", Name, "", false, *Spelling); | |
| Ret.emplace_back("CXX11", Name, "clang", false, *Spelling); | |
| if (Spelling->getValueAsBit("AllowInC")) | |
| Ret.emplace_back("C23", Name, "clang", false, *Spelling); | |
| } else if (Variety == "ClangGCC") { | |
| Ret.emplace_back("GNU", Name, "", false, *Spelling); | |
| Ret.emplace_back("CXX11", Name, "clang", false, *Spelling); | |
| Ret.emplace_back("CXX11", Name, "gnu", false, *Spelling); | |
| if (Spelling->getValueAsBit("AllowInC")) { | |
| Ret.emplace_back("C23", Name, "clang", false, *Spelling); | |
| Ret.emplace_back("C23", Name, "gnu", false, *Spelling); | |
| } | |
| } else { | |
| Ret.push_back(FlattenedSpelling(*Spelling)); | |
| } | |
| } | |
| return Ret; | |
| } | |
| static std::string ReadPCHRecord(StringRef type) { | |
| return StringSwitch<std::string>(type) | |
| .EndsWith("Decl *", "Record.readDeclAs<" + type.drop_back().str() + ">()") | |
| .Case("TypeSourceInfo *", "Record.readTypeSourceInfo()") | |
| .Case("Expr *", "Record.readExpr()") | |
| .Case("const IdentifierInfo *", "Record.readIdentifier()") | |
| .Case("StringRef", "Record.readString()") | |
| .Case("ParamIdx", "ParamIdx::deserialize(Record.readInt())") | |
| .Case("OMPTraitInfo *", "Record.readOMPTraitInfo()") | |
| .Default("Record.readInt()"); | |
| } | |
| // Get a type that is suitable for storing an object of the specified type. | |
| static StringRef getStorageType(StringRef type) { | |
| return StringSwitch<StringRef>(type) | |
| .Case("StringRef", "std::string") | |
| .Default(type); | |
| } | |
| // Assumes that the way to get the value is SA->getname() | |
| static std::string WritePCHRecord(StringRef type, StringRef name) { | |
| return "Record." + | |
| StringSwitch<std::string>(type) | |
| .EndsWith("Decl *", "AddDeclRef(" + name.str() + ");\n") | |
| .Case("TypeSourceInfo *", | |
| "AddTypeSourceInfo(" + name.str() + ");\n") | |
| .Case("Expr *", "AddStmt(" + name.str() + ");\n") | |
| .Case("const IdentifierInfo *", | |
| "AddIdentifierRef(" + name.str() + ");\n") | |
| .Case("StringRef", "AddString(" + name.str() + ");\n") | |
| .Case("ParamIdx", "push_back(" + name.str() + ".serialize());\n") | |
| .Case("OMPTraitInfo *", "writeOMPTraitInfo(" + name.str() + ");\n") | |
| .Default("push_back(" + name.str() + ");\n"); | |
| } | |
| // Normalize attribute name by removing leading and trailing | |
| // underscores. For example, __foo, foo__, __foo__ would | |
| // become foo. | |
| static StringRef NormalizeAttrName(StringRef AttrName) { | |
| AttrName.consume_front("__"); | |
| AttrName.consume_back("__"); | |
| return AttrName; | |
| } | |
| // Normalize the name by removing any and all leading and trailing underscores. | |
| // This is different from NormalizeAttrName in that it also handles names like | |
| // _pascal and __pascal. | |
| static StringRef NormalizeNameForSpellingComparison(StringRef Name) { | |
| return Name.trim("_"); | |
| } | |
| // Normalize the spelling of a GNU attribute (i.e. "x" in "__attribute__((x))"), | |
| // removing "__" if it appears at the beginning and end of the attribute's name. | |
| static StringRef NormalizeGNUAttrSpelling(StringRef AttrSpelling) { | |
| if (AttrSpelling.starts_with("__") && AttrSpelling.ends_with("__")) { | |
| AttrSpelling = AttrSpelling.substr(2, AttrSpelling.size() - 4); | |
| } | |
| return AttrSpelling; | |
| } | |
| typedef std::vector<std::pair<std::string, const Record *>> ParsedAttrMap; | |
| static ParsedAttrMap getParsedAttrList(const RecordKeeper &Records, | |
| ParsedAttrMap *Dupes = nullptr, | |
| bool SemaOnly = true) { | |
| std::set<std::string> Seen; | |
| ParsedAttrMap R; | |
| for (const Record *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| if (!SemaOnly || Attr->getValueAsBit("SemaHandler")) { | |
| std::string AN; | |
| if (Attr->isSubClassOf("TargetSpecificAttr") && | |
| !Attr->isValueUnset("ParseKind")) { | |
| AN = Attr->getValueAsString("ParseKind").str(); | |
| // If this attribute has already been handled, it does not need to be | |
| // handled again. | |
| if (!Seen.insert(AN).second) { | |
| if (Dupes) | |
| Dupes->push_back(std::make_pair(AN, Attr)); | |
| continue; | |
| } | |
| } else | |
| AN = NormalizeAttrName(Attr->getName()).str(); | |
| R.push_back(std::make_pair(AN, Attr)); | |
| } | |
| } | |
| return R; | |
| } | |
| namespace { | |
| class Argument { | |
| std::string lowerName, upperName; | |
| StringRef attrName; | |
| bool isOpt; | |
| bool Fake; | |
| public: | |
| Argument(StringRef Arg, StringRef Attr) | |
| : lowerName(Arg.str()), upperName(lowerName), attrName(Attr), | |
| isOpt(false), Fake(false) { | |
| if (!lowerName.empty()) { | |
| lowerName[0] = std::tolower(lowerName[0]); | |
| upperName[0] = std::toupper(upperName[0]); | |
| } | |
| // Work around MinGW's macro definition of 'interface' to 'struct'. We | |
| // have an attribute argument called 'Interface', so only the lower case | |
| // name conflicts with the macro definition. | |
| if (lowerName == "interface") | |
| lowerName = "interface_"; | |
| } | |
| Argument(const Record &Arg, StringRef Attr) | |
| : Argument(Arg.getValueAsString("Name"), Attr) {} | |
| virtual ~Argument() = default; | |
| StringRef getLowerName() const { return lowerName; } | |
| StringRef getUpperName() const { return upperName; } | |
| StringRef getAttrName() const { return attrName; } | |
| bool isOptional() const { return isOpt; } | |
| void setOptional(bool set) { isOpt = set; } | |
| bool isFake() const { return Fake; } | |
| void setFake(bool fake) { Fake = fake; } | |
| // These functions print the argument contents formatted in different ways. | |
| virtual void writeAccessors(raw_ostream &OS) const = 0; | |
| virtual void writeAccessorDefinitions(raw_ostream &OS) const {} | |
| virtual void writeASTVisitorTraversal(raw_ostream &OS) const {} | |
| virtual void writeCloneArgs(raw_ostream &OS) const = 0; | |
| virtual void writeTemplateInstantiationArgs(raw_ostream &OS) const = 0; | |
| virtual void writeTemplateInstantiation(raw_ostream &OS) const {} | |
| virtual void writeCtorBody(raw_ostream &OS) const {} | |
| virtual void writeCtorInitializers(raw_ostream &OS) const = 0; | |
| virtual void writeCtorDefaultInitializers(raw_ostream &OS) const = 0; | |
| virtual void writeCtorParameters(raw_ostream &OS) const = 0; | |
| virtual void writeDeclarations(raw_ostream &OS) const = 0; | |
| virtual void writePCHReadArgs(raw_ostream &OS) const = 0; | |
| virtual void writePCHReadDecls(raw_ostream &OS) const = 0; | |
| virtual void writePCHWrite(raw_ostream &OS) const = 0; | |
| virtual std::string getIsOmitted() const { return "false"; } | |
| virtual void writeValue(raw_ostream &OS) const = 0; | |
| virtual void writeDump(raw_ostream &OS) const = 0; | |
| virtual void writeDumpChildren(raw_ostream &OS) const {} | |
| virtual void writeHasChildren(raw_ostream &OS) const { OS << "false"; } | |
| virtual bool isEnumArg() const { return false; } | |
| virtual bool isVariadicEnumArg() const { return false; } | |
| virtual bool isVariadic() const { return false; } | |
| virtual void writeImplicitCtorArgs(raw_ostream &OS) const { | |
| OS << getUpperName(); | |
| } | |
| constexpr StringRef getArgEqualityFn() const { return "equalAttrArgs"; } | |
| virtual std::string emitAttrArgEqualityCheck() const { | |
| std::string S = std::string("get") + std::string(getUpperName()) + "()"; | |
| return getArgEqualityFn().str() + "(" + S + ", Other." + S + ", Context)"; | |
| } | |
| virtual std::string emitAttrArgProfileCall() const { | |
| return "profileAttrArg(ID, Ctx, get" + getUpperName().str() + "())"; | |
| } | |
| }; | |
| class SimpleArgument : public Argument { | |
| std::string type; | |
| public: | |
| SimpleArgument(const Record &Arg, StringRef Attr, std::string T) | |
| : Argument(Arg, Attr), type(std::move(T)) {} | |
| std::string getType() const { return type; } | |
| void writeAccessors(raw_ostream &OS) const override { | |
| OS << " " << type << " get" << getUpperName() << "() const {\n"; | |
| OS << " return " << getLowerName() << ";\n"; | |
| OS << " }"; | |
| } | |
| void writeCloneArgs(raw_ostream &OS) const override { | |
| OS << getLowerName(); | |
| } | |
| void writeTemplateInstantiationArgs(raw_ostream &OS) const override { | |
| OS << "A->get" << getUpperName() << "()"; | |
| } | |
| void writeCtorInitializers(raw_ostream &OS) const override { | |
| OS << getLowerName() << "(" << getUpperName() << ")"; | |
| } | |
| void writeCtorDefaultInitializers(raw_ostream &OS) const override { | |
| OS << getLowerName() << "()"; | |
| } | |
| void writeCtorParameters(raw_ostream &OS) const override { | |
| OS << type << " " << getUpperName(); | |
| } | |
| void writeDeclarations(raw_ostream &OS) const override { | |
| OS << type << " " << getLowerName() << ";"; | |
| } | |
| void writePCHReadDecls(raw_ostream &OS) const override { | |
| std::string read = ReadPCHRecord(type); | |
| OS << " " << type << " " << getLowerName() << " = " << read << ";\n"; | |
| } | |
| void writePCHReadArgs(raw_ostream &OS) const override { | |
| OS << getLowerName(); | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << " " | |
| << WritePCHRecord(type, "SA->get" + getUpperName().str() + "()"); | |
| } | |
| std::string getIsOmitted() const override { | |
| auto IsOneOf = [](StringRef subject, auto... list) { | |
| return ((subject == list) || ...); | |
| }; | |
| if (IsOneOf(type, "const IdentifierInfo *", "Expr *")) | |
| return "!get" + getUpperName().str() + "()"; | |
| if (IsOneOf(type, "TypeSourceInfo *")) | |
| return "!get" + getUpperName().str() + "Loc()"; | |
| if (IsOneOf(type, "ParamIdx")) | |
| return "!get" + getUpperName().str() + "().isValid()"; | |
| assert(IsOneOf(type, "unsigned", "int", "bool", "FunctionDecl *", | |
| "VarDecl *")); | |
| return "false"; | |
| } | |
| void writeValue(raw_ostream &OS) const override { | |
| if (type == "FunctionDecl *") | |
| OS << "\" << get" << getUpperName() | |
| << "()->getNameInfo().getAsString() << \""; | |
| else if (type == "const IdentifierInfo *") | |
| // Some non-optional (comma required) identifier arguments can be the | |
| // empty string but are then recorded as a nullptr. | |
| OS << "\" << (get" << getUpperName() << "() ? get" << getUpperName() | |
| << "()->getName() : \"\") << \""; | |
| else if (type == "VarDecl *") | |
| OS << "\" << get" << getUpperName() << "()->getName() << \""; | |
| else if (type == "TypeSourceInfo *") | |
| OS << "\" << get" << getUpperName() << "().getAsString() << \""; | |
| else if (type == "ParamIdx") | |
| OS << "\" << get" << getUpperName() << "().getSourceIndex() << \""; | |
| else | |
| OS << "\" << get" << getUpperName() << "() << \""; | |
| } | |
| void writeDump(raw_ostream &OS) const override { | |
| if (StringRef(type).ends_with("Decl *")) { | |
| OS << " OS << \" \";\n"; | |
| OS << " dumpBareDeclRef(SA->get" << getUpperName() << "());\n"; | |
| } else if (type == "const IdentifierInfo *") { | |
| // Some non-optional (comma required) identifier arguments can be the | |
| // empty string but are then recorded as a nullptr. | |
| OS << " if (SA->get" << getUpperName() << "())\n" | |
| << " OS << \" \" << SA->get" << getUpperName() | |
| << "()->getName();\n"; | |
| } else if (type == "TypeSourceInfo *") { | |
| if (isOptional()) | |
| OS << " if (SA->get" << getUpperName() << "Loc())"; | |
| OS << " OS << \" \" << SA->get" << getUpperName() | |
| << "().getAsString();\n"; | |
| } else if (type == "bool") { | |
| OS << " if (SA->get" << getUpperName() << "()) OS << \" " | |
| << getUpperName() << "\";\n"; | |
| } else if (type == "int" || type == "unsigned") { | |
| OS << " OS << \" \" << SA->get" << getUpperName() << "();\n"; | |
| } else if (type == "ParamIdx") { | |
| if (isOptional()) | |
| OS << " if (SA->get" << getUpperName() << "().isValid())\n "; | |
| OS << " OS << \" \" << SA->get" << getUpperName() | |
| << "().getSourceIndex();\n"; | |
| } else if (type == "OMPTraitInfo *") { | |
| OS << " OS << \" \" << SA->get" << getUpperName() << "();\n"; | |
| } else { | |
| llvm_unreachable("Unknown SimpleArgument type!"); | |
| } | |
| } | |
| }; | |
| class DefaultSimpleArgument : public SimpleArgument { | |
| int64_t Default; | |
| public: | |
| DefaultSimpleArgument(const Record &Arg, StringRef Attr, std::string T, | |
| int64_t Default) | |
| : SimpleArgument(Arg, Attr, std::move(T)), Default(Default) {} | |
| void writeAccessors(raw_ostream &OS) const override { | |
| SimpleArgument::writeAccessors(OS); | |
| OS << "\n\n static const " << getType() << " Default" << getUpperName() | |
| << " = "; | |
| if (getType() == "bool") | |
| OS << (Default != 0 ? "true" : "false"); | |
| else | |
| OS << Default; | |
| OS << ";"; | |
| } | |
| }; | |
| class StringArgument : public Argument { | |
| public: | |
| StringArgument(const Record &Arg, StringRef Attr) | |
| : Argument(Arg, Attr) | |
| {} | |
| void writeAccessors(raw_ostream &OS) const override { | |
| OS << " llvm::StringRef get" << getUpperName() << "() const {\n"; | |
| OS << " return llvm::StringRef(" << getLowerName() << ", " | |
| << getLowerName() << "Length);\n"; | |
| OS << " }\n"; | |
| OS << " unsigned get" << getUpperName() << "Length() const {\n"; | |
| OS << " return " << getLowerName() << "Length;\n"; | |
| OS << " }\n"; | |
| OS << " void set" << getUpperName() | |
| << "(ASTContext &C, llvm::StringRef S) {\n"; | |
| OS << " " << getLowerName() << "Length = S.size();\n"; | |
| OS << " this->" << getLowerName() << " = new (C, 1) char [" | |
| << getLowerName() << "Length];\n"; | |
| OS << " if (!S.empty())\n"; | |
| OS << " std::memcpy(this->" << getLowerName() << ", S.data(), " | |
| << getLowerName() << "Length);\n"; | |
| OS << " }"; | |
| } | |
| void writeCloneArgs(raw_ostream &OS) const override { | |
| OS << "get" << getUpperName() << "()"; | |
| } | |
| void writeTemplateInstantiationArgs(raw_ostream &OS) const override { | |
| OS << "A->get" << getUpperName() << "()"; | |
| } | |
| void writeCtorBody(raw_ostream &OS) const override { | |
| OS << " if (!" << getUpperName() << ".empty())\n"; | |
| OS << " std::memcpy(" << getLowerName() << ", " << getUpperName() | |
| << ".data(), " << getLowerName() << "Length);\n"; | |
| } | |
| void writeCtorInitializers(raw_ostream &OS) const override { | |
| OS << getLowerName() << "Length(" << getUpperName() << ".size())," | |
| << getLowerName() << "(new (Ctx, 1) char[" << getLowerName() | |
| << "Length])"; | |
| } | |
| void writeCtorDefaultInitializers(raw_ostream &OS) const override { | |
| OS << getLowerName() << "Length(0)," << getLowerName() << "(nullptr)"; | |
| } | |
| void writeCtorParameters(raw_ostream &OS) const override { | |
| OS << "llvm::StringRef " << getUpperName(); | |
| } | |
| void writeDeclarations(raw_ostream &OS) const override { | |
| OS << "unsigned " << getLowerName() << "Length;\n"; | |
| OS << "char *" << getLowerName() << ";"; | |
| } | |
| void writePCHReadDecls(raw_ostream &OS) const override { | |
| OS << " std::string " << getLowerName() | |
| << "= Record.readString();\n"; | |
| } | |
| void writePCHReadArgs(raw_ostream &OS) const override { | |
| OS << getLowerName(); | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << " Record.AddString(SA->get" << getUpperName() << "());\n"; | |
| } | |
| void writeValue(raw_ostream &OS) const override { | |
| OS << "\\\"\" << get" << getUpperName() << "() << \"\\\""; | |
| } | |
| void writeDump(raw_ostream &OS) const override { | |
| OS << " OS << \" \\\"\" << SA->get" << getUpperName() | |
| << "() << \"\\\"\";\n"; | |
| } | |
| }; | |
| class AlignedArgument : public Argument { | |
| public: | |
| AlignedArgument(const Record &Arg, StringRef Attr) | |
| : Argument(Arg, Attr) | |
| {} | |
| void writeAccessors(raw_ostream &OS) const override { | |
| OS << " bool is" << getUpperName() << "Dependent() const;\n"; | |
| OS << " bool is" << getUpperName() << "ErrorDependent() const;\n"; | |
| OS << " unsigned get" << getUpperName() << "(ASTContext &Ctx) const;\n"; | |
| OS << " bool is" << getUpperName() << "Expr() const {\n"; | |
| OS << " return is" << getLowerName() << "Expr;\n"; | |
| OS << " }\n"; | |
| OS << " Expr *get" << getUpperName() << "Expr() const {\n"; | |
| OS << " assert(is" << getLowerName() << "Expr);\n"; | |
| OS << " return " << getLowerName() << "Expr;\n"; | |
| OS << " }\n"; | |
| OS << " TypeSourceInfo *get" << getUpperName() << "Type() const {\n"; | |
| OS << " assert(!is" << getLowerName() << "Expr);\n"; | |
| OS << " return " << getLowerName() << "Type;\n"; | |
| OS << " }"; | |
| OS << " std::optional<unsigned> getCached" << getUpperName() | |
| << "Value() const {\n"; | |
| OS << " return " << getLowerName() << "Cache;\n"; | |
| OS << " }"; | |
| OS << " void setCached" << getUpperName() | |
| << "Value(unsigned AlignVal) {\n"; | |
| OS << " " << getLowerName() << "Cache = AlignVal;\n"; | |
| OS << " }"; | |
| } | |
| void writeAccessorDefinitions(raw_ostream &OS) const override { | |
| OS << "bool " << getAttrName() << "Attr::is" << getUpperName() | |
| << "Dependent() const {\n"; | |
| OS << " if (is" << getLowerName() << "Expr)\n"; | |
| OS << " return " << getLowerName() << "Expr && (" << getLowerName() | |
| << "Expr->isValueDependent() || " << getLowerName() | |
| << "Expr->isTypeDependent());\n"; | |
| OS << " else\n"; | |
| OS << " return " << getLowerName() | |
| << "Type->getType()->isDependentType();\n"; | |
| OS << "}\n"; | |
| OS << "bool " << getAttrName() << "Attr::is" << getUpperName() | |
| << "ErrorDependent() const {\n"; | |
| OS << " if (is" << getLowerName() << "Expr)\n"; | |
| OS << " return " << getLowerName() << "Expr && " << getLowerName() | |
| << "Expr->containsErrors();\n"; | |
| OS << " return " << getLowerName() | |
| << "Type->getType()->containsErrors();\n"; | |
| OS << "}\n"; | |
| } | |
| void writeASTVisitorTraversal(raw_ostream &OS) const override { | |
| StringRef Name = getUpperName(); | |
| OS << " if (A->is" << Name << "Expr()) {\n" | |
| << " if (!getDerived().TraverseStmt(A->get" << Name << "Expr()))\n" | |
| << " return false;\n" | |
| << " } else if (auto *TSI = A->get" << Name << "Type()) {\n" | |
| << " if (!getDerived().TraverseTypeLoc(TSI->getTypeLoc()))\n" | |
| << " return false;\n" | |
| << " }\n"; | |
| } | |
| void writeCloneArgs(raw_ostream &OS) const override { | |
| OS << "is" << getLowerName() << "Expr, is" << getLowerName() | |
| << "Expr ? static_cast<void*>(" << getLowerName() | |
| << "Expr) : " << getLowerName() | |
| << "Type"; | |
| } | |
| void writeTemplateInstantiationArgs(raw_ostream &OS) const override { | |
| // FIXME: move the definition in Sema::InstantiateAttrs to here. | |
| // In the meantime, aligned attributes are cloned. | |
| } | |
| void writeCtorBody(raw_ostream &OS) const override { | |
| OS << " if (is" << getLowerName() << "Expr)\n"; | |
| OS << " " << getLowerName() << "Expr = reinterpret_cast<Expr *>(" | |
| << getUpperName() << ");\n"; | |
| OS << " else\n"; | |
| OS << " " << getLowerName() | |
| << "Type = reinterpret_cast<TypeSourceInfo *>(" << getUpperName() | |
| << ");\n"; | |
| } | |
| void writeCtorInitializers(raw_ostream &OS) const override { | |
| OS << "is" << getLowerName() << "Expr(Is" << getUpperName() << "Expr)"; | |
| } | |
| void writeCtorDefaultInitializers(raw_ostream &OS) const override { | |
| OS << "is" << getLowerName() << "Expr(false)"; | |
| } | |
| void writeCtorParameters(raw_ostream &OS) const override { | |
| OS << "bool Is" << getUpperName() << "Expr, void *" << getUpperName(); | |
| } | |
| void writeImplicitCtorArgs(raw_ostream &OS) const override { | |
| OS << "Is" << getUpperName() << "Expr, " << getUpperName(); | |
| } | |
| void writeDeclarations(raw_ostream &OS) const override { | |
| OS << "bool is" << getLowerName() << "Expr;\n"; | |
| OS << "union {\n"; | |
| OS << "Expr *" << getLowerName() << "Expr;\n"; | |
| OS << "TypeSourceInfo *" << getLowerName() << "Type;\n"; | |
| OS << "};\n"; | |
| OS << "std::optional<unsigned> " << getLowerName() << "Cache;\n"; | |
| } | |
| void writePCHReadArgs(raw_ostream &OS) const override { | |
| OS << "is" << getLowerName() << "Expr, " << getLowerName() << "Ptr"; | |
| } | |
| void writePCHReadDecls(raw_ostream &OS) const override { | |
| OS << " bool is" << getLowerName() << "Expr = Record.readInt();\n"; | |
| OS << " void *" << getLowerName() << "Ptr;\n"; | |
| OS << " if (is" << getLowerName() << "Expr)\n"; | |
| OS << " " << getLowerName() << "Ptr = Record.readExpr();\n"; | |
| OS << " else\n"; | |
| OS << " " << getLowerName() | |
| << "Ptr = Record.readTypeSourceInfo();\n"; | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << " Record.push_back(SA->is" << getUpperName() << "Expr());\n"; | |
| OS << " if (SA->is" << getUpperName() << "Expr())\n"; | |
| OS << " Record.AddStmt(SA->get" << getUpperName() << "Expr());\n"; | |
| OS << " else\n"; | |
| OS << " Record.AddTypeSourceInfo(SA->get" << getUpperName() | |
| << "Type());\n"; | |
| } | |
| std::string getIsOmitted() const override { | |
| return "!((is" + getLowerName().str() + "Expr && " + | |
| getLowerName().str() + "Expr) || (!is" + getLowerName().str() + | |
| "Expr && " + getLowerName().str() + "Type))"; | |
| } | |
| void writeValue(raw_ostream &OS) const override { | |
| OS << "\";\n"; | |
| OS << " if (is" << getLowerName() << "Expr && " << getLowerName() | |
| << "Expr)"; | |
| OS << " " << getLowerName() | |
| << "Expr->printPretty(OS, nullptr, Policy);\n"; | |
| OS << " if (!is" << getLowerName() << "Expr && " << getLowerName() | |
| << "Type)"; | |
| OS << " " << getLowerName() | |
| << "Type->getType().print(OS, Policy);\n"; | |
| OS << " OS << \""; | |
| } | |
| void writeDump(raw_ostream &OS) const override { | |
| OS << " if (!SA->is" << getUpperName() << "Expr())\n"; | |
| OS << " dumpType(SA->get" << getUpperName() | |
| << "Type()->getType());\n"; | |
| } | |
| void writeDumpChildren(raw_ostream &OS) const override { | |
| OS << " if (SA->is" << getUpperName() << "Expr())\n"; | |
| OS << " Visit(SA->get" << getUpperName() << "Expr());\n"; | |
| } | |
| void writeHasChildren(raw_ostream &OS) const override { | |
| OS << "SA->is" << getUpperName() << "Expr()"; | |
| } | |
| std::string emitAttrArgEqualityCheck() const override { | |
| auto GetStr = [&](bool Other) { | |
| std::string CtxStr = Other ? "Context.ToCtx" : "Context.FromCtx"; | |
| std::string S = std::string("get") + std::string(getUpperName()) + "(" + | |
| CtxStr + ")"; | |
| return S; | |
| }; | |
| return getArgEqualityFn().str() + "(" + GetStr(false) + ", Other." + | |
| GetStr(true) + ", Context)"; | |
| } | |
| }; | |
| class VariadicArgument : public Argument { | |
| std::string Type, ArgName, ArgSizeName, RangeName; | |
| protected: | |
| // Assumed to receive a parameter: raw_ostream OS. | |
| virtual void writeValueImpl(raw_ostream &OS) const { | |
| OS << " OS << Val;\n"; | |
| } | |
| // Assumed to receive a parameter: raw_ostream OS. | |
| virtual void writeDumpImpl(raw_ostream &OS) const { | |
| OS << " OS << \" \" << Val;\n"; | |
| } | |
| public: | |
| VariadicArgument(const Record &Arg, StringRef Attr, std::string T) | |
| : Argument(Arg, Attr), Type(std::move(T)), | |
| ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"), | |
| RangeName(getLowerName().str()) {} | |
| VariadicArgument(StringRef Arg, StringRef Attr, std::string T) | |
| : Argument(Arg, Attr), Type(std::move(T)), | |
| ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"), | |
| RangeName(getLowerName().str()) {} | |
| const std::string &getType() const { return Type; } | |
| const std::string &getArgName() const { return ArgName; } | |
| const std::string &getArgSizeName() const { return ArgSizeName; } | |
| bool isVariadic() const override { return true; } | |
| void writeAccessors(raw_ostream &OS) const override { | |
| std::string IteratorType = getLowerName().str() + "_iterator"; | |
| std::string BeginFn = getLowerName().str() + "_begin()"; | |
| std::string EndFn = getLowerName().str() + "_end()"; | |
| OS << " typedef " << Type << "* " << IteratorType << ";\n"; | |
| OS << " " << IteratorType << " " << BeginFn << " const {" | |
| << " return " << ArgName << "; }\n"; | |
| OS << " " << IteratorType << " " << EndFn << " const {" | |
| << " return " << ArgName << " + " << ArgSizeName << "; }\n"; | |
| OS << " unsigned " << getLowerName() << "_size() const {" | |
| << " return " << ArgSizeName << "; }\n"; | |
| OS << " llvm::iterator_range<" << IteratorType << "> " << RangeName | |
| << "() const { return llvm::make_range(" << BeginFn << ", " << EndFn | |
| << "); }\n"; | |
| } | |
| void writeSetter(raw_ostream &OS) const { | |
| OS << " void set" << getUpperName() << "(ASTContext &Ctx, "; | |
| writeCtorParameters(OS); | |
| OS << ") {\n"; | |
| OS << " " << ArgSizeName << " = " << getUpperName() << "Size;\n"; | |
| OS << " " << ArgName << " = new (Ctx, 16) " << getType() << "[" | |
| << ArgSizeName << "];\n"; | |
| OS << " "; | |
| writeCtorBody(OS); | |
| OS << " }\n"; | |
| } | |
| void writeCloneArgs(raw_ostream &OS) const override { | |
| OS << ArgName << ", " << ArgSizeName; | |
| } | |
| void writeTemplateInstantiationArgs(raw_ostream &OS) const override { | |
| // This isn't elegant, but we have to go through public methods... | |
| OS << "A->" << getLowerName() << "_begin(), " | |
| << "A->" << getLowerName() << "_size()"; | |
| } | |
| void writeASTVisitorTraversal(raw_ostream &OS) const override { | |
| // FIXME: Traverse the elements. | |
| } | |
| void writeCtorBody(raw_ostream &OS) const override { | |
| OS << " std::copy(" << getUpperName() << ", " << getUpperName() << " + " | |
| << ArgSizeName << ", " << ArgName << ");\n"; | |
| } | |
| void writeCtorInitializers(raw_ostream &OS) const override { | |
| OS << ArgSizeName << "(" << getUpperName() << "Size), " | |
| << ArgName << "(new (Ctx, 16) " << getType() << "[" | |
| << ArgSizeName << "])"; | |
| } | |
| void writeCtorDefaultInitializers(raw_ostream &OS) const override { | |
| OS << ArgSizeName << "(0), " << ArgName << "(nullptr)"; | |
| } | |
| void writeCtorParameters(raw_ostream &OS) const override { | |
| OS << getType() << " *" << getUpperName() << ", unsigned " | |
| << getUpperName() << "Size"; | |
| } | |
| void writeImplicitCtorArgs(raw_ostream &OS) const override { | |
| OS << getUpperName() << ", " << getUpperName() << "Size"; | |
| } | |
| void writeDeclarations(raw_ostream &OS) const override { | |
| OS << " unsigned " << ArgSizeName << ";\n"; | |
| OS << " " << getType() << " *" << ArgName << ";"; | |
| } | |
| void writePCHReadDecls(raw_ostream &OS) const override { | |
| OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n"; | |
| OS << " SmallVector<" << getType() << ", 4> " | |
| << getLowerName() << ";\n"; | |
| OS << " " << getLowerName() << ".reserve(" << getLowerName() | |
| << "Size);\n"; | |
| // If we can't store the values in the current type (if it's something | |
| // like StringRef), store them in a different type and convert the | |
| // container afterwards. | |
| std::string StorageType = getStorageType(getType()).str(); | |
| std::string StorageName = getLowerName().str(); | |
| if (StorageType != getType()) { | |
| StorageName += "Storage"; | |
| OS << " SmallVector<" << StorageType << ", 4> " | |
| << StorageName << ";\n"; | |
| OS << " " << StorageName << ".reserve(" << getLowerName() | |
| << "Size);\n"; | |
| } | |
| OS << " for (unsigned i = 0; i != " << getLowerName() << "Size; ++i)\n"; | |
| std::string read = ReadPCHRecord(Type); | |
| OS << " " << StorageName << ".push_back(" << read << ");\n"; | |
| if (StorageType != getType()) { | |
| OS << " for (unsigned i = 0; i != " << getLowerName() << "Size; ++i)\n"; | |
| OS << " " << getLowerName() << ".push_back(" | |
| << StorageName << "[i]);\n"; | |
| } | |
| } | |
| void writePCHReadArgs(raw_ostream &OS) const override { | |
| OS << getLowerName() << ".data(), " << getLowerName() << "Size"; | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << " Record.push_back(SA->" << getLowerName() << "_size());\n"; | |
| OS << " for (auto &Val : SA->" << RangeName << "())\n"; | |
| OS << " " << WritePCHRecord(Type, "Val"); | |
| } | |
| void writeValue(raw_ostream &OS) const override { | |
| OS << "\";\n"; | |
| OS << " for (const auto &Val : " << RangeName << "()) {\n" | |
| << " DelimitAttributeArgument(OS, IsFirstArgument);\n"; | |
| writeValueImpl(OS); | |
| OS << " }\n"; | |
| OS << " OS << \""; | |
| } | |
| void writeDump(raw_ostream &OS) const override { | |
| OS << " for (const auto &Val : SA->" << RangeName << "())\n"; | |
| writeDumpImpl(OS); | |
| } | |
| std::string emitAttrArgEqualityCheck() const override { | |
| auto GenIter = [&](bool IsOther, const std::string &Suffix) { | |
| std::string S = IsOther ? "Other." : ""; | |
| std::string LN = getLowerName().str(); | |
| S += LN + "_" + Suffix + "()"; | |
| return S; | |
| }; | |
| return getArgEqualityFn().str() + "(" + GenIter(false, "begin") + ", " + | |
| GenIter(false, "end") + ", " + GenIter(true, "begin") + ", " + | |
| GenIter(true, "end") + ", Context)"; | |
| } | |
| std::string emitAttrArgProfileCall() const override { | |
| std::string LN = getLowerName().str(); | |
| return "profileAttrArg(ID, Ctx, " + LN + "_begin(), " + LN + "_end())"; | |
| } | |
| }; | |
| class VariadicOMPInteropInfoArgument : public VariadicArgument { | |
| public: | |
| VariadicOMPInteropInfoArgument(const Record &Arg, StringRef Attr) | |
| : VariadicArgument(Arg, Attr, "OMPInteropInfo") {} | |
| void writeDump(raw_ostream &OS) const override { | |
| OS << " for (" << getAttrName() << "Attr::" << getLowerName() | |
| << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->" | |
| << getLowerName() << "_end(); I != E; ++I) {\n"; | |
| OS << " if (I->IsTarget && I->IsTargetSync)\n"; | |
| OS << " OS << \" Target_TargetSync\";\n"; | |
| OS << " else if (I->IsTarget)\n"; | |
| OS << " OS << \" Target\";\n"; | |
| OS << " else\n"; | |
| OS << " OS << \" TargetSync\";\n"; | |
| OS << " }\n"; | |
| } | |
| void writePCHReadDecls(raw_ostream &OS) const override { | |
| OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n"; | |
| OS << " SmallVector<OMPInteropInfo, 4> " << getLowerName() << ";\n"; | |
| OS << " " << getLowerName() << ".reserve(" << getLowerName() | |
| << "Size);\n"; | |
| OS << " for (unsigned I = 0, E = " << getLowerName() << "Size; "; | |
| OS << "I != E; ++I) {\n"; | |
| OS << " bool IsTarget = Record.readBool();\n"; | |
| OS << " bool IsTargetSync = Record.readBool();\n"; | |
| OS << " OMPInteropInfo Info(IsTarget, IsTargetSync);\n"; | |
| OS << " Info.HasPreferAttrs = Record.readBool();\n"; | |
| OS << " unsigned prefsSize = Record.readInt();\n"; | |
| OS << " Info.Prefs.reserve(prefsSize);\n"; | |
| OS << " for (unsigned J = 0; J < prefsSize; ++J) {\n"; | |
| OS << " bool hasFr = Record.readBool();\n"; | |
| OS << " Expr *Fr = hasFr ? Record.readExpr() : nullptr;\n"; | |
| OS << " unsigned attrsSize = Record.readInt();\n"; | |
| OS << " llvm::SmallVector<Expr *, 2> Attrs;\n"; | |
| OS << " Attrs.reserve(attrsSize);\n"; | |
| OS << " for (unsigned K = 0; K < attrsSize; ++K)\n"; | |
| OS << " Attrs.push_back(Record.readExpr());\n"; | |
| OS << " Info.Prefs.emplace_back(Fr, std::move(Attrs));\n"; | |
| OS << " }\n"; | |
| OS << " " << getLowerName() << ".push_back(Info);\n"; | |
| OS << " }\n"; | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << " Record.push_back(SA->" << getLowerName() << "_size());\n"; | |
| OS << " for (" << getAttrName() << "Attr::" << getLowerName() | |
| << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->" | |
| << getLowerName() << "_end(); I != E; ++I) {\n"; | |
| OS << " Record.writeBool(I->IsTarget);\n"; | |
| OS << " Record.writeBool(I->IsTargetSync);\n"; | |
| OS << " Record.writeBool(I->HasPreferAttrs);\n"; | |
| OS << " Record.push_back(I->Prefs.size());\n"; | |
| OS << " for (auto &P : I->Prefs) {\n"; | |
| OS << " Record.writeBool(P.Fr != nullptr);\n"; | |
| OS << " if (P.Fr) Record.AddStmt(P.Fr);\n"; | |
| OS << " Record.push_back(P.Attrs.size());\n"; | |
| OS << " for (Expr *A : P.Attrs) Record.AddStmt(A);\n"; | |
| OS << " }\n"; | |
| OS << " }\n"; | |
| } | |
| void writeASTVisitorTraversal(raw_ostream &OS) const override { | |
| OS << " {\n"; | |
| OS << " OMPInteropInfo *I = A->" << getLowerName() << "_begin();\n"; | |
| OS << " " << getType() << " *E = A->" << getLowerName() << "_end();\n"; | |
| OS << " for (; I != E; ++I) {\n"; | |
| OS << " for (auto &P : I->Prefs) {\n"; | |
| OS << " if (P.Fr && !getDerived().TraverseStmt(P.Fr))\n"; | |
| OS << " return false;\n"; | |
| OS << " for (Expr *A : P.Attrs)\n"; | |
| OS << " if (!getDerived().TraverseStmt(A))\n"; | |
| OS << " return false;\n"; | |
| OS << " }\n"; | |
| OS << " }\n"; | |
| OS << " }\n"; | |
| } | |
| void writeDumpChildren(raw_ostream &OS) const override { | |
| OS << " for (" << getAttrName() << "Attr::" << getLowerName() | |
| << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->" | |
| << getLowerName() << "_end(); I != E; ++I) {\n"; | |
| OS << " for (auto &P : I->Prefs) {\n"; | |
| OS << " if (P.Fr) Visit(P.Fr);\n"; | |
| OS << " for (Expr *A : P.Attrs) Visit(A);\n"; | |
| OS << " }\n"; | |
| OS << " }\n"; | |
| } | |
| }; | |
| class VariadicParamIdxArgument : public VariadicArgument { | |
| public: | |
| VariadicParamIdxArgument(const Record &Arg, StringRef Attr) | |
| : VariadicArgument(Arg, Attr, "ParamIdx") {} | |
| public: | |
| void writeValueImpl(raw_ostream &OS) const override { | |
| OS << " OS << Val.getSourceIndex();\n"; | |
| } | |
| void writeDumpImpl(raw_ostream &OS) const override { | |
| OS << " OS << \" \" << Val.getSourceIndex();\n"; | |
| } | |
| }; | |
| struct VariadicParamOrParamIdxArgument : public VariadicArgument { | |
| VariadicParamOrParamIdxArgument(const Record &Arg, StringRef Attr) | |
| : VariadicArgument(Arg, Attr, "int") {} | |
| }; | |
| // Unique the enums, but maintain the original declaration ordering. | |
| std::vector<StringRef> | |
| uniqueEnumsInOrder(const std::vector<StringRef> &enums) { | |
| std::vector<StringRef> uniques; | |
| SmallDenseSet<StringRef, 8> unique_set; | |
| for (const auto &i : enums) { | |
| if (unique_set.insert(i).second) | |
| uniques.push_back(i); | |
| } | |
| return uniques; | |
| } | |
| class EnumArgument : public Argument { | |
| std::string fullType; | |
| StringRef shortType; | |
| std::vector<StringRef> values, enums, uniques; | |
| bool isExternal; | |
| bool isCovered; | |
| public: | |
| EnumArgument(const Record &Arg, StringRef Attr) | |
| : Argument(Arg, Attr), values(Arg.getValueAsListOfStrings("Values")), | |
| enums(Arg.getValueAsListOfStrings("Enums")), | |
| uniques(uniqueEnumsInOrder(enums)), | |
| isExternal(Arg.getValueAsBit("IsExternalType")), | |
| isCovered(Arg.getValueAsBit("IsCovered")) { | |
| 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()); | |
| } | |
| bool isEnumArg() const override { return true; } | |
| void writeAccessors(raw_ostream &OS) const override { | |
| OS << " " << fullType << " get" << getUpperName() << "() const {\n"; | |
| OS << " return " << getLowerName() << ";\n"; | |
| OS << " }"; | |
| } | |
| void writeCloneArgs(raw_ostream &OS) const override { | |
| OS << getLowerName(); | |
| } | |
| void writeTemplateInstantiationArgs(raw_ostream &OS) const override { | |
| OS << "A->get" << getUpperName() << "()"; | |
| } | |
| void writeCtorInitializers(raw_ostream &OS) const override { | |
| OS << getLowerName() << "(" << getUpperName() << ")"; | |
| } | |
| void writeCtorDefaultInitializers(raw_ostream &OS) const override { | |
| OS << getLowerName() << "(" << fullType << "(0))"; | |
| } | |
| void writeCtorParameters(raw_ostream &OS) const override { | |
| OS << fullType << " " << getUpperName(); | |
| } | |
| void writeDeclarations(raw_ostream &OS) const override { | |
| 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"; | |
| OS << " " << fullType << " " << getLowerName() << ";"; | |
| } | |
| void writePCHReadDecls(raw_ostream &OS) const override { | |
| OS << " " << fullType << " " << getLowerName() << "(static_cast<" | |
| << fullType << ">(Record.readInt()));\n"; | |
| } | |
| void writePCHReadArgs(raw_ostream &OS) const override { | |
| OS << getLowerName(); | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << "Record.push_back(static_cast<uint64_t>(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" << shortType | |
| << "ToStr(get" << getUpperName() << "()) << \"\\\""; | |
| } | |
| void writeDump(raw_ostream &OS) const override { | |
| OS << " switch(SA->get" << getUpperName() << "()) {\n"; | |
| for (const auto &I : uniques) { | |
| OS << " case " << fullType << "::" << I << ":\n"; | |
| OS << " OS << \" " << I << "\";\n"; | |
| OS << " break;\n"; | |
| } | |
| if (!isCovered) { | |
| 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" << shortType << "(StringRef Val, " | |
| << fullType << " &Out);\n"; | |
| OS << " static const char *Convert" << shortType << "ToStr(" | |
| << fullType << " Val);\n"; | |
| return; | |
| } | |
| OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << shortType | |
| << "(StringRef Val, " << fullType << " &Out) {\n"; | |
| OS << " std::optional<" << fullType << "> " | |
| << "R = llvm::StringSwitch<std::optional<" << fullType << ">>(Val)\n"; | |
| for (size_t I = 0; I < enums.size(); ++I) { | |
| OS << " .Case(\"" << values[I] << "\", "; | |
| OS << fullType << "::" << enums[I] << ")\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"; | |
| // Mapping from enumeration values back to enumeration strings isn't | |
| // 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" << shortType | |
| << "ToStr(" << fullType << " Val) {\n" | |
| << " switch(Val) {\n"; | |
| SmallDenseSet<StringRef, 8> Uniques; | |
| for (size_t I = 0; I < enums.size(); ++I) { | |
| if (Uniques.insert(enums[I]).second) | |
| OS << " case " << fullType << "::" << enums[I] << ": return \"" | |
| << values[I] << "\";\n"; | |
| } | |
| if (!isCovered) { | |
| OS << " default: llvm_unreachable(\"Invalid attribute value\");\n"; | |
| } | |
| OS << " }\n" | |
| << " llvm_unreachable(\"No enumerator with that value\");\n" | |
| << "}\n"; | |
| } | |
| }; | |
| class VariadicEnumArgument: public VariadicArgument { | |
| std::string fullType; | |
| StringRef shortType; | |
| std::vector<StringRef> values, enums, uniques; | |
| bool isExternal; | |
| bool isCovered; | |
| 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" | |
| << shortType << "ToStr(Val)" | |
| << "<< \"\\\"\";\n"; | |
| } | |
| public: | |
| VariadicEnumArgument(const Record &Arg, StringRef Attr) | |
| : VariadicArgument(Arg, Attr, Arg.getValueAsString("Type").str()), | |
| values(Arg.getValueAsListOfStrings("Values")), | |
| enums(Arg.getValueAsListOfStrings("Enums")), | |
| uniques(uniqueEnumsInOrder(enums)), | |
| isExternal(Arg.getValueAsBit("IsExternalType")), | |
| isCovered(Arg.getValueAsBit("IsCovered")) { | |
| 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()); | |
| } | |
| bool isVariadicEnumArg() const override { return true; } | |
| void writeDeclarations(raw_ostream &OS) const override { | |
| 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); | |
| } | |
| void writeDump(raw_ostream &OS) const override { | |
| OS << " for (" << getAttrName() << "Attr::" << getLowerName() | |
| << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->" | |
| << getLowerName() << "_end(); I != E; ++I) {\n"; | |
| OS << " switch(*I) {\n"; | |
| for (const auto &UI : uniques) { | |
| OS << " case " << fullType << "::" << UI << ":\n"; | |
| OS << " OS << \" " << UI << "\";\n"; | |
| OS << " break;\n"; | |
| } | |
| if (!isCovered) { | |
| OS << " default:\n"; | |
| OS << " llvm_unreachable(\"Invalid attribute value\");\n"; | |
| } | |
| OS << " }\n"; | |
| OS << " }\n"; | |
| } | |
| void writePCHReadDecls(raw_ostream &OS) const override { | |
| OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n"; | |
| 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<" << fullType << ">(Record.readInt()));\n"; | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << " Record.push_back(SA->" << getLowerName() << "_size());\n"; | |
| OS << " for (" << getAttrName() << "Attr::" << getLowerName() | |
| << "_iterator i = SA->" << getLowerName() << "_begin(), e = SA->" | |
| << getLowerName() << "_end(); i != e; ++i)\n"; | |
| OS << " " << WritePCHRecord(fullType, "(*i)"); | |
| } | |
| void writeConversion(raw_ostream &OS, bool Header) const { | |
| if (Header) { | |
| 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" << shortType | |
| << "(StringRef Val, "; | |
| OS << fullType << " &Out) {\n"; | |
| OS << " std::optional<" << fullType | |
| << "> R = llvm::StringSwitch<std::optional<"; | |
| OS << fullType << ">>(Val)\n"; | |
| for (size_t I = 0; I < enums.size(); ++I) { | |
| OS << " .Case(\"" << values[I] << "\", "; | |
| OS << fullType << "::" << enums[I] << ")\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" << shortType | |
| << "ToStr(" << fullType << " Val) {\n" | |
| << " switch(Val) {\n"; | |
| SmallDenseSet<StringRef, 8> Uniques; | |
| for (size_t I = 0; I < enums.size(); ++I) { | |
| if (Uniques.insert(enums[I]).second) | |
| OS << " case " << fullType << "::" << enums[I] << ": return \"" | |
| << values[I] << "\";\n"; | |
| } | |
| if (!isCovered) { | |
| OS << " default: llvm_unreachable(\"Invalid attribute value\");\n"; | |
| } | |
| OS << " }\n" | |
| << " llvm_unreachable(\"No enumerator with that value\");\n" | |
| << "}\n"; | |
| } | |
| }; | |
| class VersionArgument : public Argument { | |
| public: | |
| VersionArgument(const Record &Arg, StringRef Attr) | |
| : Argument(Arg, Attr) | |
| {} | |
| void writeAccessors(raw_ostream &OS) const override { | |
| OS << " VersionTuple get" << getUpperName() << "() const {\n"; | |
| OS << " return " << getLowerName() << ";\n"; | |
| OS << " }\n"; | |
| OS << " void set" << getUpperName() | |
| << "(ASTContext &C, VersionTuple V) {\n"; | |
| OS << " " << getLowerName() << " = V;\n"; | |
| OS << " }"; | |
| } | |
| void writeCloneArgs(raw_ostream &OS) const override { | |
| OS << "get" << getUpperName() << "()"; | |
| } | |
| void writeTemplateInstantiationArgs(raw_ostream &OS) const override { | |
| OS << "A->get" << getUpperName() << "()"; | |
| } | |
| void writeCtorInitializers(raw_ostream &OS) const override { | |
| OS << getLowerName() << "(" << getUpperName() << ")"; | |
| } | |
| void writeCtorDefaultInitializers(raw_ostream &OS) const override { | |
| OS << getLowerName() << "()"; | |
| } | |
| void writeCtorParameters(raw_ostream &OS) const override { | |
| OS << "VersionTuple " << getUpperName(); | |
| } | |
| void writeDeclarations(raw_ostream &OS) const override { | |
| OS << "VersionTuple " << getLowerName() << ";\n"; | |
| } | |
| void writePCHReadDecls(raw_ostream &OS) const override { | |
| OS << " VersionTuple " << getLowerName() | |
| << "= Record.readVersionTuple();\n"; | |
| } | |
| void writePCHReadArgs(raw_ostream &OS) const override { | |
| OS << getLowerName(); | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << " Record.AddVersionTuple(SA->get" << getUpperName() << "());\n"; | |
| } | |
| void writeValue(raw_ostream &OS) const override { | |
| OS << getLowerName() << "=\" << get" << getUpperName() << "() << \""; | |
| } | |
| void writeDump(raw_ostream &OS) const override { | |
| OS << " OS << \" \" << SA->get" << getUpperName() << "();\n"; | |
| } | |
| }; | |
| class ExprArgument : public SimpleArgument { | |
| public: | |
| ExprArgument(const Record &Arg, StringRef Attr) | |
| : SimpleArgument(Arg, Attr, "Expr *") | |
| {} | |
| void writeASTVisitorTraversal(raw_ostream &OS) const override { | |
| OS << " if (!" | |
| << "getDerived().TraverseStmt(A->get" << getUpperName() << "()))\n"; | |
| OS << " return false;\n"; | |
| } | |
| void writeTemplateInstantiationArgs(raw_ostream &OS) const override { | |
| OS << "tempInst" << getUpperName(); | |
| } | |
| void writeTemplateInstantiation(raw_ostream &OS) const override { | |
| OS << " " << getType() << " tempInst" << getUpperName() << ";\n"; | |
| OS << " {\n"; | |
| OS << " EnterExpressionEvaluationContext " | |
| << "Unevaluated(S, Sema::ExpressionEvaluationContext::Unevaluated);\n"; | |
| OS << " ExprResult " << "Result = S.SubstExpr(" | |
| << "A->get" << getUpperName() << "(), TemplateArgs);\n"; | |
| OS << " if (Result.isInvalid())\n"; | |
| OS << " return nullptr;\n"; | |
| OS << " tempInst" << getUpperName() << " = Result.get();\n"; | |
| OS << " }\n"; | |
| } | |
| void writeValue(raw_ostream &OS) const override { | |
| OS << "\";\n"; | |
| OS << " get" << getUpperName() | |
| << "()->printPretty(OS, nullptr, Policy);\n"; | |
| OS << " OS << \""; | |
| } | |
| void writeDump(raw_ostream &OS) const override {} | |
| void writeDumpChildren(raw_ostream &OS) const override { | |
| OS << " Visit(SA->get" << getUpperName() << "());\n"; | |
| } | |
| void writeHasChildren(raw_ostream &OS) const override { OS << "true"; } | |
| }; | |
| class VariadicExprArgument : public VariadicArgument { | |
| public: | |
| VariadicExprArgument(const Record &Arg, StringRef Attr) | |
| : VariadicArgument(Arg, Attr, "Expr *") | |
| {} | |
| VariadicExprArgument(StringRef ArgName, StringRef Attr) | |
| : VariadicArgument(ArgName, Attr, "Expr *") {} | |
| void writeASTVisitorTraversal(raw_ostream &OS) const override { | |
| OS << " {\n"; | |
| OS << " " << getType() << " *I = A->" << getLowerName() | |
| << "_begin();\n"; | |
| OS << " " << getType() << " *E = A->" << getLowerName() | |
| << "_end();\n"; | |
| OS << " for (; I != E; ++I) {\n"; | |
| OS << " if (!getDerived().TraverseStmt(*I))\n"; | |
| OS << " return false;\n"; | |
| OS << " }\n"; | |
| OS << " }\n"; | |
| } | |
| void writeTemplateInstantiationArgs(raw_ostream &OS) const override { | |
| OS << "tempInst" << getUpperName() << ", " | |
| << "numTempInst" << getUpperName(); | |
| } | |
| void writeTemplateInstantiation(raw_ostream &OS) const override { | |
| OS << " size_t numTempInst" << getUpperName() << ";\n"; | |
| OS << " " << getType() << "*tempInst" << getUpperName() << ";\n"; | |
| OS << " {\n"; | |
| OS << " EnterExpressionEvaluationContext " | |
| << "Unevaluated(S, Sema::ExpressionEvaluationContext::Unevaluated);\n"; | |
| OS << " ArrayRef<" << getType() << "> ArgsToInstantiate(A->" | |
| << getLowerName() << "_begin(), A->" << getLowerName() << "_end());\n"; | |
| OS << " SmallVector<" << getType() << ", 4> InstArgs;\n"; | |
| OS << " if (S.SubstExprs(ArgsToInstantiate, /*IsCall=*/false, " | |
| "TemplateArgs, InstArgs))\n"; | |
| OS << " return nullptr;\n"; | |
| OS << " numTempInst" << getUpperName() << " = InstArgs.size();\n"; | |
| OS << " tempInst" << getUpperName() << " = new (C, 16) " | |
| << getType() << "[numTempInst" << getUpperName() << "];\n"; | |
| OS << " std::copy(InstArgs.begin(), InstArgs.end(), tempInst" | |
| << getUpperName() << ");\n"; | |
| OS << " }\n"; | |
| } | |
| void writeDump(raw_ostream &OS) const override {} | |
| void writeDumpChildren(raw_ostream &OS) const override { | |
| OS << " for (" << getAttrName() << "Attr::" << getLowerName() | |
| << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->" | |
| << getLowerName() << "_end(); I != E; ++I)\n"; | |
| OS << " Visit(*I);\n"; | |
| } | |
| void writeHasChildren(raw_ostream &OS) const override { | |
| OS << "SA->" << getLowerName() << "_begin() != " | |
| << "SA->" << getLowerName() << "_end()"; | |
| } | |
| }; | |
| class VariadicIdentifierArgument : public VariadicArgument { | |
| public: | |
| VariadicIdentifierArgument(const Record &Arg, StringRef Attr) | |
| : VariadicArgument(Arg, Attr, "const IdentifierInfo *") {} | |
| }; | |
| class VariadicStringArgument : public VariadicArgument { | |
| public: | |
| VariadicStringArgument(const Record &Arg, StringRef Attr) | |
| : VariadicArgument(Arg, Attr, "StringRef") | |
| {} | |
| void writeCtorBody(raw_ostream &OS) const override { | |
| OS << " for (size_t I = 0, E = " << getArgSizeName() << "; I != E;\n" | |
| " ++I) {\n" | |
| " StringRef Ref = " << getUpperName() << "[I];\n" | |
| " if (!Ref.empty()) {\n" | |
| " char *Mem = new (Ctx, 1) char[Ref.size()];\n" | |
| " std::memcpy(Mem, Ref.data(), Ref.size());\n" | |
| " " << getArgName() << "[I] = StringRef(Mem, Ref.size());\n" | |
| " }\n" | |
| " }\n"; | |
| } | |
| void writeValueImpl(raw_ostream &OS) const override { | |
| OS << " OS << \"\\\"\" << Val << \"\\\"\";\n"; | |
| } | |
| }; | |
| class TypeArgument : public SimpleArgument { | |
| public: | |
| TypeArgument(const Record &Arg, StringRef Attr) | |
| : SimpleArgument(Arg, Attr, "TypeSourceInfo *") | |
| {} | |
| void writeAccessors(raw_ostream &OS) const override { | |
| OS << " QualType get" << getUpperName() << "() const {\n"; | |
| OS << " return " << getLowerName() << "->getType();\n"; | |
| OS << " }"; | |
| OS << " " << getType() << " get" << getUpperName() << "Loc() const {\n"; | |
| OS << " return " << getLowerName() << ";\n"; | |
| OS << " }"; | |
| } | |
| void writeASTVisitorTraversal(raw_ostream &OS) const override { | |
| OS << " if (auto *TSI = A->get" << getUpperName() << "Loc())\n"; | |
| OS << " if (!getDerived().TraverseTypeLoc(TSI->getTypeLoc()))\n"; | |
| OS << " return false;\n"; | |
| } | |
| void writeTemplateInstantiation(raw_ostream &OS) const override { | |
| OS << " " << getType() << " tempInst" << getUpperName() << " =\n"; | |
| OS << " S.SubstType(A->get" << getUpperName() << "Loc(), " | |
| << "TemplateArgs, A->getLoc(), A->getAttrName());\n"; | |
| OS << " if (!tempInst" << getUpperName() << ")\n"; | |
| OS << " return nullptr;\n"; | |
| } | |
| void writeTemplateInstantiationArgs(raw_ostream &OS) const override { | |
| OS << "tempInst" << getUpperName(); | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << " " | |
| << WritePCHRecord(getType(), | |
| "SA->get" + getUpperName().str() + "Loc()"); | |
| } | |
| }; | |
| class WrappedAttr : public SimpleArgument { | |
| std::string AttrType; // C++ class name for the wrapped attr | |
| public: | |
| WrappedAttr(const Record &Arg, StringRef Attr) | |
| : SimpleArgument(Arg, Attr, "Attr *"), | |
| AttrType(Arg.getValueAsString("AttrType")) {} | |
| void writeAccessors(raw_ostream &OS) const override { | |
| // The field is always stored as Attr * regardless of AttrType. This is | |
| // required because the generated isEquivalent method calls | |
| // equalAttrArgs(getInferredAttr(), Other.getInferredAttr(), Context). | |
| // If the field were AttrType * (e.g. AvailabilityAttr *), that call | |
| // would instantiate equalAttrArgs<AvailabilityAttr *>, which has no | |
| // specialization and returns false. Storing Attr * routes the call | |
| // through equalAttrArgs<Attr *>, which handles null and calls | |
| // isEquivalent. Typed get<Name>As() / set<Name>As() accessors are | |
| // provided for callers that need the specific type. | |
| OS << " Attr *get" << getUpperName() << "() const {\n"; | |
| OS << " return " << getLowerName() << ";\n"; | |
| OS << " }\n"; | |
| OS << " void set" << getUpperName() << "(Attr *V) {\n"; | |
| OS << " " << getLowerName() << " = V;\n"; | |
| OS << " }"; | |
| if (!AttrType.empty()) { | |
| OS << "\n"; | |
| OS << " " << AttrType << " *get" << getUpperName() << "As() const {\n"; | |
| OS << " return llvm::cast_or_null<" << AttrType << ">(" | |
| << getLowerName() << ");\n"; | |
| OS << " }\n"; | |
| OS << " void set" << getUpperName() << "As(" << AttrType << " *V) {\n"; | |
| OS << " " << getLowerName() << " = V;\n"; | |
| OS << " }"; | |
| } | |
| } | |
| void writePCHReadDecls(raw_ostream &OS) const override { | |
| OS << " Attr *" << getLowerName() << " = Record.readAttr();"; | |
| } | |
| void writePCHWrite(raw_ostream &OS) const override { | |
| OS << " AddAttr(SA->get" << getUpperName() << "());"; | |
| } | |
| std::string getIsOmitted() const override { | |
| if (isOptional()) | |
| return "!get" + getUpperName().str() + "()"; | |
| return "false"; | |
| } | |
| void writeValue(raw_ostream &OS) const override {} | |
| void writeDump(raw_ostream &OS) const override {} | |
| void writeDumpChildren(raw_ostream &OS) const override { | |
| if (isOptional()) { | |
| OS << " if (auto *W = SA->get" << getUpperName() << "())\n"; | |
| OS << " Visit(W);\n"; | |
| } else { | |
| OS << " Visit(SA->get" << getUpperName() << "());\n"; | |
| } | |
| } | |
| void writeHasChildren(raw_ostream &OS) const override { | |
| if (isOptional()) | |
| OS << "SA->get" << getUpperName() << "() != nullptr"; | |
| else | |
| OS << "true"; | |
| } | |
| }; | |
| } // end anonymous namespace | |
| static std::unique_ptr<Argument> | |
| createArgument(const Record &Arg, StringRef Attr, | |
| const Record *Search = nullptr) { | |
| if (!Search) | |
| Search = &Arg; | |
| std::unique_ptr<Argument> Ptr; | |
| StringRef ArgName = Search->getName(); | |
| if (ArgName == "AlignedArgument") | |
| Ptr = std::make_unique<AlignedArgument>(Arg, Attr); | |
| else if (ArgName == "EnumArgument") | |
| Ptr = std::make_unique<EnumArgument>(Arg, Attr); | |
| else if (ArgName == "ExprArgument") | |
| Ptr = std::make_unique<ExprArgument>(Arg, Attr); | |
| else if (ArgName == "DeclArgument") | |
| Ptr = std::make_unique<SimpleArgument>( | |
| Arg, Attr, (Arg.getValueAsDef("Kind")->getName() + "Decl *").str()); | |
| else if (ArgName == "IdentifierArgument") | |
| Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "const IdentifierInfo *"); | |
| else if (ArgName == "DefaultBoolArgument") | |
| Ptr = std::make_unique<DefaultSimpleArgument>( | |
| Arg, Attr, "bool", Arg.getValueAsBit("Default")); | |
| else if (ArgName == "BoolArgument") | |
| Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "bool"); | |
| else if (ArgName == "DefaultIntArgument") | |
| Ptr = std::make_unique<DefaultSimpleArgument>( | |
| Arg, Attr, "int", Arg.getValueAsInt("Default")); | |
| else if (ArgName == "IntArgument") | |
| Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "int"); | |
| else if (ArgName == "StringArgument") | |
| Ptr = std::make_unique<StringArgument>(Arg, Attr); | |
| else if (ArgName == "TypeArgument") | |
| Ptr = std::make_unique<TypeArgument>(Arg, Attr); | |
| else if (ArgName == "UnsignedArgument") | |
| Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "unsigned"); | |
| else if (ArgName == "VariadicUnsignedArgument") | |
| Ptr = std::make_unique<VariadicArgument>(Arg, Attr, "unsigned"); | |
| else if (ArgName == "VariadicStringArgument") | |
| Ptr = std::make_unique<VariadicStringArgument>(Arg, Attr); | |
| else if (ArgName == "VariadicEnumArgument") | |
| Ptr = std::make_unique<VariadicEnumArgument>(Arg, Attr); | |
| else if (ArgName == "VariadicExprArgument") | |
| Ptr = std::make_unique<VariadicExprArgument>(Arg, Attr); | |
| else if (ArgName == "VariadicParamIdxArgument") | |
| Ptr = std::make_unique<VariadicParamIdxArgument>(Arg, Attr); | |
| else if (ArgName == "VariadicParamOrParamIdxArgument") | |
| Ptr = std::make_unique<VariadicParamOrParamIdxArgument>(Arg, Attr); | |
| else if (ArgName == "ParamIdxArgument") | |
| Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "ParamIdx"); | |
| else if (ArgName == "VariadicIdentifierArgument") | |
| Ptr = std::make_unique<VariadicIdentifierArgument>(Arg, Attr); | |
| else if (ArgName == "VersionArgument") | |
| Ptr = std::make_unique<VersionArgument>(Arg, Attr); | |
| else if (ArgName == "WrappedAttr") | |
| Ptr = std::make_unique<WrappedAttr>(Arg, Attr); | |
| else if (ArgName == "OMPTraitInfoArgument") | |
| Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "OMPTraitInfo *"); | |
| else if (ArgName == "VariadicOMPInteropInfoArgument") | |
| Ptr = std::make_unique<VariadicOMPInteropInfoArgument>(Arg, Attr); | |
| if (!Ptr) { | |
| // Search in reverse order so that the most-derived type is handled first. | |
| std::vector<const Record *> SCs = Search->getSuperClasses(); | |
| for (const Record *Base : reverse(SCs)) { | |
| if ((Ptr = createArgument(Arg, Attr, Base))) | |
| break; | |
| } | |
| } | |
| if (Ptr && Arg.getValueAsBit("Optional")) | |
| Ptr->setOptional(true); | |
| if (Ptr && Arg.getValueAsBit("Fake")) | |
| Ptr->setFake(true); | |
| return Ptr; | |
| } | |
| static void writeAvailabilityValue(raw_ostream &OS) { | |
| OS << "\" << getPlatform()->getName();\n" | |
| << " if (getStrict()) OS << \", strict\";\n" | |
| << " if (!getIntroduced().empty()) OS << \", introduced=\" << getIntroduced();\n" | |
| << " if (!getDeprecated().empty()) OS << \", deprecated=\" << getDeprecated();\n" | |
| << " if (!getObsoleted().empty()) OS << \", obsoleted=\" << getObsoleted();\n" | |
| << " if (getUnavailable()) OS << \", unavailable\";\n" | |
| << " OS << \""; | |
| } | |
| static void writeDeprecatedAttrValue(raw_ostream &OS, StringRef Variety) { | |
| OS << "\\\"\" << getMessage() << \"\\\"\";\n"; | |
| // Only GNU deprecated has an optional fixit argument at the second position. | |
| if (Variety == "GNU") | |
| OS << " if (!getReplacement().empty()) OS << \", \\\"\"" | |
| " << getReplacement() << \"\\\"\";\n"; | |
| OS << " OS << \""; | |
| } | |
| static void writeGetSpellingFunction(const Record &R, raw_ostream &OS) { | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); | |
| OS << "const char *" << R.getName() << "Attr::getSpelling() const {\n"; | |
| if (Spellings.empty()) { | |
| OS << " return \"(No spelling)\";\n}\n\n"; | |
| return; | |
| } | |
| OS << " switch (getAttributeSpellingListIndex()) {\n" | |
| " default:\n" | |
| " llvm_unreachable(\"Unknown attribute spelling!\");\n" | |
| " return \"(No spelling)\";\n"; | |
| for (const auto &[Idx, S] : enumerate(Spellings)) { | |
| // clang-format off | |
| OS << " case " << Idx << ":\n" | |
| " return \"" << S.name() << "\";\n"; | |
| // clang-format on | |
| } | |
| // End of the switch statement. | |
| OS << " }\n"; | |
| // End of the getSpelling function. | |
| OS << "}\n\n"; | |
| } | |
| static void | |
| writePrettyPrintFunction(const Record &R, | |
| const std::vector<std::unique_ptr<Argument>> &Args, | |
| raw_ostream &OS) { | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); | |
| OS << "void " << R.getName() << "Attr::printPretty(" | |
| << "raw_ostream &OS, const PrintingPolicy &Policy) const {\n"; | |
| if (Spellings.empty()) { | |
| OS << "}\n\n"; | |
| return; | |
| } | |
| OS << " bool IsFirstArgument = true; (void)IsFirstArgument;\n" | |
| << " unsigned TrailingOmittedArgs = 0; (void)TrailingOmittedArgs;\n" | |
| << " switch (getAttributeSpellingListIndex()) {\n" | |
| << " default:\n" | |
| << " llvm_unreachable(\"Unknown attribute spelling!\");\n" | |
| << " break;\n"; | |
| for (const auto &[Idx, S] : enumerate(Spellings)) { | |
| SmallString<16> Prefix; | |
| SmallString<8> Suffix; | |
| // The actual spelling of the name and namespace (if applicable) | |
| // of an attribute without considering prefix and suffix. | |
| SmallString<64> Spelling; | |
| StringRef Name = S.name(); | |
| StringRef Variety = S.variety(); | |
| if (Variety == "GNU") { | |
| Prefix = "__attribute__(("; | |
| Suffix = "))"; | |
| } else if (Variety == "CXX11" || Variety == "C23") { | |
| Prefix = "[["; | |
| Suffix = "]]"; | |
| StringRef Namespace = S.nameSpace(); | |
| if (!Namespace.empty()) { | |
| Spelling += Namespace; | |
| Spelling += "::"; | |
| } | |
| } else if (Variety == "Declspec") { | |
| Prefix = "__declspec("; | |
| Suffix = ")"; | |
| } else if (Variety == "Microsoft") { | |
| Prefix = "["; | |
| Suffix = "]"; | |
| } else if (Variety == "Keyword") { | |
| Prefix = ""; | |
| Suffix = ""; | |
| } else if (Variety == "Pragma") { | |
| Prefix = "#pragma "; | |
| Suffix = "\n"; | |
| StringRef Namespace = S.nameSpace(); | |
| if (!Namespace.empty()) { | |
| Spelling += Namespace; | |
| Spelling += " "; | |
| } | |
| } else if (Variety == "HLSLAnnotation") { | |
| Prefix = ":"; | |
| Suffix = ""; | |
| } else { | |
| llvm_unreachable("Unknown attribute syntax variety!"); | |
| } | |
| Spelling += Name; | |
| OS << " case " << Idx << " : {\n" | |
| << " OS << \"" << Prefix << Spelling << "\";\n"; | |
| if (Variety == "Pragma") { | |
| OS << " printPrettyPragma(OS, Policy);\n"; | |
| OS << " OS << \"\\n\";"; | |
| OS << " break;\n"; | |
| OS << " }\n"; | |
| continue; | |
| } | |
| if (Spelling == "availability") { | |
| OS << " OS << \"("; | |
| writeAvailabilityValue(OS); | |
| OS << ")\";\n"; | |
| } else if (Spelling == "deprecated" || Spelling == "gnu::deprecated") { | |
| OS << " OS << \"("; | |
| writeDeprecatedAttrValue(OS, Variety); | |
| OS << ")\";\n"; | |
| } else { | |
| // To avoid printing parentheses around an empty argument list or | |
| // printing spurious commas at the end of an argument list, we need to | |
| // determine where the last provided non-fake argument is. | |
| bool FoundNonOptArg = false; | |
| for (const auto &arg : reverse(Args)) { | |
| if (arg->isFake()) | |
| continue; | |
| if (FoundNonOptArg) | |
| continue; | |
| // FIXME: arg->getIsOmitted() == "false" means we haven't implemented | |
| // any way to detect whether the argument was omitted. | |
| if (!arg->isOptional() || arg->getIsOmitted() == "false") { | |
| FoundNonOptArg = true; | |
| continue; | |
| } | |
| OS << " if (" << arg->getIsOmitted() << ")\n" | |
| << " ++TrailingOmittedArgs;\n"; | |
| } | |
| unsigned ArgIndex = 0; | |
| for (const auto &arg : Args) { | |
| if (arg->isFake()) | |
| continue; | |
| std::string IsOmitted = arg->getIsOmitted(); | |
| if (arg->isOptional() && IsOmitted != "false") | |
| OS << " if (!(" << IsOmitted << ")) {\n"; | |
| // Variadic arguments print their own leading comma. | |
| if (!arg->isVariadic()) | |
| OS << " DelimitAttributeArgument(OS, IsFirstArgument);\n"; | |
| OS << " OS << \""; | |
| arg->writeValue(OS); | |
| OS << "\";\n"; | |
| if (arg->isOptional() && IsOmitted != "false") | |
| OS << " }\n"; | |
| ++ArgIndex; | |
| } | |
| if (ArgIndex != 0) | |
| OS << " if (!IsFirstArgument)\n" | |
| << " OS << \")\";\n"; | |
| } | |
| OS << " OS << \"" << Suffix << "\";\n" | |
| << " break;\n" | |
| << " }\n"; | |
| } | |
| // End of the switch statement. | |
| OS << "}\n"; | |
| // End of the print function. | |
| OS << "}\n\n"; | |
| } | |
| /// Return the index of a spelling in a spelling list. | |
| static unsigned getSpellingListIndex(ArrayRef<FlattenedSpelling> SpellingList, | |
| const FlattenedSpelling &Spelling) { | |
| assert(!SpellingList.empty() && "Spelling list is empty!"); | |
| for (const auto &[Index, S] : enumerate(SpellingList)) { | |
| if (S.variety() == Spelling.variety() && | |
| S.nameSpace() == Spelling.nameSpace() && S.name() == Spelling.name()) | |
| return Index; | |
| } | |
| PrintFatalError("Unknown spelling: " + Spelling.name()); | |
| } | |
| static void writeAttrAccessorDefinition(const Record &R, raw_ostream &OS) { | |
| std::vector<const Record *> Accessors = R.getValueAsListOfDefs("Accessors"); | |
| if (Accessors.empty()) | |
| return; | |
| const std::vector<FlattenedSpelling> SpellingList = GetFlattenedSpellings(R); | |
| assert(!SpellingList.empty() && | |
| "Attribute with empty spelling list can't have accessors!"); | |
| for (const auto *Accessor : Accessors) { | |
| const StringRef Name = Accessor->getValueAsString("Name"); | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Accessor); | |
| OS << " bool " << Name | |
| << "() const { return getAttributeSpellingListIndex() == "; | |
| for (unsigned Index = 0; Index < Spellings.size(); ++Index) { | |
| OS << getSpellingListIndex(SpellingList, Spellings[Index]); | |
| if (Index != Spellings.size() - 1) | |
| OS << " ||\n getAttributeSpellingListIndex() == "; | |
| else | |
| OS << "; }\n"; | |
| } | |
| } | |
| } | |
| static bool | |
| SpellingNamesAreCommon(const std::vector<FlattenedSpelling>& Spellings) { | |
| assert(!Spellings.empty() && "An empty list of spellings was provided"); | |
| StringRef FirstName = | |
| NormalizeNameForSpellingComparison(Spellings.front().name()); | |
| for (const auto &Spelling : drop_begin(Spellings)) { | |
| StringRef Name = NormalizeNameForSpellingComparison(Spelling.name()); | |
| if (Name != FirstName) | |
| return false; | |
| } | |
| return true; | |
| } | |
| typedef std::map<unsigned, std::string> SemanticSpellingMap; | |
| static std::string | |
| CreateSemanticSpellings(const std::vector<FlattenedSpelling> &Spellings, | |
| SemanticSpellingMap &Map) { | |
| // The enumerants are automatically generated based on the variety, | |
| // namespace (if present) and name for each attribute spelling. However, | |
| // care is taken to avoid trampling on the reserved namespace due to | |
| // underscores. | |
| std::string Ret(" enum Spelling {\n"); | |
| std::set<std::string> Uniques; | |
| unsigned Idx = 0; | |
| // If we have a need to have this many spellings we likely need to add an | |
| // extra bit to the SpellingIndex in AttributeCommonInfo, then increase the | |
| // value of SpellingNotCalculated there and here. | |
| assert(Spellings.size() < 15 && | |
| "Too many spellings, would step on SpellingNotCalculated in " | |
| "AttributeCommonInfo"); | |
| for (auto I = Spellings.begin(), E = Spellings.end(); I != E; ++I, ++Idx) { | |
| const FlattenedSpelling &S = *I; | |
| StringRef Variety = S.variety(); | |
| StringRef Spelling = S.name(); | |
| StringRef Namespace = S.nameSpace(); | |
| std::string EnumName; | |
| EnumName += Variety; | |
| EnumName += "_"; | |
| if (!Namespace.empty()) | |
| EnumName += NormalizeNameForSpellingComparison(Namespace).str() + "_"; | |
| EnumName += NormalizeNameForSpellingComparison(Spelling); | |
| // Even if the name is not unique, this spelling index corresponds to a | |
| // particular enumerant name that we've calculated. | |
| Map[Idx] = EnumName; | |
| // Since we have been stripping underscores to avoid trampling on the | |
| // reserved namespace, we may have inadvertently created duplicate | |
| // enumerant names. These duplicates are not considered part of the | |
| // semantic spelling, and can be elided. | |
| if (!Uniques.insert(EnumName).second) | |
| continue; | |
| if (I != Spellings.begin()) | |
| Ret += ",\n"; | |
| // Duplicate spellings are not considered part of the semantic spelling | |
| // enumeration, but the spelling index and semantic spelling values are | |
| // meant to be equivalent, so we must specify a concrete value for each | |
| // enumerator. | |
| Ret += " " + EnumName + " = " + utostr(Idx); | |
| } | |
| Ret += ",\n SpellingNotCalculated = 15\n"; | |
| Ret += "\n };\n\n"; | |
| return Ret; | |
| } | |
| static void WriteSemanticSpellingSwitch(StringRef VarName, | |
| const SemanticSpellingMap &Map, | |
| raw_ostream &OS) { | |
| OS << " switch (" << VarName << ") {\n default: " | |
| << "llvm_unreachable(\"Unknown spelling list index\");\n"; | |
| for (const auto &I : Map) | |
| OS << " case " << I.first << ": return " << I.second << ";\n"; | |
| OS << " }\n"; | |
| } | |
| // Note: these values need to match the values used by LateAttrParseKind in | |
| // `Attr.td` | |
| enum class LateAttrParseKind { Never = 0, Standard = 1, ExperimentalExt = 2 }; | |
| static LateAttrParseKind getLateAttrParseKind(const Record *Attr) { | |
| // This function basically does | |
| // `Attr->getValueAsDef("LateParsed")->getValueAsInt("Kind")` but does a bunch | |
| // of sanity checking to ensure that `LateAttrParseMode` in `Attr.td` is in | |
| // sync with the `LateAttrParseKind` enum in this source file. | |
| static constexpr StringRef LateParsedStr = "LateParsed"; | |
| static constexpr StringRef LateAttrParseKindStr = "LateAttrParseKind"; | |
| static constexpr StringRef KindFieldStr = "Kind"; | |
| auto *LAPK = Attr->getValueAsDef(LateParsedStr); | |
| // Typecheck the `LateParsed` field. | |
| if (LAPK->getDirectSuperClasses().size() != 1) | |
| PrintFatalError(Attr, "Field `" + Twine(LateParsedStr) + | |
| "`should only have one super class"); | |
| const Record *SuperClass = LAPK->getDirectSuperClasses()[0].first; | |
| if (SuperClass->getName() != LateAttrParseKindStr) | |
| PrintFatalError( | |
| Attr, "Field `" + Twine(LateParsedStr) + "`should only have type `" + | |
| Twine(LateAttrParseKindStr) + "` but found type `" + | |
| SuperClass->getName() + "`"); | |
| // Get Kind and verify the enum name matches the name in `Attr.td`. | |
| unsigned Kind = LAPK->getValueAsInt(KindFieldStr); | |
| switch (LateAttrParseKind(Kind)) { | |
| #define CASE(X) \ | |
| case LateAttrParseKind::X: \ | |
| if (LAPK->getName().compare("LateAttrParse" #X) != 0) { \ | |
| PrintFatalError( \ | |
| Attr, \ | |
| "Field `" + Twine(LateParsedStr) + "` set to `" + LAPK->getName() + \ | |
| "` but this converts to `LateAttrParseKind::" + Twine(#X) + \ | |
| "`"); \ | |
| } \ | |
| return LateAttrParseKind::X; | |
| CASE(Never) | |
| CASE(Standard) | |
| CASE(ExperimentalExt) | |
| #undef CASE | |
| } | |
| // The Kind value is completely invalid | |
| auto KindValueStr = utostr(Kind); | |
| PrintFatalError(Attr, "Field `" + Twine(LateParsedStr) + "` set to `" + | |
| LAPK->getName() + "` has unexpected `" + | |
| Twine(KindFieldStr) + "` value of " + KindValueStr); | |
| } | |
| // Emits the LateParsed property for attributes. | |
| static void emitClangAttrLateParsedListImpl(const RecordKeeper &Records, | |
| raw_ostream &OS, | |
| LateAttrParseKind LateParseMode) { | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| if (LateAttrParseKind LateParsed = getLateAttrParseKind(Attr); | |
| LateParsed != LateParseMode) | |
| continue; | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr); | |
| // FIXME: Handle non-GNU attributes | |
| for (const auto &I : Spellings) { | |
| if (I.variety() != "GNU") | |
| continue; | |
| OS << ".Case(\"" << I.name() << "\", 1)\n"; | |
| } | |
| } | |
| } | |
| static void emitClangAttrLateParsedList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_LATE_PARSED_LIST)\n"; | |
| emitClangAttrLateParsedListImpl(Records, OS, LateAttrParseKind::Standard); | |
| OS << "#endif // CLANG_ATTR_LATE_PARSED_LIST\n\n"; | |
| } | |
| static void emitClangAttrLateParsedExperimentalList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST)\n"; | |
| emitClangAttrLateParsedListImpl(Records, OS, | |
| LateAttrParseKind::ExperimentalExt); | |
| OS << "#endif // CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST\n\n"; | |
| } | |
| // Emits a list of attributes whose argument list is parsed inside a function | |
| // prototype scope so it can refer to the enclosing function's parameters. | |
| static void | |
| emitClangAttrParseArgsInFunctionScopeList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_PARSE_ARGS_IN_FUNCTION_SCOPE_LIST)\n"; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| if (!Attr->getValueAsBit("ParseArgsInFunctionScope")) | |
| continue; | |
| // FIXME: Handle non-GNU attributes | |
| for (const auto &I : GetFlattenedSpellings(*Attr)) | |
| if (I.variety() == "GNU") | |
| OS << ".Case(\"" << I.name() << "\", 1)\n"; | |
| } | |
| OS << "#endif // CLANG_ATTR_PARSE_ARGS_IN_FUNCTION_SCOPE_LIST\n\n"; | |
| } | |
| static bool hasGNUorCXX11Spelling(const Record &Attribute) { | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attribute); | |
| for (const auto &I : Spellings) { | |
| if (I.variety() == "GNU" || I.variety() == "CXX11") | |
| return true; | |
| } | |
| return false; | |
| } | |
| namespace { | |
| struct AttributeSubjectMatchRule { | |
| const Record *MetaSubject; | |
| const Record *Constraint; | |
| AttributeSubjectMatchRule(const Record *MetaSubject, const Record *Constraint) | |
| : MetaSubject(MetaSubject), Constraint(Constraint) { | |
| assert(MetaSubject && "Missing subject"); | |
| } | |
| bool isSubRule() const { return Constraint != nullptr; } | |
| std::vector<const Record *> getSubjects() const { | |
| return (Constraint ? Constraint : MetaSubject) | |
| ->getValueAsListOfDefs("Subjects"); | |
| } | |
| std::vector<const Record *> getLangOpts() const { | |
| if (Constraint) { | |
| // Lookup the options in the sub-rule first, in case the sub-rule | |
| // overrides the rules options. | |
| std::vector<const Record *> Opts = | |
| Constraint->getValueAsListOfDefs("LangOpts"); | |
| if (!Opts.empty()) | |
| return Opts; | |
| } | |
| return MetaSubject->getValueAsListOfDefs("LangOpts"); | |
| } | |
| // Abstract rules are used only for sub-rules | |
| bool isAbstractRule() const { return getSubjects().empty(); } | |
| StringRef getName() const { | |
| return (Constraint ? Constraint : MetaSubject)->getValueAsString("Name"); | |
| } | |
| bool isNegatedSubRule() const { | |
| assert(isSubRule() && "Not a sub-rule"); | |
| return Constraint->getValueAsBit("Negated"); | |
| } | |
| std::string getSpelling() const { | |
| std::string Result = MetaSubject->getValueAsString("Name").str(); | |
| if (isSubRule()) { | |
| Result += '('; | |
| if (isNegatedSubRule()) | |
| Result += "unless("; | |
| Result += getName(); | |
| if (isNegatedSubRule()) | |
| Result += ')'; | |
| Result += ')'; | |
| } | |
| return Result; | |
| } | |
| std::string getEnumValueName() const { | |
| SmallString<128> Result; | |
| Result += "SubjectMatchRule_"; | |
| Result += MetaSubject->getValueAsString("Name"); | |
| if (isSubRule()) { | |
| Result += "_"; | |
| if (isNegatedSubRule()) | |
| Result += "not_"; | |
| Result += Constraint->getValueAsString("Name"); | |
| } | |
| if (isAbstractRule()) | |
| Result += "_abstract"; | |
| return std::string(Result); | |
| } | |
| std::string getEnumValue() const { return "attr::" + getEnumValueName(); } | |
| static const char *EnumName; | |
| }; | |
| const char *AttributeSubjectMatchRule::EnumName = "attr::SubjectMatchRule"; | |
| struct PragmaClangAttributeSupport { | |
| std::vector<AttributeSubjectMatchRule> Rules; | |
| class RuleOrAggregateRuleSet { | |
| std::vector<AttributeSubjectMatchRule> Rules; | |
| bool IsRule; | |
| RuleOrAggregateRuleSet(ArrayRef<AttributeSubjectMatchRule> Rules, | |
| bool IsRule) | |
| : Rules(Rules), IsRule(IsRule) {} | |
| public: | |
| bool isRule() const { return IsRule; } | |
| const AttributeSubjectMatchRule &getRule() const { | |
| assert(IsRule && "not a rule!"); | |
| return Rules[0]; | |
| } | |
| ArrayRef<AttributeSubjectMatchRule> getAggregateRuleSet() const { | |
| return Rules; | |
| } | |
| static RuleOrAggregateRuleSet | |
| getRule(const AttributeSubjectMatchRule &Rule) { | |
| return RuleOrAggregateRuleSet(Rule, /*IsRule=*/true); | |
| } | |
| static RuleOrAggregateRuleSet | |
| getAggregateRuleSet(ArrayRef<AttributeSubjectMatchRule> Rules) { | |
| return RuleOrAggregateRuleSet(Rules, /*IsRule=*/false); | |
| } | |
| }; | |
| DenseMap<const Record *, RuleOrAggregateRuleSet> SubjectsToRules; | |
| PragmaClangAttributeSupport(const RecordKeeper &Records); | |
| bool isAttributedSupported(const Record &Attribute); | |
| void emitMatchRuleList(raw_ostream &OS); | |
| void generateStrictConformsTo(const Record &Attr, raw_ostream &OS); | |
| void generateParsingHelpers(raw_ostream &OS); | |
| }; | |
| } // end anonymous namespace | |
| static bool isSupportedPragmaClangAttributeSubject(const Record &Subject) { | |
| // FIXME: #pragma clang attribute does not currently support statement | |
| // attributes, so test whether the subject is one that appertains to a | |
| // declaration node. However, it may be reasonable for support for statement | |
| // attributes to be added. | |
| if (Subject.isSubClassOf("DeclNode") || Subject.isSubClassOf("DeclBase") || | |
| Subject.getName() == "DeclBase") | |
| return true; | |
| if (Subject.isSubClassOf("SubsetSubject")) | |
| return isSupportedPragmaClangAttributeSubject( | |
| *Subject.getValueAsDef("Base")); | |
| return false; | |
| } | |
| static bool doesDeclDeriveFrom(const Record *D, const Record *Base) { | |
| const Record *CurrentBase = D->getValueAsOptionalDef(BaseFieldName); | |
| if (!CurrentBase) | |
| return false; | |
| if (CurrentBase == Base) | |
| return true; | |
| return doesDeclDeriveFrom(CurrentBase, Base); | |
| } | |
| PragmaClangAttributeSupport::PragmaClangAttributeSupport( | |
| const RecordKeeper &Records) { | |
| auto MapFromSubjectsToRules = [this](const Record *SubjectContainer, | |
| const Record *MetaSubject, | |
| const Record *Constraint) { | |
| Rules.emplace_back(MetaSubject, Constraint); | |
| for (const Record *Subject : | |
| SubjectContainer->getValueAsListOfDefs("Subjects")) { | |
| bool Inserted = | |
| SubjectsToRules | |
| .try_emplace(Subject, RuleOrAggregateRuleSet::getRule( | |
| AttributeSubjectMatchRule(MetaSubject, | |
| Constraint))) | |
| .second; | |
| if (!Inserted) { | |
| PrintFatalError("Attribute subject match rules should not represent" | |
| "same attribute subjects."); | |
| } | |
| } | |
| }; | |
| for (const auto *MetaSubject : | |
| Records.getAllDerivedDefinitions("AttrSubjectMatcherRule")) { | |
| MapFromSubjectsToRules(MetaSubject, MetaSubject, /*Constraints=*/nullptr); | |
| for (const Record *Constraint : | |
| MetaSubject->getValueAsListOfDefs("Constraints")) | |
| MapFromSubjectsToRules(Constraint, MetaSubject, Constraint); | |
| } | |
| ArrayRef<const Record *> DeclNodes = | |
| Records.getAllDerivedDefinitions(DeclNodeClassName); | |
| for (const auto *Aggregate : | |
| Records.getAllDerivedDefinitions("AttrSubjectMatcherAggregateRule")) { | |
| const Record *SubjectDecl = Aggregate->getValueAsDef("Subject"); | |
| // Gather sub-classes of the aggregate subject that act as attribute | |
| // subject rules. | |
| std::vector<AttributeSubjectMatchRule> Rules; | |
| for (const auto *D : DeclNodes) { | |
| if (doesDeclDeriveFrom(D, SubjectDecl)) { | |
| auto It = SubjectsToRules.find(D); | |
| if (It == SubjectsToRules.end()) | |
| continue; | |
| if (!It->second.isRule() || It->second.getRule().isSubRule()) | |
| continue; // Assume that the rule will be included as well. | |
| Rules.push_back(It->second.getRule()); | |
| } | |
| } | |
| bool Inserted = | |
| SubjectsToRules | |
| .try_emplace(SubjectDecl, | |
| RuleOrAggregateRuleSet::getAggregateRuleSet(Rules)) | |
| .second; | |
| if (!Inserted) { | |
| PrintFatalError("Attribute subject match rules should not represent" | |
| "same attribute subjects."); | |
| } | |
| } | |
| } | |
| static PragmaClangAttributeSupport & | |
| getPragmaAttributeSupport(const RecordKeeper &Records) { | |
| static PragmaClangAttributeSupport Instance(Records); | |
| return Instance; | |
| } | |
| void PragmaClangAttributeSupport::emitMatchRuleList(raw_ostream &OS) { | |
| OS << "#ifndef ATTR_MATCH_SUB_RULE\n"; | |
| OS << "#define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, " | |
| "IsNegated) " | |
| << "ATTR_MATCH_RULE(Value, Spelling, IsAbstract)\n"; | |
| OS << "#endif\n"; | |
| for (const auto &Rule : Rules) { | |
| OS << (Rule.isSubRule() ? "ATTR_MATCH_SUB_RULE" : "ATTR_MATCH_RULE") << '('; | |
| OS << Rule.getEnumValueName() << ", \"" << Rule.getSpelling() << "\", " | |
| << Rule.isAbstractRule(); | |
| if (Rule.isSubRule()) | |
| OS << ", " | |
| << AttributeSubjectMatchRule(Rule.MetaSubject, nullptr).getEnumValue() | |
| << ", " << Rule.isNegatedSubRule(); | |
| OS << ")\n"; | |
| } | |
| OS << "#undef ATTR_MATCH_SUB_RULE\n"; | |
| } | |
| bool PragmaClangAttributeSupport::isAttributedSupported( | |
| const Record &Attribute) { | |
| // If the attribute explicitly specified whether to support #pragma clang | |
| // attribute, use that setting. | |
| bool Unset; | |
| bool SpecifiedResult = | |
| Attribute.getValueAsBitOrUnset("PragmaAttributeSupport", Unset); | |
| if (!Unset) | |
| return SpecifiedResult; | |
| // Opt-out rules: | |
| // An attribute requires delayed parsing (LateParsed is on). | |
| switch (getLateAttrParseKind(&Attribute)) { | |
| case LateAttrParseKind::Never: | |
| break; | |
| case LateAttrParseKind::Standard: | |
| return false; | |
| case LateAttrParseKind::ExperimentalExt: | |
| // This is only late parsed in certain parsing contexts when | |
| // `LangOpts.ExperimentalLateParseAttributes` is true. Information about the | |
| // parsing context and `LangOpts` is not available in this method so just | |
| // opt this attribute out. | |
| return false; | |
| } | |
| // An attribute has no GNU/CXX11 spelling | |
| if (!hasGNUorCXX11Spelling(Attribute)) | |
| return false; | |
| // An attribute subject list has a subject that isn't covered by one of the | |
| // subject match rules or has no subjects at all. | |
| if (Attribute.isValueUnset("Subjects")) | |
| return false; | |
| const Record *SubjectObj = Attribute.getValueAsDef("Subjects"); | |
| bool HasAtLeastOneValidSubject = false; | |
| for (const auto *Subject : SubjectObj->getValueAsListOfDefs("Subjects")) { | |
| if (!isSupportedPragmaClangAttributeSubject(*Subject)) | |
| continue; | |
| if (!SubjectsToRules.contains(Subject)) | |
| return false; | |
| HasAtLeastOneValidSubject = true; | |
| } | |
| return HasAtLeastOneValidSubject; | |
| } | |
| static std::string GenerateTestExpression(ArrayRef<const Record *> LangOpts) { | |
| std::string Test; | |
| for (auto *E : LangOpts) { | |
| if (!Test.empty()) | |
| Test += " || "; | |
| const StringRef Code = E->getValueAsString("CustomCode"); | |
| if (!Code.empty()) { | |
| Test += "("; | |
| Test += Code; | |
| Test += ")"; | |
| if (!E->getValueAsString("Name").empty()) { | |
| PrintWarning( | |
| E->getLoc(), | |
| "non-empty 'Name' field ignored because 'CustomCode' was supplied"); | |
| } | |
| } else { | |
| Test += "LangOpts."; | |
| Test += E->getValueAsString("Name"); | |
| } | |
| } | |
| if (Test.empty()) | |
| return "true"; | |
| return Test; | |
| } | |
| void | |
| PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, | |
| raw_ostream &OS) { | |
| if (!isAttributedSupported(Attr) || Attr.isValueUnset("Subjects")) | |
| return; | |
| // Generate a function that constructs a set of matching rules that describe | |
| // to which declarations the attribute should apply to. | |
| OS << "void getPragmaAttributeMatchRules(" | |
| << "llvm::SmallVectorImpl<std::pair<" | |
| << AttributeSubjectMatchRule::EnumName | |
| << ", bool>> &MatchRules, const LangOptions &LangOpts) const override {\n"; | |
| const Record *SubjectObj = Attr.getValueAsDef("Subjects"); | |
| for (const auto *Subject : SubjectObj->getValueAsListOfDefs("Subjects")) { | |
| if (!isSupportedPragmaClangAttributeSubject(*Subject)) | |
| continue; | |
| auto It = SubjectsToRules.find(Subject); | |
| assert(It != SubjectsToRules.end() && | |
| "This attribute is unsupported by #pragma clang attribute"); | |
| for (const auto &Rule : It->getSecond().getAggregateRuleSet()) { | |
| // The rule might be language specific, so only subtract it from the given | |
| // rules if the specific language options are specified. | |
| std::vector<const Record *> LangOpts = Rule.getLangOpts(); | |
| OS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue() | |
| << ", /*IsSupported=*/" << GenerateTestExpression(LangOpts) | |
| << "));\n"; | |
| } | |
| } | |
| OS << "}\n\n"; | |
| } | |
| void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) { | |
| // Generate routines that check the names of sub-rules. | |
| OS << "std::optional<attr::SubjectMatchRule> " | |
| "defaultIsAttributeSubjectMatchSubRuleFor(StringRef, bool) {\n"; | |
| OS << " return std::nullopt;\n"; | |
| OS << "}\n\n"; | |
| MapVector<const Record *, std::vector<AttributeSubjectMatchRule>> | |
| SubMatchRules; | |
| for (const auto &Rule : Rules) { | |
| if (!Rule.isSubRule()) | |
| continue; | |
| SubMatchRules[Rule.MetaSubject].push_back(Rule); | |
| } | |
| for (const auto &SubMatchRule : SubMatchRules) { | |
| OS << "std::optional<attr::SubjectMatchRule> " | |
| "isAttributeSubjectMatchSubRuleFor_" | |
| << SubMatchRule.first->getValueAsString("Name") | |
| << "(StringRef Name, bool IsUnless) {\n"; | |
| OS << " if (IsUnless)\n"; | |
| OS << " return " | |
| "llvm::StringSwitch<std::optional<attr::SubjectMatchRule>>(Name).\n"; | |
| for (const auto &Rule : SubMatchRule.second) { | |
| if (Rule.isNegatedSubRule()) | |
| OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue() | |
| << ").\n"; | |
| } | |
| OS << " Default(std::nullopt);\n"; | |
| OS << " return " | |
| "llvm::StringSwitch<std::optional<attr::SubjectMatchRule>>(Name).\n"; | |
| for (const auto &Rule : SubMatchRule.second) { | |
| if (!Rule.isNegatedSubRule()) | |
| OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue() | |
| << ").\n"; | |
| } | |
| OS << " Default(std::nullopt);\n"; | |
| OS << "}\n\n"; | |
| } | |
| // Generate the function that checks for the top-level rules. | |
| OS << "std::pair<std::optional<attr::SubjectMatchRule>, " | |
| "std::optional<attr::SubjectMatchRule> (*)(StringRef, " | |
| "bool)> isAttributeSubjectMatchRule(StringRef Name) {\n"; | |
| OS << " return " | |
| "llvm::StringSwitch<std::pair<std::optional<attr::SubjectMatchRule>, " | |
| "std::optional<attr::SubjectMatchRule> (*) (StringRef, " | |
| "bool)>>(Name).\n"; | |
| for (const auto &Rule : Rules) { | |
| if (Rule.isSubRule()) | |
| continue; | |
| std::string SubRuleFunction; | |
| if (SubMatchRules.count(Rule.MetaSubject)) | |
| SubRuleFunction = | |
| ("isAttributeSubjectMatchSubRuleFor_" + Rule.getName()).str(); | |
| else | |
| SubRuleFunction = "defaultIsAttributeSubjectMatchSubRuleFor"; | |
| OS << " Case(\"" << Rule.getName() << "\", std::make_pair(" | |
| << Rule.getEnumValue() << ", " << SubRuleFunction << ")).\n"; | |
| } | |
| OS << " Default(std::make_pair(std::nullopt, " | |
| "defaultIsAttributeSubjectMatchSubRuleFor));\n"; | |
| OS << "}\n\n"; | |
| // Generate the function that checks for the submatch rules. | |
| OS << "const char *validAttributeSubjectMatchSubRules(" | |
| << AttributeSubjectMatchRule::EnumName << " Rule) {\n"; | |
| OS << " switch (Rule) {\n"; | |
| for (const auto &SubMatchRule : SubMatchRules) { | |
| OS << " case " | |
| << AttributeSubjectMatchRule(SubMatchRule.first, nullptr).getEnumValue() | |
| << ":\n"; | |
| OS << " return \"'"; | |
| bool IsFirst = true; | |
| for (const auto &Rule : SubMatchRule.second) { | |
| if (!IsFirst) | |
| OS << ", '"; | |
| IsFirst = false; | |
| if (Rule.isNegatedSubRule()) | |
| OS << "unless("; | |
| OS << Rule.getName(); | |
| if (Rule.isNegatedSubRule()) | |
| OS << ')'; | |
| OS << "'"; | |
| } | |
| OS << "\";\n"; | |
| } | |
| OS << " default: return nullptr;\n"; | |
| OS << " }\n"; | |
| OS << "}\n\n"; | |
| } | |
| template <typename Fn> static void forEachSpelling(const Record &Attr, Fn &&F) { | |
| for (const FlattenedSpelling &S : GetFlattenedSpellings(Attr)) { | |
| F(S); | |
| } | |
| } | |
| static std::map<StringRef, std::vector<const Record *>> NameToAttrsMap; | |
| /// Build a map from the attribute name to the Attrs that use that name. If more | |
| /// than one Attr use a name, the arguments could be different so a more complex | |
| /// check is needed in the generated switch. | |
| static void generateNameToAttrsMap(const RecordKeeper &Records) { | |
| for (const auto *A : Records.getAllDerivedDefinitions("Attr")) { | |
| for (const FlattenedSpelling &S : GetFlattenedSpellings(*A)) { | |
| auto [It, Inserted] = NameToAttrsMap.try_emplace(S.name()); | |
| if (Inserted || !is_contained(It->second, A)) | |
| It->second.emplace_back(A); | |
| } | |
| } | |
| } | |
| /// Generate the info needed to produce the case values in case more than one | |
| /// attribute has the same name. Store the info in a map that can be processed | |
| /// after all attributes are seen. | |
| static void generateFlattenedSpellingInfo(const Record &Attr, | |
| std::map<StringRef, FSIVecTy> &Map, | |
| uint32_t ArgMask = 0) { | |
| std::string TargetTest; | |
| if (Attr.isSubClassOf("TargetSpecificAttr") && | |
| !Attr.isValueUnset("ParseKind")) { | |
| const Record *T = Attr.getValueAsDef("Target"); | |
| std::vector<StringRef> Arches = T->getValueAsListOfStrings("Arches"); | |
| (void)GenerateTargetSpecificAttrChecks(T, Arches, TargetTest, nullptr); | |
| } | |
| forEachSpelling(Attr, [&](const FlattenedSpelling &S) { | |
| Map[S.name()].emplace_back(S.variety(), S.nameSpace(), TargetTest, ArgMask); | |
| }); | |
| } | |
| static bool nameAppliesToOneAttribute(StringRef Name) { | |
| auto It = NameToAttrsMap.find(Name); | |
| assert(It != NameToAttrsMap.end()); | |
| return It->second.size() == 1; | |
| } | |
| static bool emitIfSimpleValue(StringRef Name, uint32_t ArgMask, | |
| raw_ostream &OS) { | |
| if (nameAppliesToOneAttribute(Name)) { | |
| OS << ".Case(\"" << Name << "\", "; | |
| if (ArgMask != 0) | |
| OS << ArgMask << ")\n"; | |
| else | |
| OS << "true)\n"; | |
| return true; | |
| } | |
| return false; | |
| } | |
| static void emitSingleCondition(const FlattenedSpellingInfo &FSI, | |
| raw_ostream &OS) { | |
| OS << "(Syntax==AttributeCommonInfo::AS_" << FSI.Syntax << " && "; | |
| if (!FSI.Scope.empty()) | |
| OS << "ScopeName && ScopeName->getName()==\"" << FSI.Scope << "\""; | |
| else | |
| OS << "!ScopeName"; | |
| if (!FSI.TargetTest.empty()) | |
| OS << " && " << FSI.TargetTest; | |
| OS << ")"; | |
| } | |
| static void emitStringSwitchCases(std::map<StringRef, FSIVecTy> &Map, | |
| raw_ostream &OS) { | |
| for (const auto &[Name, Vec] : Map) { | |
| if (emitIfSimpleValue(Name, Vec[0].ArgMask, OS)) | |
| continue; | |
| // Not simple, build expressions for each case. | |
| OS << ".Case(\"" << Name << "\", "; | |
| for (unsigned I = 0, E = Vec.size(); I < E; ++I) { | |
| emitSingleCondition(Vec[I], OS); | |
| uint32_t ArgMask = Vec[I].ArgMask; | |
| if (E == 1 && ArgMask == 0) | |
| continue; | |
| // More than one or it's the Mask form. Create a conditional expression. | |
| uint32_t SuccessValue = ArgMask != 0 ? ArgMask : 1; | |
| OS << " ? " << SuccessValue << " : "; | |
| if (I == E - 1) | |
| OS << 0; | |
| } | |
| OS << ")\n"; | |
| } | |
| } | |
| static bool isTypeArgument(const Record *Arg) { | |
| return !Arg->getDirectSuperClasses().empty() && | |
| Arg->getDirectSuperClasses().back().first->getName() == "TypeArgument"; | |
| } | |
| /// Emits the first-argument-is-type property for attributes. | |
| static void emitClangAttrTypeArgList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_TYPE_ARG_LIST)\n"; | |
| std::map<StringRef, FSIVecTy> FSIMap; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| // Determine whether the first argument is a type. | |
| std::vector<const Record *> Args = Attr->getValueAsListOfDefs("Args"); | |
| if (Args.empty()) | |
| continue; | |
| if (!isTypeArgument(Args[0])) | |
| continue; | |
| generateFlattenedSpellingInfo(*Attr, FSIMap); | |
| } | |
| emitStringSwitchCases(FSIMap, OS); | |
| OS << "#endif // CLANG_ATTR_TYPE_ARG_LIST\n\n"; | |
| } | |
| /// Emits the parse-arguments-in-unevaluated-context property for | |
| /// attributes. | |
| static void emitClangAttrArgContextList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_ARG_CONTEXT_LIST)\n"; | |
| std::map<StringRef, FSIVecTy> FSIMap; | |
| ParsedAttrMap Attrs = getParsedAttrList(Records); | |
| for (const auto &I : Attrs) { | |
| const Record &Attr = *I.second; | |
| if (!Attr.getValueAsBit("ParseArgumentsAsUnevaluated")) | |
| continue; | |
| generateFlattenedSpellingInfo(Attr, FSIMap); | |
| } | |
| emitStringSwitchCases(FSIMap, OS); | |
| OS << "#endif // CLANG_ATTR_ARG_CONTEXT_LIST\n\n"; | |
| } | |
| static bool isIdentifierArgument(const Record *Arg) { | |
| return !Arg->getDirectSuperClasses().empty() && | |
| StringSwitch<bool>( | |
| Arg->getDirectSuperClasses().back().first->getName()) | |
| .Case("IdentifierArgument", true) | |
| .Case("EnumArgument", true) | |
| .Case("VariadicEnumArgument", true) | |
| .Default(false); | |
| } | |
| static bool isVariadicIdentifierArgument(const Record *Arg) { | |
| return !Arg->getDirectSuperClasses().empty() && | |
| StringSwitch<bool>( | |
| Arg->getDirectSuperClasses().back().first->getName()) | |
| .Case("VariadicIdentifierArgument", true) | |
| .Case("VariadicParamOrParamIdxArgument", true) | |
| .Default(false); | |
| } | |
| static bool isVariadicExprArgument(const Record *Arg) { | |
| return !Arg->getDirectSuperClasses().empty() && | |
| StringSwitch<bool>( | |
| Arg->getDirectSuperClasses().back().first->getName()) | |
| .Case("VariadicExprArgument", true) | |
| .Default(false); | |
| } | |
| static bool isStringLiteralArgument(const Record *Arg) { | |
| if (Arg->getDirectSuperClasses().empty()) | |
| return false; | |
| StringRef ArgKind = Arg->getDirectSuperClasses().back().first->getName(); | |
| if (ArgKind == "EnumArgument") | |
| return Arg->getValueAsBit("IsString"); | |
| return ArgKind == "StringArgument"; | |
| } | |
| static bool isVariadicStringLiteralArgument(const Record *Arg) { | |
| if (Arg->getDirectSuperClasses().empty()) | |
| return false; | |
| StringRef ArgKind = Arg->getDirectSuperClasses().back().first->getName(); | |
| if (ArgKind == "VariadicEnumArgument") | |
| return Arg->getValueAsBit("IsString"); | |
| return ArgKind == "VariadicStringArgument"; | |
| } | |
| static void emitClangAttrVariadicIdentifierArgList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST)\n"; | |
| std::map<StringRef, FSIVecTy> FSIMap; | |
| for (const auto *A : Records.getAllDerivedDefinitions("Attr")) { | |
| // Determine whether the first argument is a variadic identifier. | |
| std::vector<const Record *> Args = A->getValueAsListOfDefs("Args"); | |
| if (Args.empty() || !isVariadicIdentifierArgument(Args[0])) | |
| continue; | |
| generateFlattenedSpellingInfo(*A, FSIMap); | |
| } | |
| emitStringSwitchCases(FSIMap, OS); | |
| OS << "#endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST\n\n"; | |
| } | |
| // Emits the list of arguments that should be parsed as unevaluated string | |
| // literals for each attribute. | |
| static void | |
| emitClangAttrUnevaluatedStringLiteralList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_STRING_LITERAL_ARG_LIST)\n"; | |
| auto MakeMask = [](ArrayRef<const Record *> Args) { | |
| uint32_t Bits = 0; | |
| assert(Args.size() <= 32 && "unsupported number of arguments in attribute"); | |
| for (uint32_t N = 0; N < Args.size(); ++N) { | |
| Bits |= (isStringLiteralArgument(Args[N]) << N); | |
| // If we have a variadic string argument, set all the remaining bits to 1 | |
| if (isVariadicStringLiteralArgument(Args[N])) { | |
| Bits |= maskTrailingZeros<decltype(Bits)>(N); | |
| break; | |
| } | |
| } | |
| return Bits; | |
| }; | |
| std::map<StringRef, FSIVecTy> FSIMap; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| // Determine whether there are any string arguments. | |
| uint32_t ArgMask = MakeMask(Attr->getValueAsListOfDefs("Args")); | |
| if (!ArgMask) | |
| continue; | |
| generateFlattenedSpellingInfo(*Attr, FSIMap, ArgMask); | |
| } | |
| emitStringSwitchCases(FSIMap, OS); | |
| OS << "#endif // CLANG_ATTR_STRING_LITERAL_ARG_LIST\n\n"; | |
| } | |
| // Emits the first-argument-is-identifier property for attributes. | |
| static void emitClangAttrIdentifierArgList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_IDENTIFIER_ARG_LIST)\n"; | |
| std::map<StringRef, FSIVecTy> FSIMap; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| // Determine whether the first argument is an identifier. | |
| std::vector<const Record *> Args = Attr->getValueAsListOfDefs("Args"); | |
| if (Args.empty() || !isIdentifierArgument(Args[0])) | |
| continue; | |
| generateFlattenedSpellingInfo(*Attr, FSIMap); | |
| } | |
| emitStringSwitchCases(FSIMap, OS); | |
| OS << "#endif // CLANG_ATTR_IDENTIFIER_ARG_LIST\n\n"; | |
| } | |
| // Emits the list for attributes having StrictEnumParameters. | |
| static void emitClangAttrStrictIdentifierArgList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_STRICT_IDENTIFIER_ARG_LIST)\n"; | |
| std::map<StringRef, FSIVecTy> FSIMap; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| if (!Attr->getValueAsBit("StrictEnumParameters")) | |
| continue; | |
| // Check that there is really an identifier argument. | |
| std::vector<const Record *> Args = Attr->getValueAsListOfDefs("Args"); | |
| if (none_of(Args, [&](const Record *R) { return isIdentifierArgument(R); })) | |
| continue; | |
| generateFlattenedSpellingInfo(*Attr, FSIMap); | |
| } | |
| emitStringSwitchCases(FSIMap, OS); | |
| OS << "#endif // CLANG_ATTR_STRICT_IDENTIFIER_ARG_LIST\n\n"; | |
| } | |
| static bool keywordThisIsaIdentifierInArgument(const Record *Arg) { | |
| return !Arg->getDirectSuperClasses().empty() && | |
| StringSwitch<bool>( | |
| Arg->getDirectSuperClasses().back().first->getName()) | |
| .Case("VariadicParamOrParamIdxArgument", true) | |
| .Default(false); | |
| } | |
| static void emitClangAttrThisIsaIdentifierArgList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST)\n"; | |
| std::map<StringRef, FSIVecTy> FSIMap; | |
| for (const auto *A : Records.getAllDerivedDefinitions("Attr")) { | |
| // Determine whether the first argument is a variadic identifier. | |
| std::vector<const Record *> Args = A->getValueAsListOfDefs("Args"); | |
| if (Args.empty() || !keywordThisIsaIdentifierInArgument(Args[0])) | |
| continue; | |
| generateFlattenedSpellingInfo(*A, FSIMap); | |
| } | |
| emitStringSwitchCases(FSIMap, OS); | |
| OS << "#endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST\n\n"; | |
| } | |
| static void emitClangAttrAcceptsExprPack(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "#if defined(CLANG_ATTR_ACCEPTS_EXPR_PACK)\n"; | |
| ParsedAttrMap Attrs = getParsedAttrList(Records); | |
| std::map<StringRef, FSIVecTy> FSIMap; | |
| for (const auto &I : Attrs) { | |
| const Record &Attr = *I.second; | |
| if (!Attr.getValueAsBit("AcceptsExprPack")) | |
| continue; | |
| generateFlattenedSpellingInfo(Attr, FSIMap); | |
| } | |
| emitStringSwitchCases(FSIMap, OS); | |
| OS << "#endif // CLANG_ATTR_ACCEPTS_EXPR_PACK\n\n"; | |
| } | |
| static bool isRegularKeywordAttribute(const FlattenedSpelling &S) { | |
| return (S.variety() == "Keyword" && | |
| !S.getSpellingRecord().getValueAsBit("HasOwnParseRules")); | |
| } | |
| static void emitFormInitializer(raw_ostream &OS, | |
| const FlattenedSpelling &Spelling, | |
| StringRef SpellingIndex) { | |
| bool IsAlignas = | |
| (Spelling.variety() == "Keyword" && Spelling.name() == "alignas"); | |
| OS << "{AttributeCommonInfo::AS_" << Spelling.variety() << ", " | |
| << SpellingIndex << ", " << (IsAlignas ? "true" : "false") | |
| << " /*IsAlignas*/, " | |
| << (isRegularKeywordAttribute(Spelling) ? "true" : "false") | |
| << " /*IsRegularKeywordAttribute*/}"; | |
| } | |
| static void emitAttributes(const RecordKeeper &Records, raw_ostream &OS, | |
| bool Header) { | |
| ParsedAttrMap AttrMap = getParsedAttrList(Records); | |
| // Helper to print the starting character of an attribute argument. If there | |
| // hasn't been an argument yet, it prints an opening parenthese; otherwise it | |
| // prints a comma. | |
| OS << "static inline void DelimitAttributeArgument(" | |
| << "raw_ostream& OS, bool& IsFirst) {\n" | |
| << " if (IsFirst) {\n" | |
| << " IsFirst = false;\n" | |
| << " OS << \"(\";\n" | |
| << " } else\n" | |
| << " OS << \", \";\n" | |
| << "}\n"; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &R = *Attr; | |
| // FIXME: Currently, documentation is generated as-needed due to the fact | |
| // that there is no way to allow a generated project "reach into" the docs | |
| // directory (for instance, it may be an out-of-tree build). However, we want | |
| // to ensure that every attribute has a Documentation field, and produce an | |
| // error if it has been neglected. Otherwise, the on-demand generation which | |
| // happens server-side will fail. This code is ensuring that functionality, | |
| // even though this Emitter doesn't technically need the documentation. | |
| // When attribute documentation can be generated as part of the build | |
| // itself, this code can be removed. | |
| (void)R.getValueAsListOfDefs("Documentation"); | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| std::vector<const Record *> Supers = R.getSuperClasses(); | |
| assert(!Supers.empty() && "Forgot to specify a superclass for the attr"); | |
| std::string SuperName; | |
| bool Inheritable = false; | |
| for (const Record *R : reverse(Supers)) { | |
| if (R->getName() != "TargetSpecificAttr" && | |
| R->getName() != "DeclOrTypeAttr" && SuperName.empty()) | |
| SuperName = R->getName().str(); | |
| if (R->getName() == "InheritableAttr") | |
| Inheritable = true; | |
| } | |
| if (Header) | |
| OS << "class CLANG_ABI " << R.getName() << "Attr : public " << SuperName | |
| << " {\n"; | |
| else | |
| OS << "\n// " << R.getName() << "Attr implementation\n\n"; | |
| std::vector<const Record *> ArgRecords = R.getValueAsListOfDefs("Args"); | |
| std::vector<std::unique_ptr<Argument>> Args; | |
| Args.reserve(ArgRecords.size()); | |
| bool AttrAcceptsExprPack = Attr->getValueAsBit("AcceptsExprPack"); | |
| if (AttrAcceptsExprPack) { | |
| for (size_t I = 0; I < ArgRecords.size(); ++I) { | |
| const Record *ArgR = ArgRecords[I]; | |
| if (isIdentifierArgument(ArgR) || isVariadicIdentifierArgument(ArgR) || | |
| isTypeArgument(ArgR)) | |
| PrintFatalError(Attr->getLoc(), | |
| "Attributes accepting packs cannot also " | |
| "have identifier or type arguments."); | |
| // When trying to determine if value-dependent expressions can populate | |
| // the attribute without prior instantiation, the decision is made based | |
| // on the assumption that only the last argument is ever variadic. | |
| if (I < (ArgRecords.size() - 1) && isVariadicExprArgument(ArgR)) | |
| PrintFatalError(Attr->getLoc(), | |
| "Attributes accepting packs can only have the last " | |
| "argument be variadic."); | |
| } | |
| } | |
| bool HasOptArg = false; | |
| bool HasFakeArg = false; | |
| for (const auto *ArgRecord : ArgRecords) { | |
| Args.emplace_back(createArgument(*ArgRecord, R.getName())); | |
| if (Header) { | |
| Args.back()->writeDeclarations(OS); | |
| OS << "\n\n"; | |
| } | |
| // For these purposes, fake takes priority over optional. | |
| if (Args.back()->isFake()) { | |
| HasFakeArg = true; | |
| } else if (Args.back()->isOptional()) { | |
| HasOptArg = true; | |
| } | |
| } | |
| std::unique_ptr<VariadicExprArgument> DelayedArgs = nullptr; | |
| if (AttrAcceptsExprPack) { | |
| DelayedArgs = | |
| std::make_unique<VariadicExprArgument>("DelayedArgs", R.getName()); | |
| if (Header) { | |
| DelayedArgs->writeDeclarations(OS); | |
| OS << "\n\n"; | |
| } | |
| } | |
| if (Header) | |
| OS << "public:\n"; | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); | |
| // If there are zero or one spellings, all spelling-related functionality | |
| // can be elided. If all of the spellings share the same name, the spelling | |
| // functionality can also be elided. | |
| bool ElideSpelling = (Spellings.size() <= 1) || | |
| SpellingNamesAreCommon(Spellings); | |
| // This maps spelling index values to semantic Spelling enumerants. | |
| SemanticSpellingMap SemanticToSyntacticMap; | |
| std::string SpellingEnum; | |
| if (Spellings.size() > 1) | |
| SpellingEnum = CreateSemanticSpellings(Spellings, SemanticToSyntacticMap); | |
| if (Header) | |
| OS << SpellingEnum; | |
| const auto &ParsedAttrSpellingItr = | |
| find_if(AttrMap, [R](const std::pair<std::string, const Record *> &P) { | |
| return &R == P.second; | |
| }); | |
| // Emit CreateImplicit factory methods. | |
| auto emitCreate = [&](bool Implicit, bool DelayedArgsOnly, bool emitFake) { | |
| if (Header) | |
| OS << " static "; | |
| OS << R.getName() << "Attr *"; | |
| if (!Header) | |
| OS << R.getName() << "Attr::"; | |
| OS << "Create"; | |
| if (Implicit) | |
| OS << "Implicit"; | |
| if (DelayedArgsOnly) | |
| OS << "WithDelayedArgs"; | |
| OS << "("; | |
| OS << "ASTContext &Ctx"; | |
| if (!DelayedArgsOnly) { | |
| for (auto const &ai : Args) { | |
| if (ai->isFake() && !emitFake) | |
| continue; | |
| OS << ", "; | |
| ai->writeCtorParameters(OS); | |
| } | |
| } else { | |
| OS << ", "; | |
| DelayedArgs->writeCtorParameters(OS); | |
| } | |
| OS << ", const AttributeCommonInfo &CommonInfo"; | |
| OS << ")"; | |
| if (Header) { | |
| OS << ";\n"; | |
| return; | |
| } | |
| OS << " {\n"; | |
| OS << " auto *A = new (Ctx) " << R.getName(); | |
| OS << "Attr(Ctx, CommonInfo"; | |
| if (!DelayedArgsOnly) { | |
| for (auto const &ai : Args) { | |
| if (ai->isFake() && !emitFake) | |
| continue; | |
| OS << ", "; | |
| ai->writeImplicitCtorArgs(OS); | |
| } | |
| } | |
| OS << ");\n"; | |
| if (Implicit) { | |
| OS << " A->setImplicit(true);\n"; | |
| } | |
| if (Implicit || ElideSpelling) { | |
| OS << " if (!A->isAttributeSpellingListCalculated() && " | |
| "!A->getAttrName())\n"; | |
| OS << " A->setAttributeSpellingListIndex(0);\n"; | |
| } | |
| if (DelayedArgsOnly) { | |
| OS << " A->setDelayedArgs(Ctx, "; | |
| DelayedArgs->writeImplicitCtorArgs(OS); | |
| OS << ");\n"; | |
| } | |
| OS << " return A;\n}\n\n"; | |
| }; | |
| auto emitCreateNoCI = [&](bool Implicit, bool DelayedArgsOnly, | |
| bool emitFake) { | |
| if (Header) | |
| OS << " static "; | |
| OS << R.getName() << "Attr *"; | |
| if (!Header) | |
| OS << R.getName() << "Attr::"; | |
| OS << "Create"; | |
| if (Implicit) | |
| OS << "Implicit"; | |
| if (DelayedArgsOnly) | |
| OS << "WithDelayedArgs"; | |
| OS << "("; | |
| OS << "ASTContext &Ctx"; | |
| if (!DelayedArgsOnly) { | |
| for (auto const &ai : Args) { | |
| if (ai->isFake() && !emitFake) | |
| continue; | |
| OS << ", "; | |
| ai->writeCtorParameters(OS); | |
| } | |
| } else { | |
| OS << ", "; | |
| DelayedArgs->writeCtorParameters(OS); | |
| } | |
| OS << ", SourceRange Range"; | |
| if (Header) | |
| OS << " = {}"; | |
| if (Spellings.size() > 1) { | |
| OS << ", Spelling S"; | |
| if (Header) | |
| OS << " = " << SemanticToSyntacticMap[0]; | |
| } | |
| OS << ")"; | |
| if (Header) { | |
| OS << ";\n"; | |
| return; | |
| } | |
| OS << " {\n"; | |
| OS << " AttributeCommonInfo I(Range, "; | |
| if (ParsedAttrSpellingItr != std::end(AttrMap)) | |
| OS << "AT_" << ParsedAttrSpellingItr->first; | |
| else | |
| OS << "NoSemaHandlerAttribute"; | |
| if (Spellings.size() == 0) { | |
| OS << ", AttributeCommonInfo::Form::Implicit()"; | |
| } else if (Spellings.size() == 1) { | |
| OS << ", "; | |
| emitFormInitializer(OS, Spellings[0], "0"); | |
| } else { | |
| OS << ", [&]() {\n"; | |
| OS << " switch (S) {\n"; | |
| std::set<std::string> Uniques; | |
| unsigned Idx = 0; | |
| for (auto I = Spellings.begin(), E = Spellings.end(); I != E; | |
| ++I, ++Idx) { | |
| const FlattenedSpelling &S = *I; | |
| const auto &Name = SemanticToSyntacticMap[Idx]; | |
| if (Uniques.insert(Name).second) { | |
| OS << " case " << Name << ":\n"; | |
| OS << " return AttributeCommonInfo::Form"; | |
| emitFormInitializer(OS, S, Name); | |
| OS << ";\n"; | |
| } | |
| } | |
| OS << " default:\n"; | |
| OS << " llvm_unreachable(\"Unknown attribute spelling!\");\n" | |
| << " return AttributeCommonInfo::Form"; | |
| emitFormInitializer(OS, Spellings[0], "0"); | |
| OS << ";\n" | |
| << " }\n" | |
| << " }()"; | |
| } | |
| OS << ");\n"; | |
| OS << " return Create"; | |
| if (Implicit) | |
| OS << "Implicit"; | |
| if (DelayedArgsOnly) | |
| OS << "WithDelayedArgs"; | |
| OS << "(Ctx"; | |
| if (!DelayedArgsOnly) { | |
| for (auto const &ai : Args) { | |
| if (ai->isFake() && !emitFake) | |
| continue; | |
| OS << ", "; | |
| ai->writeImplicitCtorArgs(OS); | |
| } | |
| } else { | |
| OS << ", "; | |
| DelayedArgs->writeImplicitCtorArgs(OS); | |
| } | |
| OS << ", I);\n"; | |
| OS << "}\n\n"; | |
| }; | |
| auto emitCreates = [&](bool DelayedArgsOnly, bool emitFake) { | |
| emitCreate(true, DelayedArgsOnly, emitFake); | |
| emitCreate(false, DelayedArgsOnly, emitFake); | |
| emitCreateNoCI(true, DelayedArgsOnly, emitFake); | |
| emitCreateNoCI(false, DelayedArgsOnly, emitFake); | |
| }; | |
| if (Header) | |
| OS << " // Factory methods\n"; | |
| // Emit a CreateImplicit that takes all the arguments. | |
| emitCreates(false, true); | |
| // Emit a CreateImplicit that takes all the non-fake arguments. | |
| if (HasFakeArg) | |
| emitCreates(false, false); | |
| // Emit a CreateWithDelayedArgs that takes only the dependent argument | |
| // expressions. | |
| if (DelayedArgs) | |
| emitCreates(true, false); | |
| // Emit constructors. | |
| auto emitCtor = [&](bool emitOpt, bool emitFake, bool emitNoArgs) { | |
| auto shouldEmitArg = [=](const std::unique_ptr<Argument> &arg) { | |
| if (emitNoArgs) | |
| return false; | |
| if (arg->isFake()) | |
| return emitFake; | |
| if (arg->isOptional()) | |
| return emitOpt; | |
| return true; | |
| }; | |
| if (Header) | |
| OS << " "; | |
| else | |
| OS << R.getName() << "Attr::"; | |
| OS << R.getName() | |
| << "Attr(ASTContext &Ctx, const AttributeCommonInfo &CommonInfo"; | |
| OS << '\n'; | |
| for (auto const &ai : Args) { | |
| if (!shouldEmitArg(ai)) | |
| continue; | |
| OS << " , "; | |
| ai->writeCtorParameters(OS); | |
| OS << "\n"; | |
| } | |
| OS << " )"; | |
| if (Header) { | |
| OS << ";\n"; | |
| return; | |
| } | |
| OS << "\n : " << SuperName << "(Ctx, CommonInfo, "; | |
| OS << "attr::" << R.getName() << ", "; | |
| // Handle different late parsing modes. | |
| OS << "/*IsLateParsed=*/"; | |
| switch (getLateAttrParseKind(&R)) { | |
| case LateAttrParseKind::Never: | |
| OS << "false"; | |
| break; | |
| case LateAttrParseKind::ExperimentalExt: | |
| // Currently no clients need to know the distinction between `Standard` | |
| // and `ExperimentalExt` so treat `ExperimentalExt` just like | |
| // `Standard` for now. | |
| case LateAttrParseKind::Standard: | |
| // Note: This is misleading. `IsLateParsed` doesn't mean the | |
| // attribute was actually late parsed. Instead it means the attribute in | |
| // `Attr.td` is marked as being late parsed. Maybe it should be called | |
| // `IsLateParseable`? | |
| OS << "true"; | |
| break; | |
| } | |
| if (Inheritable) { | |
| OS << ", " | |
| << (R.getValueAsBit("InheritEvenIfAlreadyPresent") ? "true" | |
| : "false"); | |
| } | |
| OS << ")\n"; | |
| for (auto const &ai : Args) { | |
| OS << " , "; | |
| if (!shouldEmitArg(ai)) { | |
| ai->writeCtorDefaultInitializers(OS); | |
| } else { | |
| ai->writeCtorInitializers(OS); | |
| } | |
| OS << "\n"; | |
| } | |
| if (DelayedArgs) { | |
| OS << " , "; | |
| DelayedArgs->writeCtorDefaultInitializers(OS); | |
| OS << "\n"; | |
| } | |
| OS << " {\n"; | |
| for (auto const &ai : Args) { | |
| if (!shouldEmitArg(ai)) | |
| continue; | |
| ai->writeCtorBody(OS); | |
| } | |
| OS << "}\n\n"; | |
| }; | |
| if (Header) | |
| OS << "\n // Constructors\n"; | |
| // Emit a constructor that includes all the arguments. | |
| // This is necessary for cloning. | |
| emitCtor(true, true, false); | |
| // Emit a constructor that takes all the non-fake arguments. | |
| if (HasFakeArg) | |
| emitCtor(true, false, false); | |
| // Emit a constructor that takes all the non-fake, non-optional arguments. | |
| if (HasOptArg) | |
| emitCtor(false, false, false); | |
| // Emit constructors that takes no arguments if none already exists. | |
| // This is used for delaying arguments. | |
| bool HasRequiredArgs = | |
| count_if(Args, [=](const std::unique_ptr<Argument> &arg) { | |
| return !arg->isFake() && !arg->isOptional(); | |
| }); | |
| if (DelayedArgs && HasRequiredArgs) | |
| emitCtor(false, false, true); | |
| if (Header) { | |
| OS << '\n'; | |
| OS << " " << R.getName() << "Attr *clone(ASTContext &C) const;\n"; | |
| OS << " void printPretty(raw_ostream &OS,\n" | |
| << " const PrintingPolicy &Policy) const;\n"; | |
| OS << " const char *getSpelling() const;\n"; | |
| } | |
| if (!ElideSpelling) { | |
| assert(!SemanticToSyntacticMap.empty() && "Empty semantic mapping list"); | |
| if (Header) | |
| OS << " Spelling getSemanticSpelling() const;\n"; | |
| else { | |
| OS << R.getName() << "Attr::Spelling " << R.getName() | |
| << "Attr::getSemanticSpelling() const {\n"; | |
| WriteSemanticSpellingSwitch("getAttributeSpellingListIndex()", | |
| SemanticToSyntacticMap, OS); | |
| OS << "}\n"; | |
| } | |
| } | |
| if (Header) | |
| writeAttrAccessorDefinition(R, OS); | |
| for (auto const &ai : Args) { | |
| if (Header) { | |
| ai->writeAccessors(OS); | |
| } else { | |
| ai->writeAccessorDefinitions(OS); | |
| } | |
| OS << "\n\n"; | |
| // Don't write conversion routines for fake arguments. | |
| if (ai->isFake()) continue; | |
| if (ai->isEnumArg()) | |
| static_cast<const EnumArgument *>(ai.get())->writeConversion(OS, | |
| Header); | |
| else if (ai->isVariadicEnumArg()) | |
| static_cast<const VariadicEnumArgument *>(ai.get())->writeConversion( | |
| OS, Header); | |
| } | |
| std::string FnStr = "isEquivalent(const "; | |
| FnStr += R.getName(); | |
| FnStr += "Attr &Other, StructuralEquivalenceContext &Context) const"; | |
| if (Header) { | |
| OS << " bool " << FnStr << ";\n"; | |
| } else { | |
| OS << "bool " << R.getName() << "Attr::" << FnStr << " {\n"; | |
| std::string CustomFn = R.getValueAsString("comparisonFn").str(); | |
| if (CustomFn.empty()) { | |
| if (!ElideSpelling) | |
| OS << " if (getSpelling() != Other.getSpelling()) return false;\n\n"; | |
| for (const auto &ai : Args) { | |
| OS << " if (!" << ai->emitAttrArgEqualityCheck() << ")\n"; | |
| OS << " return false;\n"; | |
| } | |
| OS << " return true;\n"; | |
| } else { | |
| OS << " return " + CustomFn + "(*this, Other, Context);\n"; | |
| } | |
| OS << "}\n\n"; | |
| } | |
| StringRef ProfileSig = "Profile(llvm::FoldingSetNodeID &ID, " | |
| "const ASTContext &Ctx) const"; | |
| if (Header) { | |
| OS << " void " << ProfileSig << ";\n"; | |
| } else { | |
| OS << "void " << R.getName() << "Attr::" << ProfileSig << " {\n"; | |
| std::string CustomFn = R.getValueAsString("profileFn").str(); | |
| if (CustomFn.empty()) { | |
| for (const auto &ai : Args) | |
| OS << " " << ai->emitAttrArgProfileCall() << ";\n"; | |
| } else { | |
| OS << " " << CustomFn << "(*this, ID, Ctx);\n"; | |
| } | |
| OS << "}\n\n"; | |
| } | |
| if (Header) { | |
| if (DelayedArgs) { | |
| DelayedArgs->writeAccessors(OS); | |
| DelayedArgs->writeSetter(OS); | |
| } | |
| OS << R.getValueAsString("AdditionalMembers"); | |
| OS << "\n\n"; | |
| OS << " static bool classof(const Attr *A) { return A->getKind() == " | |
| << "attr::" << R.getName() << "; }\n"; | |
| OS << "};\n\n"; | |
| } else { | |
| if (DelayedArgs) | |
| DelayedArgs->writeAccessorDefinitions(OS); | |
| OS << R.getName() << "Attr *" << R.getName() | |
| << "Attr::clone(ASTContext &C) const {\n"; | |
| OS << " auto *A = new (C) " << R.getName() << "Attr(C, *this"; | |
| for (auto const &ai : Args) { | |
| OS << ", "; | |
| ai->writeCloneArgs(OS); | |
| } | |
| OS << ");\n"; | |
| OS << " A->Inherited = Inherited;\n"; | |
| OS << " A->IsPackExpansion = IsPackExpansion;\n"; | |
| OS << " A->setImplicit(Implicit);\n"; | |
| if (DelayedArgs) { | |
| OS << " A->setDelayedArgs(C, "; | |
| DelayedArgs->writeCloneArgs(OS); | |
| OS << ");\n"; | |
| } | |
| OS << " return A;\n}\n\n"; | |
| writePrettyPrintFunction(R, Args, OS); | |
| writeGetSpellingFunction(R, OS); | |
| } | |
| } | |
| } | |
| // Emits the class definitions for attributes. | |
| void clang::EmitClangAttrClass(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Attribute classes' definitions", OS, Records); | |
| OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n"; | |
| OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n"; | |
| emitAttributes(Records, OS, true); | |
| OS << "#endif // LLVM_CLANG_ATTR_CLASSES_INC\n"; | |
| } | |
| static void emitEquivalenceFunction(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "bool Attr::isEquivalent(const Attr &Other, " | |
| "StructuralEquivalenceContext &Context) const {\n"; | |
| OS << "if (getKind() != Other.getKind()) return false;\n\n"; | |
| OS << " switch (getKind()) {\n"; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| OS << " case attr::" << R.getName() << ":\n"; | |
| OS << " return cast<" << R.getName() << "Attr>(this)->isEquivalent(cast<" | |
| << R.getName() << "Attr>(Other), Context);\n"; | |
| } | |
| OS << " }\n"; | |
| OS << " llvm_unreachable(\"Unexpected attribute kind!\");\n"; | |
| OS << "}\n\n"; | |
| } | |
| static void emitProfileFunction(const RecordKeeper &Records, raw_ostream &OS) { | |
| OS << "void Attr::Profile(llvm::FoldingSetNodeID &ID, " | |
| "const ASTContext &Ctx) const {\n"; | |
| OS << " switch (getKind()) {\n"; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| OS << " case attr::" << R.getName() << ":\n"; | |
| OS << " return cast<" << R.getName() | |
| << "Attr>(this)->Profile(ID, Ctx);\n"; | |
| } | |
| OS << " }\n"; | |
| OS << " llvm_unreachable(\"Unexpected attribute kind!\");\n"; | |
| OS << "}\n\n"; | |
| } | |
| // Emits the class method definitions for attributes. | |
| void clang::EmitClangAttrImpl(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Attribute classes' member function definitions", OS, | |
| Records); | |
| emitAttributes(Records, OS, false); | |
| // Instead of relying on virtual dispatch we just create a huge dispatch | |
| // switch. This is both smaller and faster than virtual functions. | |
| auto EmitFunc = [&](const char *Method) { | |
| OS << " switch (getKind()) {\n"; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| OS << " case attr::" << R.getName() << ":\n"; | |
| OS << " return cast<" << R.getName() << "Attr>(this)->" << Method | |
| << ";\n"; | |
| } | |
| OS << " }\n"; | |
| OS << " llvm_unreachable(\"Unexpected attribute kind!\");\n"; | |
| OS << "}\n\n"; | |
| }; | |
| OS << "const char *Attr::getSpelling() const {\n"; | |
| EmitFunc("getSpelling()"); | |
| OS << "Attr *Attr::clone(ASTContext &C) const {\n"; | |
| EmitFunc("clone(C)"); | |
| OS << "void Attr::printPretty(raw_ostream &OS, " | |
| "const PrintingPolicy &Policy) const {\n"; | |
| EmitFunc("printPretty(OS, Policy)"); | |
| emitEquivalenceFunction(Records, OS); | |
| emitProfileFunction(Records, OS); | |
| } | |
| static void emitAttrList(raw_ostream &OS, StringRef Class, | |
| ArrayRef<const Record *> AttrList) { | |
| for (auto Cur : AttrList) { | |
| OS << Class << "(" << Cur->getName() << ")\n"; | |
| } | |
| } | |
| // Determines if an attribute has a Pragma spelling. | |
| static bool AttrHasPragmaSpelling(const Record *R) { | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*R); | |
| return any_of(Spellings, [](const FlattenedSpelling &S) { | |
| return S.variety() == "Pragma"; | |
| }); | |
| } | |
| namespace { | |
| struct AttrClassDescriptor { | |
| const char * const MacroName; | |
| const char * const TableGenName; | |
| }; | |
| } // end anonymous namespace | |
| static const AttrClassDescriptor AttrClassDescriptors[] = { | |
| {"ATTR", "Attr"}, | |
| {"TYPE_ATTR", "TypeAttr"}, | |
| {"STMT_ATTR", "StmtAttr"}, | |
| {"DECL_OR_STMT_ATTR", "DeclOrStmtAttr"}, | |
| {"INHERITABLE_ATTR", "InheritableAttr"}, | |
| {"DECL_OR_TYPE_ATTR", "DeclOrTypeAttr"}, | |
| {"INHERITABLE_PARAM_ATTR", "InheritableParamAttr"}, | |
| {"INHERITABLE_PARAM_OR_STMT_ATTR", "InheritableParamOrStmtAttr"}, | |
| {"PARAMETER_ABI_ATTR", "ParameterABIAttr"}, | |
| {"HLSL_ANNOTATION_ATTR", "HLSLAnnotationAttr"}, | |
| {"HLSL_SEMANTIC_ATTR", "HLSLSemanticBaseAttr"}}; | |
| static void emitDefaultDefine(raw_ostream &OS, StringRef name, | |
| const char *superName) { | |
| OS << "#ifndef " << name << "\n"; | |
| OS << "#define " << name << "(NAME) "; | |
| if (superName) OS << superName << "(NAME)"; | |
| OS << "\n#endif\n\n"; | |
| } | |
| namespace { | |
| /// A class of attributes. | |
| struct AttrClass { | |
| const AttrClassDescriptor &Descriptor; | |
| const Record *TheRecord; | |
| AttrClass *SuperClass = nullptr; | |
| std::vector<AttrClass*> SubClasses; | |
| std::vector<const Record *> Attrs; | |
| AttrClass(const AttrClassDescriptor &Descriptor, const Record *R) | |
| : Descriptor(Descriptor), TheRecord(R) {} | |
| void emitDefaultDefines(raw_ostream &OS) const { | |
| // Default the macro unless this is a root class (i.e. Attr). | |
| if (SuperClass) { | |
| emitDefaultDefine(OS, Descriptor.MacroName, | |
| SuperClass->Descriptor.MacroName); | |
| } | |
| } | |
| void emitUndefs(raw_ostream &OS) const { | |
| OS << "#undef " << Descriptor.MacroName << "\n"; | |
| } | |
| void emitAttrList(raw_ostream &OS) const { | |
| for (auto SubClass : SubClasses) { | |
| SubClass->emitAttrList(OS); | |
| } | |
| ::emitAttrList(OS, Descriptor.MacroName, Attrs); | |
| } | |
| void classifyAttrOnRoot(const Record *Attr) { | |
| bool result = classifyAttr(Attr); | |
| assert(result && "failed to classify on root"); (void) result; | |
| } | |
| void emitAttrRange(raw_ostream &OS) const { | |
| OS << "ATTR_RANGE(" << Descriptor.TableGenName | |
| << ", " << getFirstAttr()->getName() | |
| << ", " << getLastAttr()->getName() << ")\n"; | |
| } | |
| private: | |
| bool classifyAttr(const Record *Attr) { | |
| // Check all the subclasses. | |
| for (auto SubClass : SubClasses) { | |
| if (SubClass->classifyAttr(Attr)) | |
| return true; | |
| } | |
| // It's not more specific than this class, but it might still belong here. | |
| if (Attr->isSubClassOf(TheRecord)) { | |
| Attrs.push_back(Attr); | |
| return true; | |
| } | |
| return false; | |
| } | |
| const Record *getFirstAttr() const { | |
| if (!SubClasses.empty()) | |
| return SubClasses.front()->getFirstAttr(); | |
| return Attrs.front(); | |
| } | |
| const Record *getLastAttr() const { | |
| if (!Attrs.empty()) | |
| return Attrs.back(); | |
| return SubClasses.back()->getLastAttr(); | |
| } | |
| }; | |
| /// The entire hierarchy of attribute classes. | |
| class AttrClassHierarchy { | |
| std::vector<std::unique_ptr<AttrClass>> Classes; | |
| public: | |
| AttrClassHierarchy(const RecordKeeper &Records) { | |
| // Find records for all the classes. | |
| for (auto &Descriptor : AttrClassDescriptors) { | |
| const Record *ClassRecord = Records.getClass(Descriptor.TableGenName); | |
| AttrClass *Class = new AttrClass(Descriptor, ClassRecord); | |
| Classes.emplace_back(Class); | |
| } | |
| // Link up the hierarchy. | |
| for (auto &Class : Classes) { | |
| if (AttrClass *SuperClass = findSuperClass(Class->TheRecord)) { | |
| Class->SuperClass = SuperClass; | |
| SuperClass->SubClasses.push_back(Class.get()); | |
| } | |
| } | |
| #ifndef NDEBUG | |
| for (auto i = Classes.begin(), e = Classes.end(); i != e; ++i) { | |
| assert((i == Classes.begin()) == ((*i)->SuperClass == nullptr) && | |
| "only the first class should be a root class!"); | |
| } | |
| #endif | |
| } | |
| void emitDefaultDefines(raw_ostream &OS) const { | |
| for (auto &Class : Classes) { | |
| Class->emitDefaultDefines(OS); | |
| } | |
| } | |
| void emitUndefs(raw_ostream &OS) const { | |
| for (auto &Class : Classes) { | |
| Class->emitUndefs(OS); | |
| } | |
| } | |
| void emitAttrLists(raw_ostream &OS) const { | |
| // Just start from the root class. | |
| Classes[0]->emitAttrList(OS); | |
| } | |
| void emitAttrRanges(raw_ostream &OS) const { | |
| for (auto &Class : Classes) | |
| Class->emitAttrRange(OS); | |
| } | |
| void classifyAttr(const Record *Attr) { | |
| // Add the attribute to the root class. | |
| Classes[0]->classifyAttrOnRoot(Attr); | |
| } | |
| private: | |
| AttrClass *findClassByRecord(const Record *R) const { | |
| for (auto &Class : Classes) { | |
| if (Class->TheRecord == R) | |
| return Class.get(); | |
| } | |
| return nullptr; | |
| } | |
| AttrClass *findSuperClass(const Record *R) const { | |
| // TableGen flattens the superclass list, so we just need to walk it | |
| // in reverse. | |
| std::vector<const Record *> SuperClasses = R->getSuperClasses(); | |
| for (const Record *R : reverse(SuperClasses)) { | |
| if (AttrClass *SuperClass = findClassByRecord(R)) | |
| return SuperClass; | |
| } | |
| return nullptr; | |
| } | |
| }; | |
| } // end anonymous namespace | |
| namespace clang { | |
| // Emits the enumeration list for attributes. | |
| void EmitClangAttrList(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("List of all attributes that Clang recognizes", OS, | |
| Records); | |
| AttrClassHierarchy Hierarchy(Records); | |
| // Add defaulting macro definitions. | |
| Hierarchy.emitDefaultDefines(OS); | |
| emitDefaultDefine(OS, "PRAGMA_SPELLING_ATTR", nullptr); | |
| std::vector<const Record *> PragmaAttrs; | |
| for (auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| if (!Attr->getValueAsBit("ASTNode")) | |
| continue; | |
| // Add the attribute to the ad-hoc groups. | |
| if (AttrHasPragmaSpelling(Attr)) | |
| PragmaAttrs.push_back(Attr); | |
| // Place it in the hierarchy. | |
| Hierarchy.classifyAttr(Attr); | |
| } | |
| // Emit the main attribute list. | |
| Hierarchy.emitAttrLists(OS); | |
| // Emit the ad hoc groups. | |
| emitAttrList(OS, "PRAGMA_SPELLING_ATTR", PragmaAttrs); | |
| // Emit the attribute ranges. | |
| OS << "#ifdef ATTR_RANGE\n"; | |
| Hierarchy.emitAttrRanges(OS); | |
| OS << "#undef ATTR_RANGE\n"; | |
| OS << "#endif\n"; | |
| Hierarchy.emitUndefs(OS); | |
| OS << "#undef PRAGMA_SPELLING_ATTR\n"; | |
| } | |
| // Emits the enumeration list for attributes. | |
| void EmitClangAttrSubjectMatchRuleList(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| emitSourceFileHeader( | |
| "List of all attribute subject matching rules that Clang recognizes", OS, | |
| Records); | |
| PragmaClangAttributeSupport &PragmaAttributeSupport = | |
| getPragmaAttributeSupport(Records); | |
| emitDefaultDefine(OS, "ATTR_MATCH_RULE", nullptr); | |
| PragmaAttributeSupport.emitMatchRuleList(OS); | |
| OS << "#undef ATTR_MATCH_RULE\n"; | |
| } | |
| // Emits the code to read an attribute from a precompiled header. | |
| void EmitClangAttrPCHRead(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Attribute deserialization code", OS, Records); | |
| const Record *InhClass = Records.getClass("InheritableAttr"); | |
| std::vector<const Record *> ArgRecords; | |
| std::vector<std::unique_ptr<Argument>> Args; | |
| std::unique_ptr<VariadicExprArgument> DelayedArgs; | |
| OS << " switch (Kind) {\n"; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| OS << " case attr::" << R.getName() << ": {\n"; | |
| if (R.isSubClassOf(InhClass)) | |
| OS << " bool isInherited = Record.readInt();\n"; | |
| OS << " bool isImplicit = Record.readInt();\n"; | |
| OS << " bool isPackExpansion = Record.readInt();\n"; | |
| DelayedArgs = nullptr; | |
| if (Attr->getValueAsBit("AcceptsExprPack")) { | |
| DelayedArgs = | |
| std::make_unique<VariadicExprArgument>("DelayedArgs", R.getName()); | |
| DelayedArgs->writePCHReadDecls(OS); | |
| } | |
| ArgRecords = R.getValueAsListOfDefs("Args"); | |
| Args.clear(); | |
| for (const auto *Arg : ArgRecords) { | |
| Args.emplace_back(createArgument(*Arg, R.getName())); | |
| Args.back()->writePCHReadDecls(OS); | |
| } | |
| OS << " New = new (Context) " << R.getName() << "Attr(Context, Info"; | |
| for (auto const &ri : Args) { | |
| OS << ", "; | |
| ri->writePCHReadArgs(OS); | |
| } | |
| OS << ");\n"; | |
| if (R.isSubClassOf(InhClass)) | |
| OS << " cast<InheritableAttr>(New)->setInherited(isInherited);\n"; | |
| OS << " New->setImplicit(isImplicit);\n"; | |
| OS << " New->setPackExpansion(isPackExpansion);\n"; | |
| if (DelayedArgs) { | |
| OS << " cast<" << R.getName() | |
| << "Attr>(New)->setDelayedArgs(Context, "; | |
| DelayedArgs->writePCHReadArgs(OS); | |
| OS << ");\n"; | |
| } | |
| if (Attr->getValueAsBit("HasCustomSerialization")) | |
| OS << " read" << R.getName() << "Attr(cast<" << R.getName() | |
| << "Attr>(New));\n"; | |
| OS << " break;\n"; | |
| OS << " }\n"; | |
| } | |
| OS << " }\n"; | |
| } | |
| // Emits the code to write an attribute to a precompiled header. | |
| void EmitClangAttrPCHWrite(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Attribute serialization code", OS, Records); | |
| const Record *InhClass = Records.getClass("InheritableAttr"); | |
| OS << " switch (A->getKind()) {\n"; | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| OS << " case attr::" << R.getName() << ": {\n"; | |
| std::vector<const Record *> Args = R.getValueAsListOfDefs("Args"); | |
| if (R.isSubClassOf(InhClass) || !Args.empty()) | |
| OS << " const auto *SA = cast<" << R.getName() | |
| << "Attr>(A);\n"; | |
| if (R.isSubClassOf(InhClass)) | |
| OS << " Record.push_back(SA->isInherited());\n"; | |
| OS << " Record.push_back(A->isImplicit());\n"; | |
| OS << " Record.push_back(A->isPackExpansion());\n"; | |
| if (Attr->getValueAsBit("AcceptsExprPack")) | |
| VariadicExprArgument("DelayedArgs", R.getName()).writePCHWrite(OS); | |
| for (const auto *Arg : Args) | |
| createArgument(*Arg, R.getName())->writePCHWrite(OS); | |
| if (Attr->getValueAsBit("HasCustomSerialization")) | |
| OS << " Record.Add" << R.getName() << "Attr(SA);\n"; | |
| OS << " break;\n"; | |
| OS << " }\n"; | |
| } | |
| OS << " }\n"; | |
| } | |
| } // namespace clang | |
| // Helper function for GenerateTargetSpecificAttrChecks that alters the 'Test' | |
| // parameter with only a single check type, if applicable. | |
| static bool GenerateTargetSpecificAttrCheck(const Record *R, std::string &Test, | |
| std::string *FnName, | |
| StringRef ListName, | |
| StringRef CheckAgainst, | |
| StringRef Scope) { | |
| if (!R->isValueUnset(ListName)) { | |
| Test += " && ("; | |
| std::vector<StringRef> Items = R->getValueAsListOfStrings(ListName); | |
| for (auto I = Items.begin(), E = Items.end(); I != E; ++I) { | |
| StringRef Part = *I; | |
| Test += CheckAgainst; | |
| Test += " == "; | |
| Test += Scope; | |
| Test += Part; | |
| if (I + 1 != E) | |
| Test += " || "; | |
| if (FnName) | |
| *FnName += Part; | |
| } | |
| Test += ")"; | |
| return true; | |
| } | |
| return false; | |
| } | |
| // Generate a conditional expression to check if the current target satisfies | |
| // the conditions for a TargetSpecificAttr record, and append the code for | |
| // those checks to the Test string. If the FnName string pointer is non-null, | |
| // append a unique suffix to distinguish this set of target checks from other | |
| // TargetSpecificAttr records. | |
| static bool GenerateTargetSpecificAttrChecks(const Record *R, | |
| std::vector<StringRef> &Arches, | |
| std::string &Test, | |
| std::string *FnName) { | |
| bool AnyTargetChecks = false; | |
| // It is assumed that there will be an Triple object | |
| // named "T" and a TargetInfo object named "Target" within | |
| // scope that can be used to determine whether the attribute exists in | |
| // a given target. | |
| Test += "true"; | |
| // If one or more architectures is specified, check those. Arches are handled | |
| // differently because GenerateTargetRequirements needs to combine the list | |
| // with ParseKind. | |
| if (!Arches.empty()) { | |
| AnyTargetChecks = true; | |
| Test += " && ("; | |
| for (auto I = Arches.begin(), E = Arches.end(); I != E; ++I) { | |
| StringRef Part = *I; | |
| Test += "T.getArch() == llvm::Triple::"; | |
| Test += Part; | |
| if (I + 1 != E) | |
| Test += " || "; | |
| if (FnName) | |
| *FnName += Part; | |
| } | |
| Test += ")"; | |
| } | |
| // If the attribute is specific to particular OSes, check those. | |
| AnyTargetChecks |= GenerateTargetSpecificAttrCheck( | |
| R, Test, FnName, "OSes", "T.getOS()", "llvm::Triple::"); | |
| // If one or more object formats is specified, check those. | |
| AnyTargetChecks |= | |
| GenerateTargetSpecificAttrCheck(R, Test, FnName, "ObjectFormats", | |
| "T.getObjectFormat()", "llvm::Triple::"); | |
| // If custom code is specified, emit it. | |
| StringRef Code = R->getValueAsString("CustomCode"); | |
| if (!Code.empty()) { | |
| AnyTargetChecks = true; | |
| Test += " && ("; | |
| Test += Code; | |
| Test += ")"; | |
| } | |
| return AnyTargetChecks; | |
| } | |
| static void GenerateHasAttrSpellingStringSwitch( | |
| ArrayRef<std::pair<const Record *, FlattenedSpelling>> Attrs, | |
| raw_ostream &OS, StringRef Variety, StringRef Scope = "") { | |
| // It turns out that there are duplicate records for a given spelling. This | |
| // map combines matching test strings using '||'. For example, if there are | |
| // three conditions A, B, and C, the final result will be: A || B || C. | |
| llvm::StringMap<std::string> TestStringMap; | |
| for (const auto &[Attr, Spelling] : Attrs) { | |
| // C++11-style attributes have specific version information associated with | |
| // them. If the attribute has no scope, the version information must not | |
| // have the default value (1), as that's incorrect. Instead, the unscoped | |
| // attribute version information should be taken from the SD-6 standing | |
| // document, which can be found at: | |
| // https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations | |
| // | |
| // C23-style attributes have the same kind of version information | |
| // associated with them. The unscoped attribute version information should | |
| // be taken from the specification of the attribute in the C Standard. | |
| // | |
| // Clang-specific attributes have the same kind of version information | |
| // associated with them. This version is typically the default value (1). | |
| // These version values are clang-specific and should typically be | |
| // incremented once the attribute changes its syntax and/or semantics in a | |
| // a way that is impactful to the end user. | |
| int Version = 1; | |
| assert(Spelling.variety() == Variety); | |
| std::string Name = ""; | |
| if (Spelling.nameSpace().empty() || Scope == Spelling.nameSpace()) { | |
| Name = Spelling.name(); | |
| Version = static_cast<int>( | |
| Spelling.getSpellingRecord().getValueAsInt("Version")); | |
| // Verify that explicitly specified CXX11 and C23 spellings (i.e. | |
| // not inferred from Clang/GCC spellings) have a version that's | |
| // different from the default (1). | |
| bool RequiresValidVersion = | |
| (Variety == "CXX11" || Variety == "C23") && | |
| Spelling.getSpellingRecord().getValueAsString("Variety") == Variety; | |
| if (RequiresValidVersion && Scope.empty() && Version == 1) | |
| PrintError(Spelling.getSpellingRecord().getLoc(), | |
| "Standard attributes must have " | |
| "valid version information."); | |
| } | |
| std::string Test; | |
| if (Attr->isSubClassOf("TargetSpecificAttr")) { | |
| const Record *R = Attr->getValueAsDef("Target"); | |
| std::vector<StringRef> Arches = R->getValueAsListOfStrings("Arches"); | |
| GenerateTargetSpecificAttrChecks(R, Arches, Test, nullptr); | |
| } else if (!Attr->getValueAsListOfDefs("TargetSpecificSpellings").empty()) { | |
| // Add target checks if this spelling is target-specific. | |
| for (const auto &TargetSpelling : | |
| Attr->getValueAsListOfDefs("TargetSpecificSpellings")) { | |
| // Find spelling that matches current scope and name. | |
| for (const auto &Spelling : GetFlattenedSpellings(*TargetSpelling)) { | |
| if (Scope == Spelling.nameSpace() && Name == Spelling.name()) { | |
| const Record *Target = TargetSpelling->getValueAsDef("Target"); | |
| std::vector<StringRef> Arches = | |
| Target->getValueAsListOfStrings("Arches"); | |
| GenerateTargetSpecificAttrChecks(Target, Arches, Test, | |
| /*FnName=*/nullptr); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| std::string TestStr = | |
| !Test.empty() ? '(' + Test + " ? " + itostr(Version) + " : 0" + ')' | |
| : '(' + itostr(Version) + ')'; | |
| if (Scope.empty() || Scope == Spelling.nameSpace()) { | |
| if (TestStringMap.contains(Spelling.name()) && | |
| TestStringMap[Spelling.name()] != TestStr) | |
| TestStringMap[Spelling.name()] += " || " + TestStr; | |
| else | |
| TestStringMap[Spelling.name()] = TestStr; | |
| } | |
| } | |
| // Create the actual string switch statement after all the attributes have | |
| // been parsed. | |
| for (auto &Entry : TestStringMap) { | |
| OS << " .Case(\"" << Entry.getKey() << "\", " << Entry.getValue() | |
| << ")\n"; | |
| } | |
| OS << " .Default(0);\n"; | |
| } | |
| namespace clang { | |
| // Emits list of regular keyword attributes with info about their arguments. | |
| void EmitClangRegularKeywordAttributeInfo(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| emitSourceFileHeader( | |
| "A list of regular keyword attributes generated from the attribute" | |
| " definitions", | |
| OS); | |
| // Assume for now that the same token is not used in multiple regular | |
| // keyword attributes. | |
| for (auto *R : Records.getAllDerivedDefinitions("Attr")) | |
| for (const auto &S : GetFlattenedSpellings(*R)) { | |
| if (!isRegularKeywordAttribute(S)) | |
| continue; | |
| std::vector<const Record *> Args = R->getValueAsListOfDefs("Args"); | |
| bool HasArgs = any_of( | |
| Args, [](const Record *Arg) { return !Arg->getValueAsBit("Fake"); }); | |
| OS << "KEYWORD_ATTRIBUTE(" | |
| << S.getSpellingRecord().getValueAsString("Name") << ", " | |
| << (HasArgs ? "true" : "false") << ", )\n"; | |
| } | |
| OS << "#undef KEYWORD_ATTRIBUTE\n"; | |
| } | |
| void EmitCXX11AttributeInfo(const RecordKeeper &Records, raw_ostream &OS) { | |
| OS << "#if defined(CXX11_ATTR_ARGS_INFO)\n"; | |
| for (auto *R : Records.getAllDerivedDefinitions("Attr")) { | |
| for (const FlattenedSpelling &SI : GetFlattenedSpellings(*R)) { | |
| if (SI.variety() == "CXX11" && SI.nameSpace().empty()) { | |
| unsigned RequiredArgs = 0; | |
| unsigned OptionalArgs = 0; | |
| for (const auto *Arg : R->getValueAsListOfDefs("Args")) { | |
| if (Arg->getValueAsBit("Fake")) | |
| continue; | |
| if (Arg->getValueAsBit("Optional")) | |
| OptionalArgs++; | |
| else | |
| RequiredArgs++; | |
| } | |
| OS << ".Case(\"" << SI.getSpellingRecord().getValueAsString("Name") | |
| << "\"," | |
| << "AttributeCommonInfo::AttrArgsInfo::" | |
| << (RequiredArgs ? "Required" | |
| : OptionalArgs ? "Optional" | |
| : "None") | |
| << ")" | |
| << "\n"; | |
| } | |
| } | |
| } | |
| OS << "#endif // CXX11_ATTR_ARGS_INFO\n"; | |
| } | |
| // Emits the list of spellings for attributes. | |
| void EmitClangAttrHasAttrImpl(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Code to implement the __has_attribute logic", OS, | |
| Records); | |
| // Separate all of the attributes out into four group: generic, C++11, GNU, | |
| // and declspecs. Then generate a big switch statement for each of them. | |
| using PairTy = std::pair<const Record *, FlattenedSpelling>; | |
| std::vector<PairTy> Declspec, Microsoft, GNU, Pragma, HLSLAnnotation; | |
| std::map<StringRef, std::vector<PairTy>> CXX, C23; | |
| // Walk over the list of all attributes, and split them out based on the | |
| // spelling variety. | |
| for (auto *R : Records.getAllDerivedDefinitions("Attr")) { | |
| for (const FlattenedSpelling &SI : GetFlattenedSpellings(*R)) { | |
| StringRef Variety = SI.variety(); | |
| if (Variety == "GNU") | |
| GNU.emplace_back(R, SI); | |
| else if (Variety == "Declspec") | |
| Declspec.emplace_back(R, SI); | |
| else if (Variety == "Microsoft") | |
| Microsoft.emplace_back(R, SI); | |
| else if (Variety == "CXX11") | |
| CXX[SI.nameSpace()].emplace_back(R, SI); | |
| else if (Variety == "C23") | |
| C23[SI.nameSpace()].emplace_back(R, SI); | |
| else if (Variety == "Pragma") | |
| Pragma.emplace_back(R, SI); | |
| else if (Variety == "HLSLAnnotation") | |
| HLSLAnnotation.emplace_back(R, SI); | |
| } | |
| } | |
| OS << "const llvm::Triple &T = Target.getTriple();\n"; | |
| OS << "switch (Syntax) {\n"; | |
| OS << "case AttributeCommonInfo::Syntax::AS_GNU:\n"; | |
| OS << " return llvm::StringSwitch<int>(Name)\n"; | |
| GenerateHasAttrSpellingStringSwitch(GNU, OS, "GNU"); | |
| OS << "case AttributeCommonInfo::Syntax::AS_Declspec:\n"; | |
| OS << " return llvm::StringSwitch<int>(Name)\n"; | |
| GenerateHasAttrSpellingStringSwitch(Declspec, OS, "Declspec"); | |
| OS << "case AttributeCommonInfo::Syntax::AS_Microsoft:\n"; | |
| OS << " return llvm::StringSwitch<int>(Name)\n"; | |
| GenerateHasAttrSpellingStringSwitch(Microsoft, OS, "Microsoft"); | |
| OS << "case AttributeCommonInfo::Syntax::AS_Pragma:\n"; | |
| OS << " return llvm::StringSwitch<int>(Name)\n"; | |
| GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma"); | |
| OS << "case AttributeCommonInfo::Syntax::AS_HLSLAnnotation:\n"; | |
| OS << " return llvm::StringSwitch<int>(Name)\n"; | |
| GenerateHasAttrSpellingStringSwitch(HLSLAnnotation, OS, "HLSLAnnotation"); | |
| auto fn = [&OS](StringRef Spelling, | |
| const std::map<StringRef, std::vector<PairTy>> &Map) { | |
| OS << "case AttributeCommonInfo::Syntax::AS_" << Spelling << ": {\n"; | |
| // C++11-style attributes are further split out based on the Scope. | |
| ListSeparator LS(" else "); | |
| for (const auto &[Scope, List] : Map) { | |
| OS << LS; | |
| OS << "if (ScopeName == \"" << Scope << "\") {\n"; | |
| OS << " return llvm::StringSwitch<int>(Name)\n"; | |
| GenerateHasAttrSpellingStringSwitch(List, OS, Spelling, Scope); | |
| OS << "}"; | |
| } | |
| OS << "\n} break;\n"; | |
| }; | |
| fn("CXX11", CXX); | |
| fn("C23", C23); | |
| OS << "case AttributeCommonInfo::Syntax::AS_Keyword:\n"; | |
| OS << "case AttributeCommonInfo::Syntax::AS_ContextSensitiveKeyword:\n"; | |
| OS << " llvm_unreachable(\"hasAttribute not supported for keyword\");\n"; | |
| OS << " return 0;\n"; | |
| OS << "case AttributeCommonInfo::Syntax::AS_Implicit:\n"; | |
| OS << " llvm_unreachable (\"hasAttribute not supported for " | |
| "AS_Implicit\");\n"; | |
| OS << " return 0;\n"; | |
| OS << "}\n"; | |
| } | |
| void EmitClangAttrSpellingListIndex(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| emitSourceFileHeader("Code to translate different attribute spellings into " | |
| "internal identifiers", | |
| OS, Records); | |
| OS << " switch (getParsedKind()) {\n"; | |
| OS << " case IgnoredAttribute:\n"; | |
| OS << " case UnknownAttribute:\n"; | |
| OS << " case NoSemaHandlerAttribute:\n"; | |
| OS << " llvm_unreachable(\"Ignored/unknown shouldn't get here\");\n"; | |
| ParsedAttrMap Attrs = getParsedAttrList(Records); | |
| for (const auto &I : Attrs) { | |
| const Record &R = *I.second; | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); | |
| OS << " case AT_" << I.first << ": {\n"; | |
| // If there are none or one spelling to check, resort to the default | |
| // behavior of returning index as 0. | |
| if (Spellings.size() <= 1) { | |
| OS << " return 0;\n" | |
| << " break;\n" | |
| << " }\n"; | |
| continue; | |
| } | |
| std::vector<StringRef> Names; | |
| llvm::transform(Spellings, std::back_inserter(Names), | |
| [](const FlattenedSpelling &FS) { return FS.name(); }); | |
| llvm::sort(Names); | |
| Names.erase(llvm::unique(Names), Names.end()); | |
| for (const auto &[Idx, FS] : enumerate(Spellings)) { | |
| OS << " if ("; | |
| if (Names.size() > 1) { | |
| SmallVector<StringRef, 6> SameLenNames; | |
| StringRef FSName = FS.name(); | |
| llvm::copy_if( | |
| Names, std::back_inserter(SameLenNames), | |
| [&](StringRef N) { return N.size() == FSName.size(); }); | |
| if (SameLenNames.size() == 1) { | |
| OS << "Name.size() == " << FS.name().size() << " && "; | |
| } else { | |
| // FIXME: We currently fall back to comparing entire strings if there | |
| // are 2 or more spelling names with the same length. This can be | |
| // optimized to check only for the the first differing character | |
| // between them instead. | |
| OS << "Name == \"" << FS.name() << "\"" | |
| << " && "; | |
| } | |
| } | |
| OS << "getSyntax() == AttributeCommonInfo::AS_" << FS.variety() | |
| << " && ComputedScope == "; | |
| if (FS.nameSpace() == "") | |
| OS << "AttributeCommonInfo::Scope::NONE"; | |
| else | |
| OS << "AttributeCommonInfo::Scope::" + FS.nameSpace().upper(); | |
| OS << ")\n" | |
| << " return " << Idx << ";\n"; | |
| } | |
| OS << " break;\n" | |
| << " }\n"; | |
| } | |
| OS << " }\n" | |
| << " return 0;\n"; | |
| } | |
| // Emits code used by RecursiveASTVisitor to visit attributes | |
| void EmitClangAttrASTVisitor(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Used by RecursiveASTVisitor to visit attributes.", OS, | |
| Records); | |
| // Write method declarations for Traverse* methods. | |
| // We emit this here because we only generate methods for attributes that | |
| // are declared as ASTNodes. | |
| OS << "#ifdef ATTR_VISITOR_DECLS_ONLY\n\n"; | |
| ArrayRef<const Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); | |
| for (const auto *Attr : Attrs) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| OS << " bool Traverse" | |
| << R.getName() << "Attr(" << R.getName() << "Attr *A);\n"; | |
| OS << " bool Visit" | |
| << R.getName() << "Attr(" << R.getName() << "Attr *A) {\n" | |
| << " return true; \n" | |
| << " }\n"; | |
| } | |
| OS << "\n#else // ATTR_VISITOR_DECLS_ONLY\n\n"; | |
| // Write individual Traverse* methods for each attribute class. | |
| for (const auto *Attr : Attrs) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| OS << "template <typename Derived>\n" | |
| << "bool VISITORCLASS<Derived>::Traverse" | |
| << R.getName() << "Attr(" << R.getName() << "Attr *A) {\n" | |
| << " if (!getDerived().VisitAttr(A))\n" | |
| << " return false;\n" | |
| << " if (!getDerived().Visit" << R.getName() << "Attr(A))\n" | |
| << " return false;\n"; | |
| for (const auto *Arg : R.getValueAsListOfDefs("Args")) | |
| createArgument(*Arg, R.getName())->writeASTVisitorTraversal(OS); | |
| if (Attr->getValueAsBit("AcceptsExprPack")) | |
| VariadicExprArgument("DelayedArgs", R.getName()) | |
| .writeASTVisitorTraversal(OS); | |
| OS << " return true;\n"; | |
| OS << "}\n\n"; | |
| } | |
| // Write generic Traverse routine | |
| OS << "template <typename Derived>\n" | |
| << "bool VISITORCLASS<Derived>::TraverseAttr(Attr *A) {\n" | |
| << " if (!A)\n" | |
| << " return true;\n" | |
| << "\n" | |
| << " switch (A->getKind()) {\n"; | |
| for (const auto *Attr : Attrs) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| OS << " case attr::" << R.getName() << ":\n" | |
| << " return getDerived().Traverse" << R.getName() << "Attr(" | |
| << "cast<" << R.getName() << "Attr>(A));\n"; | |
| } | |
| OS << " }\n"; // end switch | |
| OS << " llvm_unreachable(\"bad attribute kind\");\n"; | |
| OS << "}\n"; // end function | |
| OS << "#endif // ATTR_VISITOR_DECLS_ONLY\n"; | |
| } | |
| static void | |
| EmitClangAttrTemplateInstantiateHelper(ArrayRef<const Record *> Attrs, | |
| raw_ostream &OS, bool AppliesToDecl) { | |
| OS << " switch (At->getKind()) {\n"; | |
| for (const auto *Attr : Attrs) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| OS << " case attr::" << R.getName() << ": {\n"; | |
| bool ShouldClone = R.getValueAsBit("Clone") && | |
| (!AppliesToDecl || | |
| R.getValueAsBit("MeaningfulToClassTemplateDefinition")); | |
| if (!ShouldClone) { | |
| OS << " return nullptr;\n"; | |
| OS << " }\n"; | |
| continue; | |
| } | |
| OS << " const auto *A = cast<" | |
| << R.getName() << "Attr>(At);\n"; | |
| bool TDependent = R.getValueAsBit("TemplateDependent"); | |
| if (!TDependent) { | |
| OS << " return A->clone(C);\n"; | |
| OS << " }\n"; | |
| continue; | |
| } | |
| std::vector<const Record *> ArgRecords = R.getValueAsListOfDefs("Args"); | |
| std::vector<std::unique_ptr<Argument>> Args; | |
| Args.reserve(ArgRecords.size()); | |
| for (const auto *ArgRecord : ArgRecords) | |
| Args.emplace_back(createArgument(*ArgRecord, R.getName())); | |
| for (auto const &ai : Args) | |
| ai->writeTemplateInstantiation(OS); | |
| OS << " return new (C) " << R.getName() << "Attr(C, *A"; | |
| for (auto const &ai : Args) { | |
| OS << ", "; | |
| ai->writeTemplateInstantiationArgs(OS); | |
| } | |
| OS << ");\n" | |
| << " }\n"; | |
| } | |
| OS << " } // end switch\n" | |
| << " llvm_unreachable(\"Unknown attribute!\");\n" | |
| << " return nullptr;\n"; | |
| } | |
| // Emits code to instantiate dependent attributes on templates. | |
| void EmitClangAttrTemplateInstantiate(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| emitSourceFileHeader("Template instantiation code for attributes", OS, | |
| Records); | |
| ArrayRef<const Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); | |
| OS << "namespace clang {\n" | |
| << "namespace sema {\n\n" | |
| << "Attr *instantiateTemplateAttribute(const Attr *At, ASTContext &C, " | |
| << "Sema &S,\n" | |
| << " const MultiLevelTemplateArgumentList &TemplateArgs) {\n"; | |
| EmitClangAttrTemplateInstantiateHelper(Attrs, OS, /*AppliesToDecl*/false); | |
| OS << "}\n\n" | |
| << "Attr *instantiateTemplateAttributeForDecl(const Attr *At,\n" | |
| << " ASTContext &C, Sema &S,\n" | |
| << " const MultiLevelTemplateArgumentList &TemplateArgs) {\n"; | |
| EmitClangAttrTemplateInstantiateHelper(Attrs, OS, /*AppliesToDecl*/true); | |
| OS << "}\n\n" | |
| << "} // end namespace sema\n" | |
| << "} // end namespace clang\n"; | |
| } | |
| // Emits the list of parsed attributes. | |
| void EmitClangAttrParsedAttrList(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("List of all attributes that Clang recognizes", OS, | |
| Records); | |
| OS << "#ifndef PARSED_ATTR\n"; | |
| OS << "#define PARSED_ATTR(NAME) NAME\n"; | |
| OS << "#endif\n\n"; | |
| ParsedAttrMap Names = getParsedAttrList(Records); | |
| for (const auto &I : Names) { | |
| OS << "PARSED_ATTR(" << I.first << ")\n"; | |
| } | |
| } | |
| void EmitAttributeSpellingList(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("List of attribute names", OS, Records); | |
| std::set<StringRef> AttrSpellingList; | |
| std::set<StringRef> AttrScopeSpellingList; | |
| for (const auto *A : Records.getAllDerivedDefinitions("Attr")) { | |
| for (const auto &S : GetFlattenedSpellings(*A)) { | |
| AttrSpellingList.insert(S.name()); | |
| if (S.nameSpace().size()) | |
| AttrScopeSpellingList.insert(S.nameSpace()); | |
| } | |
| } | |
| OS << "#ifndef ATTR_NAME" << "\n"; | |
| OS << "#define ATTR_NAME(NAME) NAME" << "\n"; | |
| OS << "#endif" << "\n" << "\n"; | |
| for (const auto &AttrName : AttrSpellingList) { | |
| OS << "ATTR_NAME(\"" << AttrName << "\")\n"; | |
| } | |
| OS << "\n"; | |
| OS << "#undef ATTR_NAME" << "\n"; | |
| OS << "\n"; | |
| OS << "#ifndef ATTR_SCOPE_NAME" << "\n"; | |
| OS << "#define ATTR_SCOPE_NAME(SCOPE_NAME) SCOPE_NAME" << "\n"; | |
| OS << "#endif" << "\n" << "\n"; | |
| for (const auto &AttrScopeName : AttrScopeSpellingList) { | |
| OS << "ATTR_SCOPE_NAME(\"" << AttrScopeName << "\")\n"; | |
| } | |
| OS << "\n"; | |
| OS << "#undef ATTR_SCOPE_NAME" << "\n"; | |
| OS << "\n"; | |
| } | |
| static bool isArgVariadic(const Record &R, StringRef AttrName) { | |
| return createArgument(R, AttrName)->isVariadic(); | |
| } | |
| static void emitArgInfo(const Record &R, raw_ostream &OS) { | |
| // This function will count the number of arguments specified for the | |
| // attribute and emit the number of required arguments followed by the | |
| // number of optional arguments. | |
| unsigned ArgCount = 0, OptCount = 0, ArgMemberCount = 0; | |
| bool HasVariadic = false; | |
| for (const auto *Arg : R.getValueAsListOfDefs("Args")) { | |
| // If the arg is fake, it's the user's job to supply it: general parsing | |
| // logic shouldn't need to know anything about it. | |
| if (Arg->getValueAsBit("Fake")) | |
| continue; | |
| Arg->getValueAsBit("Optional") ? ++OptCount : ++ArgCount; | |
| ++ArgMemberCount; | |
| if (!HasVariadic && isArgVariadic(*Arg, R.getName())) | |
| HasVariadic = true; | |
| } | |
| // If there is a variadic argument, we will set the optional argument count | |
| // to its largest value. Since it's currently a 4-bit number, we set it to 15. | |
| OS << " /*NumArgs=*/" << ArgCount << ",\n"; | |
| OS << " /*OptArgs=*/" << (HasVariadic ? 15 : OptCount) << ",\n"; | |
| OS << " /*NumArgMembers=*/" << ArgMemberCount << ",\n"; | |
| } | |
| static std::string GetDiagnosticSpelling(const Record &R) { | |
| StringRef Ret = R.getValueAsString("DiagSpelling"); | |
| if (!Ret.empty()) | |
| return Ret.str(); | |
| // If we couldn't find the DiagSpelling in this object, we can check to see | |
| // if the object is one that has a base, and if it is, loop up to the Base | |
| // member recursively. | |
| if (auto Base = R.getValueAsOptionalDef(BaseFieldName)) | |
| return GetDiagnosticSpelling(*Base); | |
| return ""; | |
| } | |
| static std::string CalculateDiagnostic(const Record &S) { | |
| // If the SubjectList object has a custom diagnostic associated with it, | |
| // return that directly. | |
| const StringRef CustomDiag = S.getValueAsString("CustomDiag"); | |
| if (!CustomDiag.empty()) | |
| return ("\"" + Twine(CustomDiag) + "\"").str(); | |
| std::vector<std::string> DiagList; | |
| for (const auto *Subject : S.getValueAsListOfDefs("Subjects")) { | |
| const Record &R = *Subject; | |
| // Get the diagnostic text from the Decl or Stmt node given. | |
| std::string V = GetDiagnosticSpelling(R); | |
| if (V.empty()) { | |
| PrintError(R.getLoc(), | |
| "Could not determine diagnostic spelling for the node: " + | |
| R.getName() + "; please add one to DeclNodes.td"); | |
| } else { | |
| // The node may contain a list of elements itself, so split the elements | |
| // by a comma, and trim any whitespace. | |
| SmallVector<StringRef, 2> Frags; | |
| SplitString(V, Frags, ","); | |
| for (auto Str : Frags) { | |
| DiagList.push_back(Str.trim().str()); | |
| } | |
| } | |
| } | |
| if (DiagList.empty()) { | |
| PrintFatalError(S.getLoc(), | |
| "Could not deduce diagnostic argument for Attr subjects"); | |
| return ""; | |
| } | |
| // FIXME: this is not particularly good for localization purposes and ideally | |
| // should be part of the diagnostics engine itself with some sort of list | |
| // specifier. | |
| // A single member of the list can be returned directly. | |
| if (DiagList.size() == 1) | |
| return '"' + DiagList.front() + '"'; | |
| if (DiagList.size() == 2) | |
| return '"' + DiagList[0] + " and " + DiagList[1] + '"'; | |
| // If there are more than two in the list, we serialize the first N - 1 | |
| // elements with a comma. This leaves the string in the state: foo, bar, | |
| // baz (but misses quux). We can then add ", and " for the last element | |
| // manually. | |
| std::string Diag = join(DiagList.begin(), DiagList.end() - 1, ", "); | |
| return '"' + Diag + ", and " + *(DiagList.end() - 1) + '"'; | |
| } | |
| static std::string GetSubjectWithSuffix(const Record *R) { | |
| const std::string B = R->getName().str(); | |
| if (B == "DeclBase") | |
| return "Decl"; | |
| return B + "Decl"; | |
| } | |
| static std::string functionNameForCustomAppertainsTo(const Record &Subject) { | |
| return "is" + Subject.getName().str(); | |
| } | |
| static void GenerateCustomAppertainsTo(const Record &Subject, raw_ostream &OS) { | |
| std::string FnName = functionNameForCustomAppertainsTo(Subject); | |
| // If this code has already been generated, we don't need to do anything. | |
| static std::set<std::string> CustomSubjectSet; | |
| auto I = CustomSubjectSet.find(FnName); | |
| if (I != CustomSubjectSet.end()) | |
| return; | |
| // This only works with non-root Decls. | |
| const Record *Base = Subject.getValueAsDef(BaseFieldName); | |
| // Not currently support custom subjects within custom subjects. | |
| if (Base->isSubClassOf("SubsetSubject")) { | |
| PrintFatalError(Subject.getLoc(), | |
| "SubsetSubjects within SubsetSubjects is not supported"); | |
| return; | |
| } | |
| OS << "static bool " << FnName << "(const Decl *D) {\n"; | |
| OS << " if (const auto *S = dyn_cast<"; | |
| OS << GetSubjectWithSuffix(Base); | |
| OS << ">(D))\n"; | |
| OS << " return " << Subject.getValueAsString("CheckCode") << ";\n"; | |
| OS << " return false;\n"; | |
| OS << "}\n\n"; | |
| CustomSubjectSet.insert(FnName); | |
| } | |
| static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { | |
| // If the attribute does not contain a Subjects definition, then use the | |
| // default appertainsTo logic. | |
| if (Attr.isValueUnset("Subjects")) | |
| return; | |
| const Record *SubjectObj = Attr.getValueAsDef("Subjects"); | |
| std::vector<const Record *> Subjects = | |
| SubjectObj->getValueAsListOfDefs("Subjects"); | |
| // If the list of subjects is empty, it is assumed that the attribute | |
| // appertains to everything. | |
| if (Subjects.empty()) | |
| return; | |
| bool Warn = SubjectObj->getValueAsDef("Diag")->getValueAsBit("Warn"); | |
| // Split the subjects into declaration subjects and statement subjects. | |
| // FIXME: subset subjects are added to the declaration list until there are | |
| // enough statement attributes with custom subject needs to warrant | |
| // the implementation effort. | |
| std::vector<const Record *> DeclSubjects, StmtSubjects; | |
| copy_if(Subjects, std::back_inserter(DeclSubjects), [](const Record *R) { | |
| return R->isSubClassOf("SubsetSubject") || !R->isSubClassOf("StmtNode"); | |
| }); | |
| copy_if(Subjects, std::back_inserter(StmtSubjects), | |
| [](const Record *R) { return R->isSubClassOf("StmtNode"); }); | |
| // We should have sorted all of the subjects into two lists. | |
| // FIXME: this assertion will be wrong if we ever add type attribute subjects. | |
| assert(DeclSubjects.size() + StmtSubjects.size() == Subjects.size()); | |
| if (DeclSubjects.empty()) { | |
| // If there are no decl subjects but there are stmt subjects, diagnose | |
| // trying to apply a statement attribute to a declaration. | |
| if (!StmtSubjects.empty()) { | |
| OS << "bool diagAppertainsToDecl(Sema &S, const ParsedAttr &AL, "; | |
| OS << "const Decl *D) const override {\n"; | |
| OS << " S.Diag(AL.getLoc(), diag::err_attribute_invalid_on_decl)\n"; | |
| OS << " << AL << AL.isRegularKeywordAttribute() << " | |
| "D->getLocation();\n"; | |
| OS << " return false;\n"; | |
| OS << "}\n\n"; | |
| } | |
| } else { | |
| // Otherwise, generate an appertainsTo check specific to this attribute | |
| // which checks all of the given subjects against the Decl passed in. | |
| OS << "bool diagAppertainsToDecl(Sema &S, "; | |
| OS << "const ParsedAttr &Attr, const Decl *D) const override {\n"; | |
| OS << " if ("; | |
| for (auto I = DeclSubjects.begin(), E = DeclSubjects.end(); I != E; ++I) { | |
| // If the subject has custom code associated with it, use the generated | |
| // function for it. The function cannot be inlined into this check (yet) | |
| // because it requires the subject to be of a specific type, and were that | |
| // information inlined here, it would not support an attribute with | |
| // multiple custom subjects. | |
| if ((*I)->isSubClassOf("SubsetSubject")) | |
| OS << "!" << functionNameForCustomAppertainsTo(**I) << "(D)"; | |
| else | |
| OS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)"; | |
| if (I + 1 != E) | |
| OS << " && "; | |
| } | |
| OS << ") {\n"; | |
| OS << " S.Diag(Attr.getLoc(), diag::"; | |
| OS << (Warn ? "warn_attribute_wrong_decl_type_str" | |
| : "err_attribute_wrong_decl_type_str"); | |
| OS << ")\n"; | |
| OS << " << Attr << Attr.isRegularKeywordAttribute() << "; | |
| OS << CalculateDiagnostic(*SubjectObj) << ";\n"; | |
| OS << " return false;\n"; | |
| OS << " }\n"; | |
| OS << " return true;\n"; | |
| OS << "}\n\n"; | |
| } | |
| if (StmtSubjects.empty()) { | |
| // If there are no stmt subjects but there are decl subjects, diagnose | |
| // trying to apply a declaration attribute to a statement. | |
| if (!DeclSubjects.empty()) { | |
| OS << "bool diagAppertainsToStmt(Sema &S, const ParsedAttr &AL, "; | |
| OS << "const Stmt *St) const override {\n"; | |
| OS << " S.Diag(AL.getLoc(), diag::err_decl_attribute_invalid_on_stmt)\n"; | |
| OS << " << AL << AL.isRegularKeywordAttribute() << " | |
| "St->getBeginLoc();\n"; | |
| OS << " return false;\n"; | |
| OS << "}\n\n"; | |
| } | |
| } else { | |
| // Now, do the same for statements. | |
| OS << "bool diagAppertainsToStmt(Sema &S, "; | |
| OS << "const ParsedAttr &Attr, const Stmt *St) const override {\n"; | |
| OS << " if ("; | |
| for (auto I = StmtSubjects.begin(), E = StmtSubjects.end(); I != E; ++I) { | |
| OS << "!isa<" << (*I)->getName() << ">(St)"; | |
| if (I + 1 != E) | |
| OS << " && "; | |
| } | |
| OS << ") {\n"; | |
| OS << " S.Diag(Attr.getLoc(), diag::"; | |
| OS << (Warn ? "warn_attribute_wrong_decl_type_str" | |
| : "err_attribute_wrong_decl_type_str"); | |
| OS << ")\n"; | |
| OS << " << Attr << Attr.isRegularKeywordAttribute() << "; | |
| OS << CalculateDiagnostic(*SubjectObj) << ";\n"; | |
| OS << " return false;\n"; | |
| OS << " }\n"; | |
| OS << " return true;\n"; | |
| OS << "}\n\n"; | |
| } | |
| } | |
| // Generates the mutual exclusion checks. The checks for parsed attributes are | |
| // written into OS and the checks for merging declaration attributes are | |
| // written into MergeOS. | |
| static void GenerateMutualExclusionsChecks(const Record &Attr, | |
| const RecordKeeper &Records, | |
| raw_ostream &OS, | |
| raw_ostream &MergeDeclOS, | |
| raw_ostream &MergeStmtOS) { | |
| // We don't do any of this magic for type attributes yet. | |
| if (Attr.isSubClassOf("TypeAttr")) | |
| return; | |
| // This means the attribute is either a statement attribute, a decl | |
| // attribute, or both; find out which. | |
| bool CurAttrIsStmtAttr = Attr.isSubClassOf("StmtAttr") || | |
| Attr.isSubClassOf("DeclOrStmtAttr") || | |
| Attr.isSubClassOf("InheritableParamOrStmtAttr"); | |
| bool CurAttrIsDeclAttr = !CurAttrIsStmtAttr || | |
| Attr.isSubClassOf("DeclOrStmtAttr") || | |
| Attr.isSubClassOf("InheritableParamOrStmtAttr"); | |
| std::vector<std::string> DeclAttrs, StmtAttrs; | |
| // Find all of the definitions that inherit from MutualExclusions and include | |
| // the given attribute in the list of exclusions to generate the | |
| // diagMutualExclusion() check. | |
| for (const Record *Exclusion : | |
| Records.getAllDerivedDefinitions("MutualExclusions")) { | |
| std::vector<const Record *> MutuallyExclusiveAttrs = | |
| Exclusion->getValueAsListOfDefs("Exclusions"); | |
| auto IsCurAttr = [Attr](const Record *R) { | |
| return R->getName() == Attr.getName(); | |
| }; | |
| if (any_of(MutuallyExclusiveAttrs, IsCurAttr)) { | |
| // This list of exclusions includes the attribute we're looking for, so | |
| // add the exclusive attributes to the proper list for checking. | |
| for (const Record *AttrToExclude : MutuallyExclusiveAttrs) { | |
| if (IsCurAttr(AttrToExclude)) | |
| continue; | |
| if (CurAttrIsStmtAttr) | |
| StmtAttrs.push_back((AttrToExclude->getName() + "Attr").str()); | |
| if (CurAttrIsDeclAttr) | |
| DeclAttrs.push_back((AttrToExclude->getName() + "Attr").str()); | |
| } | |
| } | |
| } | |
| // If there are any decl or stmt attributes, silence -Woverloaded-virtual | |
| // warnings for them both. | |
| if (!DeclAttrs.empty() || !StmtAttrs.empty()) | |
| OS << " using ParsedAttrInfo::diagMutualExclusion;\n\n"; | |
| // If we discovered any decl or stmt attributes to test for, generate the | |
| // predicates for them now. | |
| if (!DeclAttrs.empty()) { | |
| // Generate the ParsedAttrInfo subclass logic for declarations. | |
| OS << " bool diagMutualExclusion(Sema &S, const ParsedAttr &AL, " | |
| << "const Decl *D) const override {\n"; | |
| for (const std::string &A : DeclAttrs) { | |
| OS << " if (const auto *A = D->getAttr<" << A << ">()) {\n"; | |
| OS << " S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible)" | |
| << " << AL << A << (AL.isRegularKeywordAttribute() ||" | |
| << " A->isRegularKeywordAttribute());\n"; | |
| OS << " S.Diag(A->getLocation(), diag::note_conflicting_attribute);"; | |
| OS << " \nreturn false;\n"; | |
| OS << " }\n"; | |
| } | |
| OS << " return true;\n"; | |
| OS << " }\n\n"; | |
| // Also generate the declaration attribute merging logic if the current | |
| // attribute is one that can be inheritted on a declaration. It is assumed | |
| // this code will be executed in the context of a function with parameters: | |
| // Sema &S, Decl *D, Attr *A and that returns a bool (false on diagnostic, | |
| // true on success). | |
| if (Attr.isSubClassOf("InheritableAttr")) { | |
| MergeDeclOS << " if (const auto *Second = dyn_cast<" | |
| << (Attr.getName() + "Attr").str() << ">(A)) {\n"; | |
| for (const std::string &A : DeclAttrs) { | |
| MergeDeclOS << " if (const auto *First = D->getAttr<" << A | |
| << ">()) {\n"; | |
| MergeDeclOS << " S.Diag(First->getLocation(), " | |
| << "diag::err_attributes_are_not_compatible) << First << " | |
| << "Second << (First->isRegularKeywordAttribute() || " | |
| << "Second->isRegularKeywordAttribute());\n"; | |
| MergeDeclOS << " S.Diag(Second->getLocation(), " | |
| << "diag::note_conflicting_attribute);\n"; | |
| MergeDeclOS << " return false;\n"; | |
| MergeDeclOS << " }\n"; | |
| } | |
| MergeDeclOS << " return true;\n"; | |
| MergeDeclOS << " }\n"; | |
| } | |
| } | |
| // Statement attributes are a bit different from declarations. With | |
| // declarations, each attribute is added to the declaration as it is | |
| // processed, and so you can look on the Decl * itself to see if there is a | |
| // conflicting attribute. Statement attributes are processed as a group | |
| // because AttributedStmt needs to tail-allocate all of the attribute nodes | |
| // at once. This means we cannot check whether the statement already contains | |
| // an attribute to check for the conflict. Instead, we need to check whether | |
| // the given list of semantic attributes contain any conflicts. It is assumed | |
| // this code will be executed in the context of a function with parameters: | |
| // Sema &S, const SmallVectorImpl<const Attr *> &C. The code will be within a | |
| // loop which loops over the container C with a loop variable named A to | |
| // represent the current attribute to check for conflicts. | |
| // | |
| // FIXME: it would be nice not to walk over the list of potential attributes | |
| // to apply to the statement more than once, but statements typically don't | |
| // have long lists of attributes on them, so re-walking the list should not | |
| // be an expensive operation. | |
| if (!StmtAttrs.empty()) { | |
| MergeStmtOS << " if (const auto *Second = dyn_cast<" | |
| << (Attr.getName() + "Attr").str() << ">(A)) {\n"; | |
| MergeStmtOS << " auto Iter = llvm::find_if(C, [](const Attr *Check) " | |
| << "{ return isa<"; | |
| interleave( | |
| StmtAttrs, [&](StringRef Name) { MergeStmtOS << Name; }, | |
| [&] { MergeStmtOS << ", "; }); | |
| MergeStmtOS << ">(Check); });\n"; | |
| MergeStmtOS << " if (Iter != C.end()) {\n"; | |
| MergeStmtOS << " S.Diag((*Iter)->getLocation(), " | |
| << "diag::err_attributes_are_not_compatible) << *Iter << " | |
| << "Second << ((*Iter)->isRegularKeywordAttribute() || " | |
| << "Second->isRegularKeywordAttribute());\n"; | |
| MergeStmtOS << " S.Diag(Second->getLocation(), " | |
| << "diag::note_conflicting_attribute);\n"; | |
| MergeStmtOS << " return false;\n"; | |
| MergeStmtOS << " }\n"; | |
| MergeStmtOS << " }\n"; | |
| } | |
| } | |
| static void | |
| emitAttributeMatchRules(PragmaClangAttributeSupport &PragmaAttributeSupport, | |
| raw_ostream &OS) { | |
| OS << "static bool checkAttributeMatchRuleAppliesTo(const Decl *D, " | |
| << AttributeSubjectMatchRule::EnumName << " rule) {\n"; | |
| OS << " switch (rule) {\n"; | |
| for (const auto &Rule : PragmaAttributeSupport.Rules) { | |
| if (Rule.isAbstractRule()) { | |
| OS << " case " << Rule.getEnumValue() << ":\n"; | |
| OS << " assert(false && \"Abstract matcher rule isn't allowed\");\n"; | |
| OS << " return false;\n"; | |
| continue; | |
| } | |
| std::vector<const Record *> Subjects = Rule.getSubjects(); | |
| assert(!Subjects.empty() && "Missing subjects"); | |
| OS << " case " << Rule.getEnumValue() << ":\n"; | |
| OS << " return "; | |
| for (auto I = Subjects.begin(), E = Subjects.end(); I != E; ++I) { | |
| // If the subject has custom code associated with it, use the function | |
| // that was generated for GenerateAppertainsTo to check if the declaration | |
| // is valid. | |
| if ((*I)->isSubClassOf("SubsetSubject")) | |
| OS << functionNameForCustomAppertainsTo(**I) << "(D)"; | |
| else | |
| OS << "isa<" << GetSubjectWithSuffix(*I) << ">(D)"; | |
| if (I + 1 != E) | |
| OS << " || "; | |
| } | |
| OS << ";\n"; | |
| } | |
| OS << " }\n"; | |
| OS << " llvm_unreachable(\"Invalid match rule\");\nreturn false;\n"; | |
| OS << "}\n\n"; | |
| } | |
| static void GenerateLangOptRequirements(const Record &R, | |
| raw_ostream &OS) { | |
| // If the attribute has an empty or unset list of language requirements, | |
| // use the default handler. | |
| std::vector<const Record *> LangOpts = R.getValueAsListOfDefs("LangOpts"); | |
| if (LangOpts.empty()) | |
| return; | |
| OS << "bool acceptsLangOpts(const LangOptions &LangOpts) const override {\n"; | |
| OS << " return " << GenerateTestExpression(LangOpts) << ";\n"; | |
| OS << "}\n\n"; | |
| } | |
| static void GenerateTargetRequirements(const Record &Attr, | |
| const ParsedAttrMap &Dupes, | |
| raw_ostream &OS) { | |
| // If the attribute is not a target specific attribute, use the default | |
| // target handler. | |
| if (!Attr.isSubClassOf("TargetSpecificAttr")) | |
| return; | |
| // Get the list of architectures to be tested for. | |
| const Record *R = Attr.getValueAsDef("Target"); | |
| std::vector<StringRef> Arches = R->getValueAsListOfStrings("Arches"); | |
| // If there are other attributes which share the same parsed attribute kind, | |
| // such as target-specific attributes with a shared spelling, collapse the | |
| // duplicate architectures. This is required because a shared target-specific | |
| // attribute has only one ParsedAttr::Kind enumeration value, but it | |
| // applies to multiple target architectures. In order for the attribute to be | |
| // considered valid, all of its architectures need to be included. | |
| if (!Attr.isValueUnset("ParseKind")) { | |
| const StringRef APK = Attr.getValueAsString("ParseKind"); | |
| for (const auto &I : Dupes) { | |
| if (I.first == APK) { | |
| std::vector<StringRef> DA = | |
| I.second->getValueAsDef("Target")->getValueAsListOfStrings( | |
| "Arches"); | |
| llvm::append_range(Arches, DA); | |
| } | |
| } | |
| } | |
| std::string FnName = "isTarget"; | |
| std::string Test; | |
| bool UsesT = GenerateTargetSpecificAttrChecks(R, Arches, Test, &FnName); | |
| OS << "bool existsInTarget(const TargetInfo &Target) const override {\n"; | |
| if (UsesT) | |
| OS << " const llvm::Triple &T = Target.getTriple(); (void)T;\n"; | |
| OS << " return " << Test << ";\n"; | |
| OS << "}\n\n"; | |
| } | |
| static void | |
| GenerateSpellingTargetRequirements(const Record &Attr, | |
| ArrayRef<const Record *> TargetSpellings, | |
| raw_ostream &OS) { | |
| // If there are no target specific spellings, use the default target handler. | |
| if (TargetSpellings.empty()) | |
| return; | |
| std::string Test; | |
| bool UsesT = false; | |
| const std::vector<FlattenedSpelling> SpellingList = | |
| GetFlattenedSpellings(Attr); | |
| for (unsigned TargetIndex = 0; TargetIndex < TargetSpellings.size(); | |
| ++TargetIndex) { | |
| const auto &TargetSpelling = TargetSpellings[TargetIndex]; | |
| std::vector<FlattenedSpelling> Spellings = | |
| GetFlattenedSpellings(*TargetSpelling); | |
| Test += "((SpellingListIndex == "; | |
| for (unsigned Index = 0; Index < Spellings.size(); ++Index) { | |
| Test += itostr(getSpellingListIndex(SpellingList, Spellings[Index])); | |
| if (Index != Spellings.size() - 1) | |
| Test += " ||\n SpellingListIndex == "; | |
| else | |
| Test += ") && "; | |
| } | |
| const Record *Target = TargetSpelling->getValueAsDef("Target"); | |
| std::vector<StringRef> Arches = Target->getValueAsListOfStrings("Arches"); | |
| std::string FnName = "isTargetSpelling"; | |
| UsesT |= GenerateTargetSpecificAttrChecks(Target, Arches, Test, &FnName); | |
| Test += ")"; | |
| if (TargetIndex != TargetSpellings.size() - 1) | |
| Test += " || "; | |
| } | |
| OS << "bool spellingExistsInTarget(const TargetInfo &Target,\n"; | |
| OS << " const unsigned SpellingListIndex) const " | |
| "override {\n"; | |
| if (UsesT) | |
| OS << " const llvm::Triple &T = Target.getTriple(); (void)T;\n"; | |
| OS << " return " << Test << ";\n", OS << "}\n\n"; | |
| } | |
| static void GenerateSpellingIndexToSemanticSpelling(const Record &Attr, | |
| raw_ostream &OS) { | |
| // If the attribute does not have a semantic form, we can bail out early. | |
| if (!Attr.getValueAsBit("ASTNode")) | |
| return; | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr); | |
| // If there are zero or one spellings, or all of the spellings share the same | |
| // name, we can also bail out early. | |
| if (Spellings.size() <= 1 || SpellingNamesAreCommon(Spellings)) | |
| return; | |
| // Generate the enumeration we will use for the mapping. | |
| SemanticSpellingMap SemanticToSyntacticMap; | |
| std::string Enum = CreateSemanticSpellings(Spellings, SemanticToSyntacticMap); | |
| OS << "unsigned spellingIndexToSemanticSpelling("; | |
| OS << "const ParsedAttr &Attr) const override {\n"; | |
| OS << Enum; | |
| OS << " unsigned Idx = Attr.getAttributeSpellingListIndex();\n"; | |
| WriteSemanticSpellingSwitch("Idx", SemanticToSyntacticMap, OS); | |
| OS << "}\n\n"; | |
| } | |
| static void GenerateHandleDeclAttribute(const Record &Attr, raw_ostream &OS) { | |
| // Only generate if Attr can be handled simply. | |
| if (!Attr.getValueAsBit("SimpleHandler")) | |
| return; | |
| // Generate a function which just converts from ParsedAttr to the Attr type. | |
| OS << "AttrHandling handleDeclAttribute(Sema &S, Decl *D,"; | |
| OS << "const ParsedAttr &Attr) const override {\n"; | |
| OS << " D->addAttr(::new (S.Context) " << Attr.getName(); | |
| OS << "Attr(S.Context, Attr));\n"; | |
| OS << " return AttributeApplied;\n"; | |
| OS << "}\n\n"; | |
| } | |
| static bool isParamExpr(const Record *Arg) { | |
| return !Arg->getDirectSuperClasses().empty() && | |
| StringSwitch<bool>( | |
| Arg->getDirectSuperClasses().back().first->getName()) | |
| .Case("ExprArgument", true) | |
| .Case("VariadicExprArgument", true) | |
| .Default(false); | |
| } | |
| static void GenerateIsParamExpr(const Record &Attr, raw_ostream &OS) { | |
| OS << "bool isParamExpr(size_t N) const override {\n"; | |
| OS << " return "; | |
| auto Args = Attr.getValueAsListOfDefs("Args"); | |
| for (size_t I = 0; I < Args.size(); ++I) | |
| if (isParamExpr(Args[I])) | |
| OS << "(N == " << I << ") || "; | |
| OS << "false;\n"; | |
| OS << "}\n\n"; | |
| } | |
| static void GenerateHandleAttrWithDelayedArgs(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| OS << "static void handleAttrWithDelayedArgs(Sema &S, Decl *D, "; | |
| OS << "const ParsedAttr &Attr) {\n"; | |
| OS << " SmallVector<Expr *, 4> ArgExprs;\n"; | |
| OS << " ArgExprs.reserve(Attr.getNumArgs());\n"; | |
| OS << " for (unsigned I = 0; I < Attr.getNumArgs(); ++I) {\n"; | |
| OS << " assert(!Attr.isArgIdent(I));\n"; | |
| OS << " ArgExprs.push_back(Attr.getArgAsExpr(I));\n"; | |
| OS << " }\n"; | |
| OS << " clang::Attr *CreatedAttr = nullptr;\n"; | |
| OS << " switch (Attr.getKind()) {\n"; | |
| OS << " default:\n"; | |
| OS << " llvm_unreachable(\"Attribute cannot hold delayed arguments.\");\n"; | |
| ParsedAttrMap Attrs = getParsedAttrList(Records); | |
| for (const auto &I : Attrs) { | |
| const Record &R = *I.second; | |
| if (!R.getValueAsBit("AcceptsExprPack")) | |
| continue; | |
| OS << " case ParsedAttr::AT_" << I.first << ": {\n"; | |
| OS << " CreatedAttr = " << R.getName() << "Attr::CreateWithDelayedArgs"; | |
| OS << "(S.Context, ArgExprs.data(), ArgExprs.size(), Attr);\n"; | |
| OS << " break;\n"; | |
| OS << " }\n"; | |
| } | |
| OS << " }\n"; | |
| OS << " D->addAttr(CreatedAttr);\n"; | |
| OS << "}\n\n"; | |
| } | |
| static bool IsKnownToGCC(const Record &Attr) { | |
| // Look at the spellings for this subject; if there are any spellings which | |
| // claim to be known to GCC, the attribute is known to GCC. | |
| return any_of(GetFlattenedSpellings(Attr), | |
| [](const FlattenedSpelling &S) { return S.knownToGCC(); }); | |
| } | |
| /// Emits the parsed attribute helpers | |
| void EmitClangAttrParsedAttrImpl(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Parsed attribute helpers", OS, Records); | |
| OS << "#if !defined(WANT_DECL_MERGE_LOGIC) && " | |
| << "!defined(WANT_STMT_MERGE_LOGIC)\n"; | |
| PragmaClangAttributeSupport &PragmaAttributeSupport = | |
| getPragmaAttributeSupport(Records); | |
| // Get the list of parsed attributes, and accept the optional list of | |
| // duplicates due to the ParseKind. | |
| ParsedAttrMap Dupes; | |
| ParsedAttrMap Attrs = getParsedAttrList(Records, &Dupes); | |
| // Generate all of the custom appertainsTo functions that the attributes | |
| // will be using. | |
| for (const auto &I : Attrs) { | |
| const Record &Attr = *I.second; | |
| if (Attr.isValueUnset("Subjects")) | |
| continue; | |
| const Record *SubjectObj = Attr.getValueAsDef("Subjects"); | |
| for (const Record *Subject : SubjectObj->getValueAsListOfDefs("Subjects")) | |
| if (Subject->isSubClassOf("SubsetSubject")) | |
| GenerateCustomAppertainsTo(*Subject, OS); | |
| } | |
| // This stream is used to collect all of the declaration attribute merging | |
| // logic for performing mutual exclusion checks. This gets emitted at the | |
| // end of the file in a helper function of its own. | |
| std::string DeclMergeChecks, StmtMergeChecks; | |
| raw_string_ostream MergeDeclOS(DeclMergeChecks), MergeStmtOS(StmtMergeChecks); | |
| // Generate a ParsedAttrInfo struct for each of the attributes. | |
| for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) { | |
| // TODO: If the attribute's kind appears in the list of duplicates, that is | |
| // because it is a target-specific attribute that appears multiple times. | |
| // It would be beneficial to test whether the duplicates are "similar | |
| // enough" to each other to not cause problems. For instance, check that | |
| // the spellings are identical, and custom parsing rules match, etc. | |
| // We need to generate struct instances based off ParsedAttrInfo from | |
| // ParsedAttr.cpp. | |
| const std::string &AttrName = I->first; | |
| const Record &Attr = *I->second; | |
| auto Spellings = GetFlattenedSpellings(Attr); | |
| if (!Spellings.empty()) { | |
| OS << "static constexpr ParsedAttrInfo::Spelling " << I->first | |
| << "Spellings[] = {\n"; | |
| for (const auto &S : Spellings) { | |
| StringRef RawSpelling = S.name(); | |
| std::string Spelling; | |
| if (!S.nameSpace().empty()) | |
| Spelling += S.nameSpace().str() + "::"; | |
| if (S.variety() == "GNU") | |
| Spelling += NormalizeGNUAttrSpelling(RawSpelling); | |
| else | |
| Spelling += RawSpelling; | |
| OS << " {AttributeCommonInfo::AS_" << S.variety(); | |
| OS << ", \"" << Spelling << "\"},\n"; | |
| } | |
| OS << "};\n"; | |
| } | |
| std::vector<std::string> ArgNames; | |
| for (const auto *Arg : Attr.getValueAsListOfDefs("Args")) { | |
| bool UnusedUnset; | |
| if (Arg->getValueAsBitOrUnset("Fake", UnusedUnset)) | |
| continue; | |
| ArgNames.push_back(Arg->getValueAsString("Name").str()); | |
| for (const Record *Class : Arg->getSuperClasses()) { | |
| if (Class->getName().starts_with("Variadic")) { | |
| ArgNames.back().append("..."); | |
| break; | |
| } | |
| } | |
| } | |
| if (!ArgNames.empty()) { | |
| OS << "static constexpr const char *" << I->first << "ArgNames[] = {\n"; | |
| for (const auto &N : ArgNames) | |
| OS << '"' << N << "\","; | |
| OS << "};\n"; | |
| } | |
| OS << "struct ParsedAttrInfo" << I->first | |
| << " final : public ParsedAttrInfo {\n"; | |
| OS << " constexpr ParsedAttrInfo" << I->first << "() : ParsedAttrInfo(\n"; | |
| OS << " /*AttrKind=*/ParsedAttr::AT_" << AttrName << ",\n"; | |
| emitArgInfo(Attr, OS); | |
| OS << " /*HasCustomParsing=*/"; | |
| OS << Attr.getValueAsBit("HasCustomParsing") << ",\n"; | |
| OS << " /*AcceptsExprPack=*/"; | |
| OS << Attr.getValueAsBit("AcceptsExprPack") << ",\n"; | |
| OS << " /*IsTargetSpecific=*/"; | |
| OS << Attr.isSubClassOf("TargetSpecificAttr") << ",\n"; | |
| OS << " /*IsType=*/"; | |
| OS << (Attr.isSubClassOf("TypeAttr") || Attr.isSubClassOf("DeclOrTypeAttr")) | |
| << ",\n"; | |
| OS << " /*IsStmt=*/"; | |
| OS << (Attr.isSubClassOf("StmtAttr") || Attr.isSubClassOf("DeclOrStmtAttr")) | |
| << ",\n"; | |
| OS << " /*IsKnownToGCC=*/"; | |
| OS << IsKnownToGCC(Attr) << ",\n"; | |
| OS << " /*IsSupportedByPragmaAttribute=*/"; | |
| OS << PragmaAttributeSupport.isAttributedSupported(*I->second) << ",\n"; | |
| if (!Spellings.empty()) | |
| OS << " /*Spellings=*/" << I->first << "Spellings,\n"; | |
| else | |
| OS << " /*Spellings=*/{},\n"; | |
| if (!ArgNames.empty()) | |
| OS << " /*ArgNames=*/" << I->first << "ArgNames"; | |
| else | |
| OS << " /*ArgNames=*/{}"; | |
| OS << ") {}\n"; | |
| GenerateAppertainsTo(Attr, OS); | |
| GenerateMutualExclusionsChecks(Attr, Records, OS, MergeDeclOS, MergeStmtOS); | |
| GenerateLangOptRequirements(Attr, OS); | |
| GenerateTargetRequirements(Attr, Dupes, OS); | |
| GenerateSpellingTargetRequirements( | |
| Attr, Attr.getValueAsListOfDefs("TargetSpecificSpellings"), OS); | |
| GenerateSpellingIndexToSemanticSpelling(Attr, OS); | |
| PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS); | |
| GenerateHandleDeclAttribute(Attr, OS); | |
| GenerateIsParamExpr(Attr, OS); | |
| OS << "static const ParsedAttrInfo" << I->first << " Instance;\n"; | |
| OS << "};\n"; | |
| OS << "const ParsedAttrInfo" << I->first << " ParsedAttrInfo" << I->first | |
| << "::Instance;\n"; | |
| } | |
| OS << "static const ParsedAttrInfo *AttrInfoMap[] = {\n"; | |
| for (const auto &Attr : Attrs) | |
| OS << "&ParsedAttrInfo" << Attr.first << "::Instance,\n"; | |
| OS << "};\n\n"; | |
| // Generate function for handling attributes with delayed arguments | |
| GenerateHandleAttrWithDelayedArgs(Records, OS); | |
| // Generate the attribute match rules. | |
| emitAttributeMatchRules(PragmaAttributeSupport, OS); | |
| OS << "#elif defined(WANT_DECL_MERGE_LOGIC)\n\n"; | |
| // Write out the declaration merging check logic. | |
| OS << "static bool DiagnoseMutualExclusions(Sema &S, const NamedDecl *D, " | |
| << "const Attr *A) {\n"; | |
| OS << DeclMergeChecks; | |
| OS << " return true;\n"; | |
| OS << "}\n\n"; | |
| OS << "#elif defined(WANT_STMT_MERGE_LOGIC)\n\n"; | |
| // Write out the statement merging check logic. | |
| OS << "static bool DiagnoseMutualExclusions(Sema &S, " | |
| << "const SmallVectorImpl<const Attr *> &C) {\n"; | |
| OS << " for (const Attr *A : C) {\n"; | |
| OS << StmtMergeChecks; | |
| OS << " }\n"; | |
| OS << " return true;\n"; | |
| OS << "}\n\n"; | |
| OS << "#endif\n"; | |
| } | |
| // Emits the kind list of parsed attributes | |
| void EmitClangAttrParsedAttrKinds(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| emitSourceFileHeader("Attribute name matcher", OS, Records); | |
| std::vector<StringMatcher::StringPair> GNU, Declspec, Microsoft, CXX11, | |
| Keywords, Pragma, C23, HLSLAnnotation; | |
| std::set<StringRef> Seen; | |
| for (const auto *A : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &Attr = *A; | |
| bool SemaHandler = Attr.getValueAsBit("SemaHandler"); | |
| bool Ignored = Attr.getValueAsBit("Ignored"); | |
| if (SemaHandler || Ignored) { | |
| // Attribute spellings can be shared between target-specific attributes, | |
| // and can be shared between syntaxes for the same attribute. For | |
| // instance, an attribute can be spelled GNU<"interrupt"> for an ARM- | |
| // specific attribute, or MSP430-specific attribute. Additionally, an | |
| // attribute can be spelled GNU<"dllexport"> and Declspec<"dllexport"> | |
| // for the same semantic attribute. Ultimately, we need to map each of | |
| // these to a single AttributeCommonInfo::Kind value, but the | |
| // StringMatcher class cannot handle duplicate match strings. So we | |
| // generate a list of string to match based on the syntax, and emit | |
| // multiple string matchers depending on the syntax used. | |
| std::string AttrName; | |
| if (Attr.isSubClassOf("TargetSpecificAttr") && | |
| !Attr.isValueUnset("ParseKind")) { | |
| StringRef ParseKind = Attr.getValueAsString("ParseKind"); | |
| if (!Seen.insert(ParseKind).second) | |
| continue; | |
| AttrName = ParseKind.str(); | |
| } else { | |
| AttrName = NormalizeAttrName(Attr.getName()).str(); | |
| } | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr); | |
| for (const auto &S : Spellings) { | |
| StringRef RawSpelling = S.name(); | |
| std::vector<StringMatcher::StringPair> *Matches = nullptr; | |
| std::string Spelling; | |
| StringRef Variety = S.variety(); | |
| if (Variety == "CXX11") { | |
| Matches = &CXX11; | |
| if (!S.nameSpace().empty()) | |
| Spelling += S.nameSpace().str() + "::"; | |
| } else if (Variety == "C23") { | |
| Matches = &C23; | |
| if (!S.nameSpace().empty()) | |
| Spelling += S.nameSpace().str() + "::"; | |
| } else if (Variety == "GNU") { | |
| Matches = &GNU; | |
| } else if (Variety == "Declspec") { | |
| Matches = &Declspec; | |
| } else if (Variety == "Microsoft") { | |
| Matches = &Microsoft; | |
| } else if (Variety == "Keyword") { | |
| Matches = &Keywords; | |
| } else if (Variety == "Pragma") { | |
| Matches = &Pragma; | |
| } else if (Variety == "HLSLAnnotation") { | |
| Matches = &HLSLAnnotation; | |
| if (RawSpelling.compare(RawSpelling.lower()) != 0) | |
| PrintError(S.getSpellingRecord().getLoc(), | |
| "HLSLAnnotation Attribute must be lower case."); | |
| } | |
| assert(Matches && "Unsupported spelling variety found"); | |
| if (Variety == "GNU") | |
| Spelling += NormalizeGNUAttrSpelling(RawSpelling); | |
| else | |
| Spelling += RawSpelling; | |
| if (SemaHandler) | |
| Matches->push_back(StringMatcher::StringPair( | |
| Spelling, "return AttributeCommonInfo::AT_" + AttrName + ";")); | |
| else | |
| Matches->push_back(StringMatcher::StringPair( | |
| Spelling, "return AttributeCommonInfo::IgnoredAttribute;")); | |
| } | |
| } | |
| } | |
| OS << "static AttributeCommonInfo::Kind getAttrKind(StringRef Name, "; | |
| OS << "AttributeCommonInfo::Syntax Syntax) {\n"; | |
| OS << " if (AttributeCommonInfo::AS_GNU == Syntax) {\n"; | |
| StringMatcher("Name", GNU, OS).Emit(); | |
| OS << " } else if (AttributeCommonInfo::AS_Declspec == Syntax) {\n"; | |
| StringMatcher("Name", Declspec, OS).Emit(); | |
| OS << " } else if (AttributeCommonInfo::AS_Microsoft == Syntax) {\n"; | |
| StringMatcher("Name", Microsoft, OS).Emit(); | |
| OS << " } else if (AttributeCommonInfo::AS_CXX11 == Syntax) {\n"; | |
| StringMatcher("Name", CXX11, OS).Emit(); | |
| OS << " } else if (AttributeCommonInfo::AS_C23 == Syntax) {\n"; | |
| StringMatcher("Name", C23, OS).Emit(); | |
| OS << " } else if (AttributeCommonInfo::AS_Keyword == Syntax || "; | |
| OS << "AttributeCommonInfo::AS_ContextSensitiveKeyword == Syntax) {\n"; | |
| StringMatcher("Name", Keywords, OS).Emit(); | |
| OS << " } else if (AttributeCommonInfo::AS_Pragma == Syntax) {\n"; | |
| StringMatcher("Name", Pragma, OS).Emit(); | |
| OS << " } else if (AttributeCommonInfo::AS_HLSLAnnotation == Syntax) {\n"; | |
| StringMatcher("Name", HLSLAnnotation, OS).Emit(); | |
| OS << " }\n"; | |
| OS << " return AttributeCommonInfo::UnknownAttribute;\n" | |
| << "}\n"; | |
| } | |
| // Emits Sema calls for type dependent attributes | |
| void EmitClangAttrIsTypeDependent(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| emitSourceFileHeader("Attribute is type dependent", OS, Records); | |
| OS << "void checkAttrIsTypeDependent(Decl *D, const Attr *A) {\n"; | |
| OS << " switch (A->getKind()) {\n"; | |
| OS << " default:\n"; | |
| OS << " break;\n"; | |
| for (const auto *A : Records.getAllDerivedDefinitions("Attr")) { | |
| if (A->getValueAsBit("IsTypeDependent")) { | |
| OS << " case attr::" << A->getName() << ":\n"; | |
| OS << " ActOn" << A->getName() << "Attr(D, A);\n"; | |
| OS << " break;\n"; | |
| } | |
| } | |
| OS << " }\n"; | |
| OS << "}\n"; | |
| } | |
| // Emits the code to dump an attribute. | |
| void EmitClangAttrTextNodeDump(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Attribute text node dumper", OS, Records); | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| if (R.getValueAsBit("HasCustomTextNodeDump")) | |
| continue; | |
| // If the attribute has a semantically-meaningful name (which is determined | |
| // by whether there is a Spelling enumeration for it), then write out the | |
| // spelling used for the attribute. | |
| std::string FunctionContent; | |
| raw_string_ostream SS(FunctionContent); | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); | |
| if (Spellings.size() > 1 && !SpellingNamesAreCommon(Spellings)) | |
| SS << " OS << \" \" << A->getSpelling();\n"; | |
| std::vector<const Record *> Args = R.getValueAsListOfDefs("Args"); | |
| for (const auto *Arg : Args) | |
| createArgument(*Arg, R.getName())->writeDump(SS); | |
| if (Attr->getValueAsBit("AcceptsExprPack")) | |
| VariadicExprArgument("DelayedArgs", R.getName()).writeDump(OS); | |
| if (SS.tell()) { | |
| OS << " void Visit" << R.getName() << "Attr(const " << R.getName() | |
| << "Attr *A) {\n"; | |
| if (!Args.empty()) | |
| OS << " const auto *SA = cast<" << R.getName() | |
| << "Attr>(A); (void)SA;\n"; | |
| OS << FunctionContent; | |
| OS << " }\n"; | |
| } | |
| } | |
| } | |
| void EmitClangAttrNodeTraverse(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Attribute text node traverser", OS, Records); | |
| for (const auto *Attr : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &R = *Attr; | |
| if (!R.getValueAsBit("ASTNode")) | |
| continue; | |
| std::string FunctionContent; | |
| raw_string_ostream SS(FunctionContent); | |
| std::vector<const Record *> Args = R.getValueAsListOfDefs("Args"); | |
| for (const auto *Arg : Args) | |
| createArgument(*Arg, R.getName())->writeDumpChildren(SS); | |
| if (Attr->getValueAsBit("AcceptsExprPack")) | |
| VariadicExprArgument("DelayedArgs", R.getName()).writeDumpChildren(SS); | |
| if (SS.tell()) { | |
| OS << " void Visit" << R.getName() << "Attr(const " << R.getName() | |
| << "Attr *A) {\n"; | |
| if (!Args.empty()) | |
| OS << " const auto *SA = cast<" << R.getName() | |
| << "Attr>(A); (void)SA;\n"; | |
| OS << FunctionContent; | |
| OS << " }\n"; | |
| } | |
| } | |
| } | |
| void EmitClangAttrParserStringSwitches(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| generateNameToAttrsMap(Records); | |
| emitSourceFileHeader("Parser-related llvm::StringSwitch cases", OS, Records); | |
| emitClangAttrArgContextList(Records, OS); | |
| emitClangAttrIdentifierArgList(Records, OS); | |
| emitClangAttrUnevaluatedStringLiteralList(Records, OS); | |
| emitClangAttrVariadicIdentifierArgList(Records, OS); | |
| emitClangAttrThisIsaIdentifierArgList(Records, OS); | |
| emitClangAttrAcceptsExprPack(Records, OS); | |
| emitClangAttrTypeArgList(Records, OS); | |
| emitClangAttrLateParsedList(Records, OS); | |
| emitClangAttrLateParsedExperimentalList(Records, OS); | |
| emitClangAttrParseArgsInFunctionScopeList(Records, OS); | |
| emitClangAttrStrictIdentifierArgList(Records, OS); | |
| } | |
| void EmitClangAttrSubjectMatchRulesParserStringSwitches( | |
| const RecordKeeper &Records, raw_ostream &OS) { | |
| getPragmaAttributeSupport(Records).generateParsingHelpers(OS); | |
| } | |
| void EmitClangAttrDocTable(const RecordKeeper &Records, raw_ostream &OS) { | |
| emitSourceFileHeader("Clang attribute documentation", OS, Records); | |
| for (const auto *A : Records.getAllDerivedDefinitions("Attr")) { | |
| if (!A->getValueAsBit("ASTNode")) | |
| continue; | |
| std::vector<const Record *> Docs = A->getValueAsListOfDefs("Documentation"); | |
| assert(!Docs.empty()); | |
| // Only look at the first documentation if there are several. | |
| // (Currently there's only one such attr, revisit if this becomes common). | |
| StringRef Text = | |
| Docs.front()->getValueAsOptionalString("Content").value_or(""); | |
| OS << "\nstatic const char AttrDoc_" << A->getName() << "[] = " | |
| << "R\"reST(" << Text.trim() << ")reST\";\n"; | |
| } | |
| } | |
| enum class SpellingKind : size_t { | |
| GNU, | |
| CXX11, | |
| C23, | |
| Declspec, | |
| Microsoft, | |
| Keyword, | |
| Pragma, | |
| HLSLAnnotation, | |
| NumSpellingKinds | |
| }; | |
| static const size_t NumSpellingKinds = (size_t)SpellingKind::NumSpellingKinds; | |
| class SpellingList { | |
| std::array<std::vector<std::string>, NumSpellingKinds> Spellings; | |
| public: | |
| ArrayRef<std::string> operator[](SpellingKind K) const { | |
| return Spellings[(size_t)K]; | |
| } | |
| void add(const Record &Attr, const FlattenedSpelling &Spelling) { | |
| SpellingKind Kind = | |
| StringSwitch<SpellingKind>(Spelling.variety()) | |
| .Case("GNU", SpellingKind::GNU) | |
| .Case("CXX11", SpellingKind::CXX11) | |
| .Case("C23", SpellingKind::C23) | |
| .Case("Declspec", SpellingKind::Declspec) | |
| .Case("Microsoft", SpellingKind::Microsoft) | |
| .Case("Keyword", SpellingKind::Keyword) | |
| .Case("Pragma", SpellingKind::Pragma) | |
| .Case("HLSLAnnotation", SpellingKind::HLSLAnnotation); | |
| std::string Name; | |
| StringRef NameSpace = Spelling.nameSpace(); | |
| if (!NameSpace.empty()) { | |
| Name = NameSpace; | |
| switch (Kind) { | |
| case SpellingKind::CXX11: | |
| case SpellingKind::C23: | |
| Name += "::"; | |
| break; | |
| case SpellingKind::Pragma: | |
| Name = " "; | |
| break; | |
| default: | |
| PrintFatalError(Attr.getLoc(), "Unexpected namespace in spelling"); | |
| } | |
| } | |
| Name += Spelling.name(); | |
| Spellings[(size_t)Kind].push_back(Name); | |
| } | |
| void merge(const SpellingList &Other) { | |
| for (size_t Kind = 0; Kind < NumSpellingKinds; ++Kind) { | |
| Spellings[Kind].insert(Spellings[Kind].end(), | |
| Other.Spellings[Kind].begin(), | |
| Other.Spellings[Kind].end()); | |
| } | |
| } | |
| bool hasSpelling() const { | |
| return llvm::any_of(Spellings, [](const auto &L) { return !L.empty(); }); | |
| } | |
| }; | |
| class DocumentationData { | |
| public: | |
| const Record *Documentation; | |
| const Record *Attribute; | |
| std::string Heading; | |
| SpellingList SupportedSpellings; | |
| DocumentationData(const Record &Documentation, const Record &Attribute, | |
| std::pair<std::string, SpellingList> HeadingAndSpellings) | |
| : Documentation(&Documentation), Attribute(&Attribute), | |
| Heading(std::move(HeadingAndSpellings.first)), | |
| SupportedSpellings(std::move(HeadingAndSpellings.second)) {} | |
| }; | |
| static void WriteCategoryHeader(const Record *DocCategory, | |
| raw_ostream &OS) { | |
| const StringRef Name = DocCategory->getValueAsString("Name"); | |
| OS << "## " << Name << "\n\n"; | |
| // If there is content, print that as well. | |
| const StringRef ContentStr = DocCategory->getValueAsString("Content"); | |
| // Trim leading and trailing newlines and spaces. | |
| OS << ContentStr.trim(); | |
| OS << "\n\n"; | |
| } | |
| static std::pair<std::string, SpellingList> | |
| GetAttributeHeadingAndSpellings(const Record &Documentation, | |
| const Record &Attribute, | |
| StringRef Cat) { | |
| // FIXME: there is no way to have a per-spelling category for the attribute | |
| // documentation. This may not be a limiting factor since the spellings | |
| // should generally be consistently applied across the category. | |
| if (Cat == "HLSL Semantics") { | |
| if (!Attribute.getName().starts_with("HLSL")) | |
| PrintFatalError(Attribute.getLoc(), | |
| "HLSL semantic attribute name must start with HLSL"); | |
| assert(Attribute.getName().size() > 4); | |
| std::string Name = Attribute.getName().substr(4).str(); | |
| return std::make_pair(std::move(Name), SpellingList()); | |
| } | |
| std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attribute); | |
| if (Spellings.empty()) | |
| PrintFatalError(Attribute.getLoc(), | |
| "Attribute has no supported spellings; cannot be " | |
| "documented"); | |
| // Determine the heading to be used for this attribute. | |
| std::string Heading = Documentation.getValueAsString("Heading").str(); | |
| if (Heading.empty()) { | |
| // If there's only one spelling, we can simply use that. | |
| if (Spellings.size() == 1) | |
| Heading = Spellings.begin()->name(); | |
| else { | |
| std::set<std::string> Uniques; | |
| for (const FlattenedSpelling &FS : Spellings) { | |
| std::string Spelling = | |
| NormalizeNameForSpellingComparison(FS.name()).str(); | |
| Uniques.insert(Spelling); | |
| } | |
| // If the semantic map has only one spelling, that is sufficient for our | |
| // needs. | |
| if (Uniques.size() == 1) | |
| Heading = *Uniques.begin(); | |
| // If it's in the undocumented category, just construct a header by | |
| // concatenating all the spellings. Might not be great, but better than | |
| // nothing. | |
| else if (Cat == "Undocumented") | |
| Heading = join(Uniques.begin(), Uniques.end(), ", "); | |
| } | |
| } | |
| // If the heading is still empty, it is an error. | |
| if (Heading.empty()) | |
| PrintFatalError(Attribute.getLoc(), | |
| "This attribute requires a heading to be specified"); | |
| SpellingList SupportedSpellings; | |
| for (const auto &I : Spellings) | |
| SupportedSpellings.add(Attribute, I); | |
| return std::make_pair(std::move(Heading), std::move(SupportedSpellings)); | |
| } | |
| static void WriteDocumentation(const RecordKeeper &Records, | |
| const DocumentationData &Doc, raw_ostream &OS) { | |
| if (StringRef Label = Doc.Documentation->getValueAsString("Label"); | |
| !Label.empty()) | |
| OS << "(" << Label << ")=\n\n"; | |
| OS << "### " << Doc.Heading << "\n\n"; | |
| if (Doc.SupportedSpellings.hasSpelling()) { | |
| // List what spelling syntaxes the attribute supports. | |
| // Note: "#pragma clang attribute" is handled outside the spelling kinds | |
| // loop so it must be last. | |
| OS << ":::{list-table} Supported Syntaxes\n"; | |
| OS << ":header-rows: 1\n\n"; | |
| OS << "* - GNU\n"; | |
| OS << " - C++11\n"; | |
| OS << " - C23\n"; | |
| OS << " - `__declspec`\n"; | |
| OS << " - Keyword\n"; | |
| OS << " - `#pragma`\n"; | |
| OS << " - HLSL Annotation\n"; | |
| OS << " - `#pragma clang attribute`\n"; | |
| OS << "*"; | |
| for (size_t Kind = 0; Kind != NumSpellingKinds; ++Kind) { | |
| SpellingKind K = (SpellingKind)Kind; | |
| // TODO: List Microsoft (IDL-style attribute) spellings once we fully | |
| // support them. | |
| if (K == SpellingKind::Microsoft) | |
| continue; | |
| OS << " - "; | |
| bool PrintedAny = false; | |
| for (StringRef Spelling : Doc.SupportedSpellings[K]) { | |
| if (PrintedAny) | |
| OS << " <br/> "; | |
| OS << "`" << Spelling << "`"; | |
| PrintedAny = true; | |
| } | |
| OS << "\n "; | |
| } | |
| OS << " - "; | |
| if (getPragmaAttributeSupport(Records).isAttributedSupported( | |
| *Doc.Attribute)) | |
| OS << "Yes"; | |
| OS << "\n:::\n\n"; | |
| } | |
| // If the attribute is deprecated, print a message about it, and possibly | |
| // provide a replacement attribute. | |
| if (!Doc.Documentation->isValueUnset("Deprecated")) { | |
| OS << "This attribute has been deprecated, and may be removed in a future " | |
| << "version of Clang."; | |
| const Record &Deprecated = *Doc.Documentation->getValueAsDef("Deprecated"); | |
| const StringRef Replacement = Deprecated.getValueAsString("Replacement"); | |
| if (!Replacement.empty()) | |
| OS << " This attribute has been superseded by `" << Replacement << "`."; | |
| OS << "\n\n"; | |
| } | |
| const StringRef ContentStr = Doc.Documentation->getValueAsString("Content"); | |
| // Trim leading and trailing newlines and spaces. | |
| OS << ContentStr.trim(); | |
| OS << "\n\n\n"; | |
| } | |
| void GetListOfUndocumentedAttributes( | |
| const RecordKeeper &Records, | |
| std::vector<const Record *> &UndocumentedAttrs) { | |
| const Record *Documentation = Records.getDef("GlobalDocumentation"); | |
| if (!Documentation) { | |
| PrintFatalError("The Documentation top-level definition is missing."); | |
| return; | |
| } | |
| for (const auto *A : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &Attr = *A; | |
| std::vector<const Record *> Docs = | |
| Attr.getValueAsListOfDefs("Documentation"); | |
| for (const auto *D : Docs) { | |
| const Record &Doc = *D; | |
| const Record *Category = Doc.getValueAsDef("Category"); | |
| if (Category->getValueAsString("Name") == "Undocumented") | |
| UndocumentedAttrs.push_back(A); | |
| } | |
| } | |
| } | |
| void EmitClangUndocumentedAttrList(const llvm::RecordKeeper &Records, | |
| llvm::raw_ostream &OS) { | |
| // Emit a newline separated list of attributes whose Documentation is set to | |
| // Undocumented. | |
| std::vector<const Record *> UndocumentedAttrs; | |
| GetListOfUndocumentedAttributes(Records, UndocumentedAttrs); | |
| // Print a small header; this helps catch the situation where someone adds an | |
| // attribute without documentation but it is alphabetically before the first | |
| // attribute in the test file. | |
| OS << "Undocumented attributes:\n"; | |
| for (const auto *A : UndocumentedAttrs) { | |
| OS << A->getName() << "\n"; | |
| } | |
| // Also print the count; this helps catch attributes after the last one in | |
| // the test file. | |
| OS << "Total: " << UndocumentedAttrs.size() << "\n"; | |
| } | |
| void EmitClangAttrDocs(const RecordKeeper &Records, raw_ostream &OS) { | |
| // Get the documentation introduction paragraph. | |
| const Record *Documentation = Records.getDef("GlobalDocumentation"); | |
| if (!Documentation) { | |
| PrintFatalError("The Documentation top-level definition is missing, " | |
| "no documentation will be generated."); | |
| return; | |
| } | |
| OS << Documentation->getValueAsString("Intro") << "\n"; | |
| // Gather the Documentation lists from each of the attributes, based on the | |
| // category provided. | |
| struct CategoryLess { | |
| bool operator()(const Record *L, const Record *R) const { | |
| return L->getValueAsString("Name") < R->getValueAsString("Name"); | |
| } | |
| }; | |
| std::map<const Record *, std::map<uint32_t, DocumentationData>, CategoryLess> | |
| MergedDocs; | |
| std::vector<DocumentationData> UndocumentedDocs; | |
| const Record *UndocumentedCategory = nullptr; | |
| // Collect documentation data, grouping by category and heading. | |
| for (const auto *A : Records.getAllDerivedDefinitions("Attr")) { | |
| const Record &Attr = *A; | |
| std::vector<const Record *> Docs = | |
| Attr.getValueAsListOfDefs("Documentation"); | |
| for (const auto *D : Docs) { | |
| const Record &Doc = *D; | |
| const Record *Category = Doc.getValueAsDef("Category"); | |
| // If the category is "InternalOnly", then there cannot be any other | |
| // documentation categories (otherwise, the attribute would be | |
| // emitted into the docs). | |
| StringRef Cat = Category->getValueAsString("Name"); | |
| if (Cat == "InternalOnly" && Docs.size() > 1) | |
| PrintFatalError(Doc.getLoc(), | |
| "Attribute is \"InternalOnly\", but has multiple " | |
| "documentation categories"); | |
| if (Cat == "InternalOnly") | |
| continue; | |
| // Track the Undocumented category Record for later grouping | |
| if (Cat == "Undocumented" && !UndocumentedCategory) | |
| UndocumentedCategory = Category; | |
| // Generate Heading and Spellings. | |
| auto HeadingAndSpellings = | |
| GetAttributeHeadingAndSpellings(Doc, Attr, Cat); | |
| // Handle Undocumented category separately - no content merging | |
| if (Cat == "Undocumented" && UndocumentedCategory) { | |
| UndocumentedDocs.push_back( | |
| DocumentationData(Doc, Attr, std::move(HeadingAndSpellings))); | |
| continue; | |
| } | |
| auto &CategoryDocs = MergedDocs[Category]; | |
| std::string key = Doc.getValueAsString("Content").str(); | |
| uint32_t keyHash = llvm::hash_value(key); | |
| // If the content already exists, merge the documentation. | |
| auto It = CategoryDocs.find(keyHash); | |
| if (It != CategoryDocs.end()) { | |
| // Merge heading | |
| if (It->second.Heading != HeadingAndSpellings.first) | |
| It->second.Heading += ", " + HeadingAndSpellings.first; | |
| // Merge spellings | |
| It->second.SupportedSpellings.merge(HeadingAndSpellings.second); | |
| // Merge content | |
| It->second.Documentation = &Doc; // Update reference | |
| } else { | |
| // Create new entry for unique content | |
| CategoryDocs.emplace(keyHash, | |
| DocumentationData(Doc, Attr, HeadingAndSpellings)); | |
| } | |
| } | |
| } | |
| std::map<const Record *, std::vector<DocumentationData>, CategoryLess> | |
| SplitDocs; | |
| for (auto &CategoryPair : MergedDocs) { | |
| std::vector<DocumentationData> MD; | |
| for (auto &DocPair : CategoryPair.second) | |
| MD.push_back(std::move(DocPair.second)); | |
| SplitDocs.emplace(CategoryPair.first, MD); | |
| } | |
| // Append Undocumented category entries | |
| if (!UndocumentedDocs.empty() && UndocumentedCategory) { | |
| SplitDocs.emplace(UndocumentedCategory, UndocumentedDocs); | |
| } | |
| // Having split the attributes out based on what documentation goes where, | |
| // we can begin to generate sections of documentation. | |
| for (auto &I : SplitDocs) { | |
| WriteCategoryHeader(I.first, OS); | |
| sort(I.second, | |
| [](const DocumentationData &D1, const DocumentationData &D2) { | |
| return D1.Heading < D2.Heading; | |
| }); | |
| // Walk over each of the attributes in the category and write out their | |
| // documentation. | |
| for (const auto &Doc : I.second) | |
| WriteDocumentation(Records, Doc, OS); | |
| } | |
| } | |
| void EmitTestPragmaAttributeSupportedAttributes(const RecordKeeper &Records, | |
| raw_ostream &OS) { | |
| PragmaClangAttributeSupport Support = getPragmaAttributeSupport(Records); | |
| ParsedAttrMap Attrs = getParsedAttrList(Records); | |
| OS << "#pragma clang attribute supports the following attributes:\n"; | |
| for (const auto &I : Attrs) { | |
| if (!Support.isAttributedSupported(*I.second)) | |
| continue; | |
| OS << I.first; | |
| if (I.second->isValueUnset("Subjects")) { | |
| OS << " ()\n"; | |
| continue; | |
| } | |
| const Record *SubjectObj = I.second->getValueAsDef("Subjects"); | |
| OS << " ("; | |
| ListSeparator LS; | |
| for (const auto &Subject : | |
| enumerate(SubjectObj->getValueAsListOfDefs("Subjects"))) { | |
| if (!isSupportedPragmaClangAttributeSubject(*Subject.value())) | |
| continue; | |
| OS << LS; | |
| PragmaClangAttributeSupport::RuleOrAggregateRuleSet &RuleSet = | |
| Support.SubjectsToRules.find(Subject.value())->getSecond(); | |
| if (RuleSet.isRule()) { | |
| OS << RuleSet.getRule().getEnumValueName(); | |
| continue; | |
| } | |
| OS << "("; | |
| for (const auto &Rule : enumerate(RuleSet.getAggregateRuleSet())) { | |
| if (Rule.index()) | |
| OS << ", "; | |
| OS << Rule.value().getEnumValueName(); | |
| } | |
| OS << ")"; | |
| } | |
| OS << ")\n"; | |
| } | |
| OS << "End of supported attributes.\n"; | |
| } | |
| } // end namespace clang |