diff options
Diffstat (limited to 'clang/utils/TableGen/ClangAttrEmitter.cpp')
-rw-r--r-- | clang/utils/TableGen/ClangAttrEmitter.cpp | 853 |
1 files changed, 466 insertions, 387 deletions
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 4c3742c8e339..bd20e447a950 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -48,22 +48,19 @@ namespace { class FlattenedSpelling { std::string V, N, NS; - bool K; + bool K = false; public: FlattenedSpelling(const std::string &Variety, const std::string &Name, const std::string &Namespace, bool KnownToGCC) : V(Variety), N(Name), NS(Namespace), K(KnownToGCC) {} - explicit FlattenedSpelling(const Record &Spelling) : - V(Spelling.getValueAsString("Variety")), - N(Spelling.getValueAsString("Name")) { - + explicit FlattenedSpelling(const Record &Spelling) + : V(std::string(Spelling.getValueAsString("Variety"))), + N(std::string(Spelling.getValueAsString("Name"))) { assert(V != "GCC" && V != "Clang" && "Given a GCC spelling, which means this hasn't been flattened!"); if (V == "CXX11" || V == "C2x" || V == "Pragma") - NS = Spelling.getValueAsString("Namespace"); - bool Unset; - K = Spelling.getValueAsBitOrUnset("KnownToGCC", Unset); + NS = std::string(Spelling.getValueAsString("Namespace")); } const std::string &variety() const { return V; } @@ -83,14 +80,15 @@ GetFlattenedSpellings(const Record &Attr) { StringRef Variety = Spelling->getValueAsString("Variety"); StringRef Name = Spelling->getValueAsString("Name"); if (Variety == "GCC") { - // Gin up two new spelling objects to add into the list. - Ret.emplace_back("GNU", Name, "", true); - Ret.emplace_back("CXX11", Name, "gnu", true); + Ret.emplace_back("GNU", std::string(Name), "", true); + Ret.emplace_back("CXX11", std::string(Name), "gnu", true); + if (Spelling->getValueAsBit("AllowInC")) + Ret.emplace_back("C2x", std::string(Name), "gnu", true); } else if (Variety == "Clang") { - Ret.emplace_back("GNU", Name, "", false); - Ret.emplace_back("CXX11", Name, "clang", false); + Ret.emplace_back("GNU", std::string(Name), "", false); + Ret.emplace_back("CXX11", std::string(Name), "clang", false); if (Spelling->getValueAsBit("AllowInC")) - Ret.emplace_back("C2x", Name, "clang", false); + Ret.emplace_back("C2x", std::string(Name), "clang", false); } else Ret.push_back(FlattenedSpelling(*Spelling)); } @@ -100,14 +98,16 @@ GetFlattenedSpellings(const Record &Attr) { static std::string ReadPCHRecord(StringRef type) { return StringSwitch<std::string>(type) - .EndsWith("Decl *", "Record.GetLocalDeclAs<" - + std::string(type, 0, type.size()-1) + ">(Record.readInt())") - .Case("TypeSourceInfo *", "Record.readTypeSourceInfo()") - .Case("Expr *", "Record.readExpr()") - .Case("IdentifierInfo *", "Record.readIdentifier()") - .Case("StringRef", "Record.readString()") - .Case("ParamIdx", "ParamIdx::deserialize(Record.readInt())") - .Default("Record.readInt()"); + .EndsWith("Decl *", "Record.GetLocalDeclAs<" + + std::string(type.data(), 0, type.size() - 1) + + ">(Record.readInt())") + .Case("TypeSourceInfo *", "Record.readTypeSourceInfo()") + .Case("Expr *", "Record.readExpr()") + .Case("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. @@ -119,14 +119,20 @@ static StringRef getStorageType(StringRef 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(" + std::string(name) + ");\n") - .Case("TypeSourceInfo *", "AddTypeSourceInfo(" + std::string(name) + ");\n") - .Case("Expr *", "AddStmt(" + std::string(name) + ");\n") - .Case("IdentifierInfo *", "AddIdentifierRef(" + std::string(name) + ");\n") - .Case("StringRef", "AddString(" + std::string(name) + ");\n") - .Case("ParamIdx", "push_back(" + std::string(name) + ".serialize());\n") - .Default("push_back(" + std::string(name) + ");\n"); + return "Record." + + StringSwitch<std::string>(type) + .EndsWith("Decl *", "AddDeclRef(" + std::string(name) + ");\n") + .Case("TypeSourceInfo *", + "AddTypeSourceInfo(" + std::string(name) + ");\n") + .Case("Expr *", "AddStmt(" + std::string(name) + ");\n") + .Case("IdentifierInfo *", + "AddIdentifierRef(" + std::string(name) + ");\n") + .Case("StringRef", "AddString(" + std::string(name) + ");\n") + .Case("ParamIdx", + "push_back(" + std::string(name) + ".serialize());\n") + .Case("OMPTraitInfo *", + "writeOMPTraitInfo(" + std::string(name) + ");\n") + .Default("push_back(" + std::string(name) + ");\n"); } // Normalize attribute name by removing leading and trailing @@ -167,7 +173,7 @@ static ParsedAttrMap getParsedAttrList(const RecordKeeper &Records, std::string AN; if (Attr->isSubClassOf("TargetSpecificAttr") && !Attr->isValueUnset("ParseKind")) { - AN = Attr->getValueAsString("ParseKind"); + AN = std::string(Attr->getValueAsString("ParseKind")); // If this attribute has already been handled, it does not need to be // handled again. @@ -196,8 +202,8 @@ namespace { public: Argument(const Record &Arg, StringRef Attr) - : lowerName(Arg.getValueAsString("Name")), upperName(lowerName), - attrName(Attr), isOpt(false), Fake(false) { + : lowerName(std::string(Arg.getValueAsString("Name"))), + upperName(lowerName), attrName(Attr), isOpt(false), Fake(false) { if (!lowerName.empty()) { lowerName[0] = std::tolower(lowerName[0]); upperName[0] = std::toupper(upperName[0]); @@ -299,8 +305,9 @@ namespace { } void writePCHWrite(raw_ostream &OS) const override { - OS << " " << WritePCHRecord(type, "SA->get" + - std::string(getUpperName()) + "()"); + OS << " " + << WritePCHRecord(type, + "SA->get" + std::string(getUpperName()) + "()"); } std::string getIsOmitted() const override { @@ -331,9 +338,9 @@ namespace { } void writeDump(raw_ostream &OS) const override { - if (type == "FunctionDecl *" || type == "NamedDecl *") { + if (StringRef(type).endswith("Decl *")) { OS << " OS << \" \";\n"; - OS << " dumpBareDeclRef(SA->get" << getUpperName() << "());\n"; + OS << " dumpBareDeclRef(SA->get" << getUpperName() << "());\n"; } else if (type == "IdentifierInfo *") { // Some non-optional (comma required) identifier arguments can be the // empty string but are then recorded as a nullptr. @@ -355,6 +362,8 @@ namespace { 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!"); } @@ -416,8 +425,8 @@ namespace { } void writeCtorBody(raw_ostream &OS) const override { - OS << " if (!" << getUpperName() << ".empty())\n"; - OS << " std::memcpy(" << getLowerName() << ", " << getUpperName() + OS << " if (!" << getUpperName() << ".empty())\n"; + OS << " std::memcpy(" << getLowerName() << ", " << getUpperName() << ".data(), " << getLowerName() << "Length);\n"; } @@ -471,6 +480,7 @@ namespace { 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"; @@ -495,12 +505,21 @@ namespace { OS << " if (is" << getLowerName() << "Expr)\n"; OS << " return " << getLowerName() << "Expr && (" << getLowerName() << "Expr->isValueDependent() || " << getLowerName() - << "Expr->isTypeDependent());\n"; + << "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"; + // FIXME: Do not do the calculation here // FIXME: Handle types correctly // A null pointer means maximum alignment @@ -520,11 +539,11 @@ namespace { 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" + << " 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" + << " return false;\n" << " }\n"; } @@ -642,7 +661,7 @@ namespace { VariadicArgument(const Record &Arg, StringRef Attr, std::string T) : Argument(Arg, Attr), Type(std::move(T)), ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"), - RangeName(getLowerName()) {} + RangeName(std::string(getLowerName())) {} const std::string &getType() const { return Type; } const std::string &getArgName() const { return ArgName; } @@ -653,7 +672,7 @@ namespace { 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"; @@ -681,8 +700,8 @@ namespace { } void writeCtorBody(raw_ostream &OS) const override { - OS << " std::copy(" << getUpperName() << ", " << getUpperName() - << " + " << ArgSizeName << ", " << ArgName << ");\n"; + OS << " std::copy(" << getUpperName() << ", " << getUpperName() << " + " + << ArgSizeName << ", " << ArgName << ");\n"; } void writeCtorInitializers(raw_ostream &OS) const override { @@ -719,8 +738,8 @@ namespace { // 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()); - std::string StorageName = getLowerName(); + std::string StorageType = std::string(getStorageType(getType())); + std::string StorageName = std::string(getLowerName()); if (StorageType != getType()) { StorageName += "Storage"; OS << " SmallVector<" << StorageType << ", 4> " @@ -805,11 +824,10 @@ namespace { public: EnumArgument(const Record &Arg, StringRef Attr) - : Argument(Arg, Attr), type(Arg.getValueAsString("Type")), - values(Arg.getValueAsListOfStrings("Values")), - enums(Arg.getValueAsListOfStrings("Enums")), - uniques(uniqueEnumsInOrder(enums)) - { + : Argument(Arg, Attr), type(std::string(Arg.getValueAsString("Type"))), + values(Arg.getValueAsListOfStrings("Values")), + enums(Arg.getValueAsListOfStrings("Enums")), + uniques(uniqueEnumsInOrder(enums)) { // FIXME: Emit a proper error assert(!uniques.empty()); } @@ -885,40 +903,48 @@ namespace { OS << " }\n"; } - void writeConversion(raw_ostream &OS) const { - OS << " static bool ConvertStrTo" << type << "(StringRef Val, "; - OS << type << " &Out) {\n"; - OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<"; + void writeConversion(raw_ostream &OS, bool Header) const { + if (Header) { + OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type + << " &Out);\n"; + OS << " static const char *Convert" << type << "ToStr(" << type + << " Val);\n"; + return; + } + + OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type + << "(StringRef Val, " << type << " &Out) {\n"; + OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<"; OS << type << ">>(Val)\n"; for (size_t I = 0; I < enums.size(); ++I) { - OS << " .Case(\"" << values[I] << "\", "; + OS << " .Case(\"" << values[I] << "\", "; OS << getAttrName() << "Attr::" << enums[I] << ")\n"; } - OS << " .Default(Optional<" << type << ">());\n"; - OS << " if (R) {\n"; - OS << " Out = *R;\n return true;\n }\n"; - OS << " return false;\n"; - OS << " }\n\n"; + OS << " .Default(Optional<" << type << ">());\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 << " static const char *Convert" << type << "ToStr(" - << type << " Val) {\n" - << " switch(Val) {\n"; + OS << "const char *" << getAttrName() << "Attr::Convert" << type + << "ToStr(" << type << " 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 " << getAttrName() << "Attr::" << enums[I] - << ": return \"" << values[I] << "\";\n"; + OS << " case " << getAttrName() << "Attr::" << enums[I] + << ": return \"" << values[I] << "\";\n"; } - OS << " }\n" - << " llvm_unreachable(\"No enumerator with that value\");\n" - << " }\n"; + OS << " }\n" + << " llvm_unreachable(\"No enumerator with that value\");\n" + << "}\n"; } }; - + class VariadicEnumArgument: public VariadicArgument { std::string type, QualifiedTypeName; std::vector<StringRef> values, enums, uniques; @@ -934,20 +960,20 @@ namespace { public: VariadicEnumArgument(const Record &Arg, StringRef Attr) - : VariadicArgument(Arg, Attr, Arg.getValueAsString("Type")), - type(Arg.getValueAsString("Type")), - values(Arg.getValueAsListOfStrings("Values")), - enums(Arg.getValueAsListOfStrings("Enums")), - uniques(uniqueEnumsInOrder(enums)) - { + : VariadicArgument(Arg, Attr, + std::string(Arg.getValueAsString("Type"))), + type(std::string(Arg.getValueAsString("Type"))), + values(Arg.getValueAsListOfStrings("Values")), + enums(Arg.getValueAsListOfStrings("Enums")), + uniques(uniqueEnumsInOrder(enums)) { QualifiedTypeName = getAttrName().str() + "Attr::" + type; - + // FIXME: Emit a proper error assert(!uniques.empty()); } bool isVariadicEnumArg() const override { return true; } - + void writeDeclarations(raw_ostream &OS) const override { auto i = uniques.cbegin(), e = uniques.cend(); // The last one needs to not have a comma. @@ -960,7 +986,7 @@ namespace { OS << " " << *e << "\n"; OS << " };\n"; OS << "private:\n"; - + VariadicArgument::writeDeclarations(OS); } @@ -997,33 +1023,42 @@ namespace { OS << " " << WritePCHRecord(QualifiedTypeName, "(*i)"); } - void writeConversion(raw_ostream &OS) const { - OS << " static bool ConvertStrTo" << type << "(StringRef Val, "; + void writeConversion(raw_ostream &OS, bool Header) const { + if (Header) { + OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type + << " &Out);\n"; + OS << " static const char *Convert" << type << "ToStr(" << type + << " Val);\n"; + return; + } + + OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type + << "(StringRef Val, "; OS << type << " &Out) {\n"; - OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<"; + OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<"; OS << type << ">>(Val)\n"; for (size_t I = 0; I < enums.size(); ++I) { - OS << " .Case(\"" << values[I] << "\", "; + OS << " .Case(\"" << values[I] << "\", "; OS << getAttrName() << "Attr::" << enums[I] << ")\n"; } - OS << " .Default(Optional<" << type << ">());\n"; - OS << " if (R) {\n"; - OS << " Out = *R;\n return true;\n }\n"; - OS << " return false;\n"; - OS << " }\n\n"; - - OS << " static const char *Convert" << type << "ToStr(" - << type << " Val) {\n" - << " switch(Val) {\n"; + OS << " .Default(Optional<" << type << ">());\n"; + OS << " if (R) {\n"; + OS << " Out = *R;\n return true;\n }\n"; + OS << " return false;\n"; + OS << "}\n\n"; + + OS << "const char *" << getAttrName() << "Attr::Convert" << type + << "ToStr(" << type << " Val) {\n" + << " switch(Val) {\n"; SmallDenseSet<StringRef, 8> Uniques; for (size_t I = 0; I < enums.size(); ++I) { if (Uniques.insert(enums[I]).second) - OS << " case " << getAttrName() << "Attr::" << enums[I] - << ": return \"" << values[I] << "\";\n"; + OS << " case " << getAttrName() << "Attr::" << enums[I] + << ": return \"" << values[I] << "\";\n"; } - OS << " }\n" - << " llvm_unreachable(\"No enumerator with that value\");\n" - << " }\n"; + OS << " }\n" + << " llvm_unreachable(\"No enumerator with that value\");\n" + << "}\n"; } }; @@ -1037,7 +1072,7 @@ namespace { OS << " VersionTuple get" << getUpperName() << "() const {\n"; OS << " return " << getLowerName() << ";\n"; OS << " }\n"; - OS << " void set" << getUpperName() + OS << " void set" << getUpperName() << "(ASTContext &C, VersionTuple V) {\n"; OS << " " << getLowerName() << " = V;\n"; OS << " }"; @@ -1199,15 +1234,15 @@ namespace { {} 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"; + 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 { @@ -1241,8 +1276,9 @@ namespace { } void writePCHWrite(raw_ostream &OS) const override { - OS << " " << WritePCHRecord( - getType(), "SA->get" + std::string(getUpperName()) + "Loc()"); + OS << " " + << WritePCHRecord(getType(), + "SA->get" + std::string(getUpperName()) + "Loc()"); } }; @@ -1263,10 +1299,9 @@ createArgument(const Record &Arg, StringRef Attr, Ptr = std::make_unique<EnumArgument>(Arg, Attr); else if (ArgName == "ExprArgument") Ptr = std::make_unique<ExprArgument>(Arg, Attr); - else if (ArgName == "FunctionArgument") - Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "FunctionDecl *"); - else if (ArgName == "NamedArgument") - Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "NamedDecl *"); + 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, "IdentifierInfo *"); else if (ArgName == "DefaultBoolArgument") @@ -1303,6 +1338,8 @@ createArgument(const Record &Arg, StringRef Attr, Ptr = std::make_unique<VariadicIdentifierArgument>(Arg, Attr); else if (ArgName == "VersionArgument") Ptr = std::make_unique<VersionArgument>(Arg, Attr); + else if (ArgName == "OMPTraitInfoArgument") + Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "OMPTraitInfo *"); if (!Ptr) { // Search in reverse order so that the most-derived type is handled first. @@ -1341,7 +1378,7 @@ static void writeDeprecatedAttrValue(raw_ostream &OS, std::string &Variety) { OS << " OS << \""; } -static void writeGetSpellingFunction(Record &R, raw_ostream &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"; @@ -1365,7 +1402,7 @@ static void writeGetSpellingFunction(Record &R, raw_ostream &OS) { } static void -writePrettyPrintFunction(Record &R, +writePrettyPrintFunction(const Record &R, const std::vector<std::unique_ptr<Argument>> &Args, raw_ostream &OS) { std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); @@ -1577,11 +1614,12 @@ static void writeAttrAccessorDefinition(const Record &R, raw_ostream &OS) { static bool SpellingNamesAreCommon(const std::vector<FlattenedSpelling>& Spellings) { assert(!Spellings.empty() && "An empty list of spellings was provided"); - std::string FirstName = NormalizeNameForSpellingComparison( - Spellings.front().name()); + std::string FirstName = + std::string(NormalizeNameForSpellingComparison(Spellings.front().name())); for (const auto &Spelling : llvm::make_range(std::next(Spellings.begin()), Spellings.end())) { - std::string Name = NormalizeNameForSpellingComparison(Spelling.name()); + std::string Name = + std::string(NormalizeNameForSpellingComparison(Spelling.name())); if (Name != FirstName) return false; } @@ -1727,7 +1765,7 @@ struct AttributeSubjectMatchRule { } std::string getSpelling() const { - std::string Result = MetaSubject->getValueAsString("Name"); + std::string Result = std::string(MetaSubject->getValueAsString("Name")); if (isSubRule()) { Result += '('; if (isNegatedSubRule()) @@ -1752,7 +1790,7 @@ struct AttributeSubjectMatchRule { } if (isAbstractRule()) Result += "_abstract"; - return Result.str(); + return std::string(Result.str()); } std::string getEnumValue() const { return "attr::" + getEnumValueName(); } @@ -1801,7 +1839,7 @@ struct PragmaClangAttributeSupport { void emitMatchRuleList(raw_ostream &OS); - std::string generateStrictConformsTo(const Record &Attr, raw_ostream &OS); + void generateStrictConformsTo(const Record &Attr, raw_ostream &OS); void generateParsingHelpers(raw_ostream &OS); }; @@ -1950,6 +1988,11 @@ static std::string GenerateTestExpression(ArrayRef<Record *> LangOpts) { 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"); @@ -1962,21 +2005,17 @@ static std::string GenerateTestExpression(ArrayRef<Record *> LangOpts) { return Test; } -std::string +void PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, raw_ostream &OS) { - if (!isAttributedSupported(Attr)) - return "nullptr"; + 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. - std::string FnName = "matchRulesFor" + Attr.getName().str(); - OS << "static void " << FnName << "(llvm::SmallVectorImpl<std::pair<" + OS << "void getPragmaAttributeMatchRules(" + << "llvm::SmallVectorImpl<std::pair<" << AttributeSubjectMatchRule::EnumName - << ", bool>> &MatchRules, const LangOptions &LangOpts) {\n"; - if (Attr.isValueUnset("Subjects")) { - OS << "}\n\n"; - return FnName; - } + << ", bool>> &MatchRules, const LangOptions &LangOpts) const override {\n"; const Record *SubjectObj = Attr.getValueAsDef("Subjects"); std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); for (const auto *Subject : Subjects) { @@ -1993,7 +2032,6 @@ PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, } } OS << "}\n\n"; - return FnName; } void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) { @@ -2223,13 +2261,8 @@ static void emitClangAttrThisIsaIdentifierArgList(RecordKeeper &Records, OS << "#endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST\n\n"; } -// Emits the class definitions for attributes. -void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Attribute classes' definitions", OS); - - OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n"; - OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n\n"; - +static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, + bool Header) { std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); ParsedAttrMap AttrMap = getParsedAttrList(Records); @@ -2246,10 +2279,10 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { // 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; - + ArrayRef<std::pair<Record *, SMRange>> Supers = R.getSuperClasses(); assert(!Supers.empty() && "Forgot to specify a superclass for the attr"); std::string SuperName; @@ -2258,12 +2291,15 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { const Record *R = Super.first; if (R->getName() != "TargetSpecificAttr" && R->getName() != "DeclOrTypeAttr" && SuperName.empty()) - SuperName = R->getName(); + SuperName = std::string(R->getName()); if (R->getName() == "InheritableAttr") Inheritable = true; } - OS << "class " << R.getName() << "Attr : public " << SuperName << " {\n"; + if (Header) + OS << "class " << R.getName() << "Attr : public " << SuperName << " {\n"; + else + OS << "\n// " << R.getName() << "Attr implementation\n\n"; std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args"); std::vector<std::unique_ptr<Argument>> Args; @@ -2273,8 +2309,10 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { bool HasFakeArg = false; for (const auto *ArgRecord : ArgRecords) { Args.emplace_back(createArgument(*ArgRecord, R.getName())); - Args.back()->writeDeclarations(OS); - OS << "\n\n"; + if (Header) { + Args.back()->writeDeclarations(OS); + OS << "\n\n"; + } // For these purposes, fake takes priority over optional. if (Args.back()->isFake()) { @@ -2284,7 +2322,8 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { } } - OS << "public:\n"; + if (Header) + OS << "public:\n"; std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); @@ -2297,8 +2336,11 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { // This maps spelling index values to semantic Spelling enumerants. SemanticSpellingMap SemanticToSyntacticMap; - if (!ElideSpelling) - OS << CreateSemanticSpellings(Spellings, SemanticToSyntacticMap); + std::string SpellingEnum; + if (Spellings.size() > 1) + SpellingEnum = CreateSemanticSpellings(Spellings, SemanticToSyntacticMap); + if (Header) + OS << SpellingEnum; const auto &ParsedAttrSpellingItr = llvm::find_if( AttrMap, [R](const std::pair<std::string, const Record *> &P) { @@ -2307,9 +2349,14 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { // Emit CreateImplicit factory methods. auto emitCreate = [&](bool Implicit, bool emitFake) { - OS << " static " << R.getName() << "Attr *Create"; - if (Implicit) - OS << "Implicit"; + if (Header) + OS << " static "; + OS << R.getName() << "Attr *"; + if (!Header) + OS << R.getName() << "Attr::"; + OS << "Create"; + if (Implicit) + OS << "Implicit"; OS << "("; OS << "ASTContext &Ctx"; for (auto const &ai : Args) { @@ -2317,8 +2364,17 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { OS << ", "; ai->writeCtorParameters(OS); } - OS << ", const AttributeCommonInfo &CommonInfo = {SourceRange{}}) {\n"; - OS << " auto *A = new (Ctx) " << R.getName(); + OS << ", const AttributeCommonInfo &CommonInfo"; + if (Header) + OS << " = {SourceRange{}}"; + OS << ")"; + if (Header) { + OS << ";\n"; + return; + } + + OS << " {\n"; + OS << " auto *A = new (Ctx) " << R.getName(); OS << "Attr(Ctx, CommonInfo"; for (auto const &ai : Args) { if (ai->isFake() && !emitFake) continue; @@ -2327,18 +2383,23 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { } OS << ");\n"; if (Implicit) { - OS << " A->setImplicit(true);\n"; + OS << " A->setImplicit(true);\n"; } if (Implicit || ElideSpelling) { - OS << " if (!A->isAttributeSpellingListCalculated() && " + OS << " if (!A->isAttributeSpellingListCalculated() && " "!A->getAttrName())\n"; - OS << " A->setAttributeSpellingListIndex(0);\n"; + OS << " A->setAttributeSpellingListIndex(0);\n"; } - OS << " return A;\n }\n\n"; + OS << " return A;\n}\n\n"; }; auto emitCreateNoCI = [&](bool Implicit, bool emitFake) { - OS <<" static " << R.getName() << "Attr *Create"; + if (Header) + OS << " static "; + OS << R.getName() << "Attr *"; + if (!Header) + OS << R.getName() << "Attr::"; + OS << "Create"; if (Implicit) OS << "Implicit"; OS << "("; @@ -2349,12 +2410,19 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { ai->writeCtorParameters(OS); } OS << ", SourceRange Range, AttributeCommonInfo::Syntax Syntax"; - if (!ElideSpelling) - OS << ", " << R.getName() - << "Attr::Spelling S = " - "static_cast<Spelling>(SpellingNotCalculated)"; - OS << ") {\n"; - OS << " AttributeCommonInfo I(Range, "; + if (!ElideSpelling) { + OS << ", " << R.getName() << "Attr::Spelling S"; + if (Header) + OS << " = static_cast<Spelling>(SpellingNotCalculated)"; + } + OS << ")"; + if (Header) { + OS << ";\n"; + return; + } + + OS << " {\n"; + OS << " AttributeCommonInfo I(Range, "; if (ParsedAttrSpellingItr != std::end(AttrMap)) OS << "AT_" << ParsedAttrSpellingItr->first; @@ -2365,7 +2433,7 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { if (!ElideSpelling) OS << ", S"; OS << ");\n"; - OS << " return Create"; + OS << " return Create"; if (Implicit) OS << "Implicit"; OS << "(Ctx"; @@ -2375,7 +2443,7 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { ai->writeImplicitCtorArgs(OS); } OS << ", I);\n"; - OS << " }\n"; + OS << "}\n\n"; }; auto emitCreates = [&](bool emitFake) { @@ -2385,6 +2453,9 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { emitCreateNoCI(false, emitFake); }; + if (Header) + OS << " // Factory methods\n"; + // Emit a CreateImplicit that takes all the arguments. emitCreates(true); @@ -2399,7 +2470,11 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { if (arg->isOptional()) return emitOpt; return true; }; - OS << " " << R.getName() + if (Header) + OS << " "; + else + OS << R.getName() << "Attr::"; + OS << R.getName() << "Attr(ASTContext &Ctx, const AttributeCommonInfo &CommonInfo"; OS << '\n'; for (auto const &ai : Args) { @@ -2409,8 +2484,12 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { OS << "\n"; } - OS << " )\n"; - OS << " : " << SuperName << "(Ctx, CommonInfo, "; + OS << " )"; + if (Header) { + OS << ";\n"; + return; + } + OS << "\n : " << SuperName << "(Ctx, CommonInfo, "; OS << "attr::" << R.getName() << ", " << (R.getValueAsBit("LateParsed") ? "true" : "false"); if (Inheritable) { @@ -2431,14 +2510,17 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { } OS << " {\n"; - + for (auto const &ai : Args) { if (!shouldEmitArg(ai)) continue; ai->writeCtorBody(OS); } - OS << " }\n\n"; + 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); @@ -2446,48 +2528,89 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { // Emit a constructor that takes all the non-fake arguments. if (HasFakeArg) emitCtor(true, false); - + // Emit a constructor that takes all the non-fake, non-optional arguments. if (HasOptArg) emitCtor(false, false); - 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 (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"); - OS << " Spelling getSemanticSpelling() const {\n"; - WriteSemanticSpellingSwitch("getAttributeSpellingListIndex()", - SemanticToSyntacticMap, OS); - OS << " }\n"; + 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"; + } } - writeAttrAccessorDefinition(R, OS); + if (Header) + writeAttrAccessorDefinition(R, OS); for (auto const &ai : Args) { - ai->writeAccessors(OS); + 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); + static_cast<const EnumArgument *>(ai.get())->writeConversion(OS, + Header); else if (ai->isVariadicEnumArg()) - static_cast<const VariadicEnumArgument *>(ai.get()) - ->writeConversion(OS); + static_cast<const VariadicEnumArgument *>(ai.get())->writeConversion( + OS, Header); } - OS << R.getValueAsString("AdditionalMembers"); - OS << "\n\n"; + if (Header) { + OS << R.getValueAsString("AdditionalMembers"); + OS << "\n\n"; - OS << " static bool classof(const Attr *A) { return A->getKind() == " - << "attr::" << R.getName() << "; }\n"; + OS << " static bool classof(const Attr *A) { return A->getKind() == " + << "attr::" << R.getName() << "; }\n"; - OS << "};\n\n"; + OS << "};\n\n"; + } else { + 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"; + OS << " return A;\n}\n\n"; + + writePrettyPrintFunction(R, Args, OS); + writeGetSpellingFunction(R, OS); + } } +} +// Emits the class definitions for attributes. +void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Attribute classes' definitions", OS); + + OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n"; + OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n\n"; + + emitAttributes(Records, OS, true); OS << "#endif // LLVM_CLANG_ATTR_CLASSES_INC\n"; } @@ -2496,38 +2619,9 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { void clang::EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Attribute classes' member function definitions", OS); - std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); - - for (auto *Attr : Attrs) { - Record &R = *Attr; - - if (!R.getValueAsBit("ASTNode")) - continue; - - std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args"); - std::vector<std::unique_ptr<Argument>> Args; - for (const auto *Arg : ArgRecords) - Args.emplace_back(createArgument(*Arg, R.getName())); - - for (auto const &ai : Args) - ai->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"; - OS << " return A;\n}\n\n"; + emitAttributes(Records, OS, false); - writePrettyPrintFunction(R, Args, OS); - writeGetSpellingFunction(R, OS); - } + std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); // Instead of relying on virtual dispatch we just create a huge dispatch // switch. This is both smaller and faster than virtual functions. @@ -2825,6 +2919,7 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { if (R.isSubClassOf(InhClass)) OS << " bool isInherited = Record.readInt();\n"; OS << " bool isImplicit = Record.readInt();\n"; + OS << " bool isPackExpansion = Record.readInt();\n"; ArgRecords = R.getValueAsListOfDefs("Args"); Args.clear(); for (const auto *Arg : ArgRecords) { @@ -2840,6 +2935,7 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { if (R.isSubClassOf(InhClass)) OS << " cast<InheritableAttr>(New)->setInherited(isInherited);\n"; OS << " New->setImplicit(isImplicit);\n"; + OS << " New->setPackExpansion(isPackExpansion);\n"; OS << " break;\n"; OS << " }\n"; } @@ -2866,6 +2962,7 @@ void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) { 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"; for (const auto *Arg : Args) createArgument(*Arg, R.getName())->writePCHWrite(OS); @@ -2965,7 +3062,7 @@ static void GenerateHasAttrSpellingStringSwitch( // 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: + // document, which can be found at: // https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations int Version = 1; @@ -3257,7 +3354,7 @@ void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS) { 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"; @@ -3287,18 +3384,12 @@ static void emitArgInfo(const Record &R, raw_ostream &OS) { // 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 << ArgCount << ", " << (HasVariadic ? 15 : OptCount); -} - -static void GenerateDefaultAppertainsTo(raw_ostream &OS) { - OS << "static bool defaultAppertainsTo(Sema &, const ParsedAttr &,"; - OS << "const Decl *) {\n"; - OS << " return true;\n"; - OS << "}\n\n"; + OS << " NumArgs = " << ArgCount << ";\n"; + OS << " OptArgs = " << (HasVariadic ? 15 : OptCount) << ";\n"; } static std::string GetDiagnosticSpelling(const Record &R) { - std::string Ret = R.getValueAsString("DiagSpelling"); + std::string Ret = std::string(R.getValueAsString("DiagSpelling")); if (!Ret.empty()) return Ret; @@ -3334,7 +3425,7 @@ static std::string CalculateDiagnostic(const Record &S) { SmallVector<StringRef, 2> Frags; llvm::SplitString(V, Frags, ","); for (auto Str : Frags) { - DiagList.push_back(Str.trim()); + DiagList.push_back(std::string(Str.trim())); } } } @@ -3365,7 +3456,7 @@ static std::string CalculateDiagnostic(const Record &S) { } static std::string GetSubjectWithSuffix(const Record *R) { - const std::string &B = R->getName(); + const std::string &B = std::string(R->getName()); if (B == "DeclBase") return "Decl"; return B + "Decl"; @@ -3375,16 +3466,14 @@ static std::string functionNameForCustomAppertainsTo(const Record &Subject) { return "is" + Subject.getName().str(); } -static std::string GenerateCustomAppertainsTo(const Record &Subject, - raw_ostream &OS) { +static void GenerateCustomAppertainsTo(const Record &Subject, raw_ostream &OS) { std::string FnName = functionNameForCustomAppertainsTo(Subject); - // If this code has already been generated, simply return the previous - // instance of it. + // 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 *I; + return; // This only works with non-root Decls. Record *Base = Subject.getValueAsDef(BaseFieldName); @@ -3393,7 +3482,7 @@ static std::string GenerateCustomAppertainsTo(const Record &Subject, if (Base->isSubClassOf("SubsetSubject")) { PrintFatalError(Subject.getLoc(), "SubsetSubjects within SubsetSubjects is not supported"); - return ""; + return; } OS << "static bool " << FnName << "(const Decl *D) {\n"; @@ -3405,14 +3494,13 @@ static std::string GenerateCustomAppertainsTo(const Record &Subject, OS << "}\n\n"; CustomSubjectSet.insert(FnName); - return FnName; } -static std::string GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { +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 "defaultAppertainsTo"; + return; const Record *SubjectObj = Attr.getValueAsDef("Subjects"); std::vector<Record*> Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); @@ -3420,52 +3508,46 @@ static std::string GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { // If the list of subjects is empty, it is assumed that the attribute // appertains to everything. if (Subjects.empty()) - return "defaultAppertainsTo"; + return; bool Warn = SubjectObj->getValueAsDef("Diag")->getValueAsBit("Warn"); // Otherwise, generate an appertainsTo check specific to this attribute which - // checks all of the given subjects against the Decl passed in. Return the - // name of that check to the caller. + // checks all of the given subjects against the Decl passed in. // // If D is null, that means the attribute was not applied to a declaration // at all (for instance because it was applied to a type), or that the caller // has determined that the check should fail (perhaps prior to the creation // of the declaration). - std::string FnName = "check" + Attr.getName().str() + "AppertainsTo"; - std::stringstream SS; - SS << "static bool " << FnName << "(Sema &S, const ParsedAttr &Attr, "; - SS << "const Decl *D) {\n"; - SS << " if (!D || ("; + OS << "bool diagAppertainsToDecl(Sema &S, "; + OS << "const ParsedAttr &Attr, const Decl *D) const override {\n"; + OS << " if ("; for (auto I = Subjects.begin(), E = Subjects.end(); I != E; ++I) { - // If the subject has custom code associated with it, generate a 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 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")) { - SS << "!" << GenerateCustomAppertainsTo(**I, OS) << "(D)"; + OS << "!" << functionNameForCustomAppertainsTo(**I) << "(D)"; } else { - SS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)"; + OS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)"; } if (I + 1 != E) - SS << " && "; + OS << " && "; } - SS << ")) {\n"; - SS << " S.Diag(Attr.getLoc(), diag::"; - SS << (Warn ? "warn_attribute_wrong_decl_type_str" : + OS << ") {\n"; + OS << " S.Diag(Attr.getLoc(), diag::"; + OS << (Warn ? "warn_attribute_wrong_decl_type_str" : "err_attribute_wrong_decl_type_str"); - SS << ")\n"; - SS << " << Attr << "; - SS << CalculateDiagnostic(*SubjectObj) << ";\n"; - SS << " return false;\n"; - SS << " }\n"; - SS << " return true;\n"; - SS << "}\n\n"; - - OS << SS.str(); - return FnName; + OS << ")\n"; + OS << " << Attr << "; + OS << CalculateDiagnostic(*SubjectObj) << ";\n"; + OS << " return false;\n"; + OS << " }\n"; + OS << " return true;\n"; + OS << "}\n\n"; } static void @@ -3504,37 +3586,16 @@ emitAttributeMatchRules(PragmaClangAttributeSupport &PragmaAttributeSupport, OS << "}\n\n"; } -static void GenerateDefaultLangOptRequirements(raw_ostream &OS) { - OS << "static bool defaultDiagnoseLangOpts(Sema &, "; - OS << "const ParsedAttr &) {\n"; - OS << " return true;\n"; - OS << "}\n\n"; -} - -static std::string GenerateLangOptRequirements(const Record &R, - raw_ostream &OS) { +static void GenerateLangOptRequirements(const Record &R, + raw_ostream &OS) { // If the attribute has an empty or unset list of language requirements, - // return the default handler. + // use the default handler. std::vector<Record *> LangOpts = R.getValueAsListOfDefs("LangOpts"); if (LangOpts.empty()) - return "defaultDiagnoseLangOpts"; - - // Generate a unique function name for the diagnostic test. The list of - // options should usually be short (one or two options), and the - // uniqueness isn't strictly necessary (it is just for codegen efficiency). - std::string FnName = "check"; - for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) - FnName += (*I)->getValueAsString("Name"); - FnName += "LangOpts"; - - // If this code has already been generated, simply return the previous - // instance of it. - static std::set<std::string> CustomLangOptsSet; - auto I = CustomLangOptsSet.find(FnName); - if (I != CustomLangOptsSet.end()) - return *I; - - OS << "static bool " << FnName << "(Sema &S, const ParsedAttr &Attr) {\n"; + return; + + OS << "bool diagLangOpts(Sema &S, const ParsedAttr &Attr) "; + OS << "const override {\n"; OS << " auto &LangOpts = S.LangOpts;\n"; OS << " if (" << GenerateTestExpression(LangOpts) << ")\n"; OS << " return true;\n\n"; @@ -3542,24 +3603,15 @@ static std::string GenerateLangOptRequirements(const Record &R, OS << "<< Attr;\n"; OS << " return false;\n"; OS << "}\n\n"; - - CustomLangOptsSet.insert(FnName); - return FnName; -} - -static void GenerateDefaultTargetRequirements(raw_ostream &OS) { - OS << "static bool defaultTargetRequirements(const TargetInfo &) {\n"; - OS << " return true;\n"; - OS << "}\n\n"; } -static std::string GenerateTargetRequirements(const Record &Attr, - const ParsedAttrMap &Dupes, - raw_ostream &OS) { - // If the attribute is not a target specific attribute, return the default +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 "defaultTargetRequirements"; + return; // Get the list of architectures to be tested for. const Record *R = Attr.getValueAsDef("Target"); @@ -3587,55 +3639,51 @@ static std::string GenerateTargetRequirements(const Record &Attr, std::string Test; bool UsesT = GenerateTargetSpecificAttrChecks(R, Arches, Test, &FnName); - // If this code has already been generated, simply return the previous - // instance of it. - static std::set<std::string> CustomTargetSet; - auto I = CustomTargetSet.find(FnName); - if (I != CustomTargetSet.end()) - return *I; - - OS << "static bool " << FnName << "(const TargetInfo &Target) {\n"; + 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"; - - CustomTargetSet.insert(FnName); - return FnName; -} - -static void GenerateDefaultSpellingIndexToSemanticSpelling(raw_ostream &OS) { - OS << "static unsigned defaultSpellingIndexToSemanticSpelling(" - << "const ParsedAttr &Attr) {\n"; - OS << " return UINT_MAX;\n"; - OS << "}\n\n"; } -static std::string GenerateSpellingIndexToSemanticSpelling(const Record &Attr, - raw_ostream &OS) { +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 "defaultSpellingIndexToSemanticSpelling"; + 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 "defaultSpellingIndexToSemanticSpelling"; + return; // Generate the enumeration we will use for the mapping. SemanticSpellingMap SemanticToSyntacticMap; std::string Enum = CreateSemanticSpellings(Spellings, SemanticToSyntacticMap); std::string Name = Attr.getName().str() + "AttrSpellingMap"; - OS << "static unsigned " << Name << "(const ParsedAttr &Attr) {\n"; + 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; - return Name; + // 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 IsKnownToGCC(const Record &Attr) { @@ -3658,19 +3706,19 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { ParsedAttrMap Dupes; ParsedAttrMap Attrs = getParsedAttrList(Records, &Dupes); - // Generate the default appertainsTo, target and language option diagnostic, - // and spelling list index mapping methods. - GenerateDefaultAppertainsTo(OS); - GenerateDefaultLangOptRequirements(OS); - GenerateDefaultTargetRequirements(OS); - GenerateDefaultSpellingIndexToSemanticSpelling(OS); - - // Generate the appertainsTo diagnostic methods and write their names into - // another mapping. At the same time, generate the AttrInfoMap object - // contents. Due to the reliance on generated code, use separate streams so - // that code will not be interleaved. - std::string Buffer; - raw_string_ostream SS {Buffer}; + // Generate all of the custom appertainsTo functions that the attributes + // will be using. + for (auto I : Attrs) { + const Record &Attr = *I.second; + if (Attr.isValueUnset("Subjects")) + continue; + const Record *SubjectObj = Attr.getValueAsDef("Subjects"); + for (auto Subject : SubjectObj->getValueAsListOfDefs("Subjects")) + if (Subject->isSubClassOf("SubsetSubject")) + GenerateCustomAppertainsTo(*Subject, OS); + } + + // 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. @@ -3680,33 +3728,63 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { // We need to generate struct instances based off ParsedAttrInfo from // ParsedAttr.cpp. - SS << " { "; - emitArgInfo(*I->second, SS); - SS << ", " << I->second->getValueAsBit("HasCustomParsing"); - SS << ", " << I->second->isSubClassOf("TargetSpecificAttr"); - SS << ", " - << (I->second->isSubClassOf("TypeAttr") || - I->second->isSubClassOf("DeclOrTypeAttr")); - SS << ", " << I->second->isSubClassOf("StmtAttr"); - SS << ", " << IsKnownToGCC(*I->second); - SS << ", " << PragmaAttributeSupport.isAttributedSupported(*I->second); - SS << ", " << GenerateAppertainsTo(*I->second, OS); - SS << ", " << GenerateLangOptRequirements(*I->second, OS); - SS << ", " << GenerateTargetRequirements(*I->second, Dupes, OS); - SS << ", " << GenerateSpellingIndexToSemanticSpelling(*I->second, OS); - SS << ", " - << PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS); - SS << " }"; - - if (I + 1 != E) - SS << ","; - - SS << " // AT_" << I->first << "\n"; + 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) { + const std::string &RawSpelling = S.name(); + std::string Spelling; + if (!S.nameSpace().empty()) + Spelling += S.nameSpace() + "::"; + if (S.variety() == "GNU") + Spelling += NormalizeGNUAttrSpelling(RawSpelling); + else + Spelling += RawSpelling; + OS << " {AttributeCommonInfo::AS_" << S.variety(); + OS << ", \"" << Spelling << "\"},\n"; + } + OS << "};\n"; + } + OS << "struct ParsedAttrInfo" << I->first + << " final : public ParsedAttrInfo {\n"; + OS << " ParsedAttrInfo" << I->first << "() {\n"; + OS << " AttrKind = ParsedAttr::AT_" << AttrName << ";\n"; + emitArgInfo(Attr, OS); + OS << " HasCustomParsing = "; + OS << Attr.getValueAsBit("HasCustomParsing") << ";\n"; + OS << " IsTargetSpecific = "; + OS << Attr.isSubClassOf("TargetSpecificAttr") << ";\n"; + OS << " IsType = "; + OS << (Attr.isSubClassOf("TypeAttr") || + Attr.isSubClassOf("DeclOrTypeAttr")) << ";\n"; + OS << " IsStmt = "; + OS << Attr.isSubClassOf("StmtAttr") << ";\n"; + OS << " IsKnownToGCC = "; + OS << IsKnownToGCC(Attr) << ";\n"; + OS << " IsSupportedByPragmaAttribute = "; + OS << PragmaAttributeSupport.isAttributedSupported(*I->second) << ";\n"; + if (!Spellings.empty()) + OS << " Spellings = " << I->first << "Spellings;\n"; + OS << " }\n"; + GenerateAppertainsTo(Attr, OS); + GenerateLangOptRequirements(Attr, OS); + GenerateTargetRequirements(Attr, Dupes, OS); + GenerateSpellingIndexToSemanticSpelling(Attr, OS); + PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS); + GenerateHandleDeclAttribute(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[ParsedAttr::UnknownAttribute " - "+ 1] = {\n"; - OS << SS.str(); + OS << "static const ParsedAttrInfo *AttrInfoMap[] = {\n"; + for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) { + OS << "&ParsedAttrInfo" << I->first << "::Instance,\n"; + } OS << "};\n\n"; // Generate the attribute match rules. @@ -3740,7 +3818,7 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { std::string AttrName; if (Attr.isSubClassOf("TargetSpecificAttr") && !Attr.isValueUnset("ParseKind")) { - AttrName = Attr.getValueAsString("ParseKind"); + AttrName = std::string(Attr.getValueAsString("ParseKind")); if (Seen.find(AttrName) != Seen.end()) continue; Seen.insert(AttrName); @@ -3755,12 +3833,12 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { const std::string &Variety = S.variety(); if (Variety == "CXX11") { Matches = &CXX11; - Spelling += S.nameSpace(); - Spelling += "::"; + if (!S.nameSpace().empty()) + Spelling += S.nameSpace() + "::"; } else if (Variety == "C2x") { Matches = &C2x; - Spelling += S.nameSpace(); - Spelling += "::"; + if (!S.nameSpace().empty()) + Spelling += S.nameSpace() + "::"; } else if (Variety == "GNU") Matches = &GNU; else if (Variety == "Declspec") @@ -3980,7 +4058,7 @@ GetAttributeHeadingAndSpellings(const Record &Documentation, "documented"); // Determine the heading to be used for this attribute. - std::string Heading = Documentation.getValueAsString("Heading"); + std::string Heading = std::string(Documentation.getValueAsString("Heading")); if (Heading.empty()) { // If there's only one spelling, we can simply use that. if (Spellings.size() == 1) @@ -3989,7 +4067,8 @@ GetAttributeHeadingAndSpellings(const Record &Documentation, std::set<std::string> Uniques; for (auto I = Spellings.begin(), E = Spellings.end(); I != E && Uniques.size() <= 1; ++I) { - std::string Spelling = NormalizeNameForSpellingComparison(I->name()); + std::string Spelling = + std::string(NormalizeNameForSpellingComparison(I->name())); Uniques.insert(Spelling); } // If the semantic map has only one spelling, that is sufficient for our |