Skip to content
Permalink
main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
<
//===- ClangAttrEmitter.cpp - Generate Clang attribute handling =-*- C++ -*--=//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// 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/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/iterator_range.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 <algorithm>
#include <cassert>
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
using namespace llvm;
namespace {
class FlattenedSpelling {
std::string V, N, NS;
bool K = false;
const Record &OriginalSpelling;
public:
FlattenedSpelling(const std::string &Variety, const std::string &Name,
const std::string &Namespace, bool KnownToGCC,
const Record &OriginalSpelling)
: V(Variety), N(Name), NS(Namespace), K(KnownToGCC),
OriginalSpelling(OriginalSpelling) {}
explicit FlattenedSpelling(const Record &Spelling)
: V(std::string(Spelling.getValueAsString("Variety"))),
N(std::string(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 == "C2x" || V == "Pragma")
NS = std::string(Spelling.getValueAsString("Namespace"));
}
const std::string &variety() const { return V; }
const std::string &name() const { return N; }
const std::string &nameSpace() const { return NS; }
bool knownToGCC() const { return K; }
const Record &getSpellingRecord() const { return OriginalSpelling; }
};
} // end anonymous namespace
static std::vector<FlattenedSpelling>
GetFlattenedSpellings(const Record &Attr) {
std::vector<Record *> Spellings = Attr.getValueAsListOfDefs("Spellings");
std::vector<FlattenedSpelling> Ret;
for (const auto &Spelling : Spellings) {
StringRef Variety = Spelling->getValueAsString("Variety");
StringRef Name = Spelling->getValueAsString("Name");
if (Variety == "GCC") {
Ret.emplace_back("GNU", std::string(Name), "", true, *Spelling);
Ret.emplace_back("CXX11", std::string(Name), "gnu", true, *Spelling);
if (Spelling->getValueAsBit("AllowInC"))
Ret.emplace_back("C2x", std::string(Name), "gnu", true, *Spelling);
} else if (Variety == "Clang") {
Ret.emplace_back("GNU", std::string(Name), "", false, *Spelling);
Ret.emplace_back("CXX11", std::string(Name), "clang", false, *Spelling);
if (Spelling->getValueAsBit("AllowInC"))
Ret.emplace_back("C2x", std::string(Name), "clang", false, *Spelling);
} else
Ret.push_back(FlattenedSpelling(*Spelling));
}
return Ret;
}
static std::string ReadPCHRecord(StringRef type) {
return StringSwitch<std::string>(type)
.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.
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(" + 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
// 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.startswith("__") && AttrSpelling.endswith("__")) {
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) {
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
std::set<std::string> Seen;
ParsedAttrMap R;
for (const auto *Attr : Attrs) {
if (Attr->getValueAsBit("SemaHandler")) {
std::string AN;
if (Attr->isSubClassOf("TargetSpecificAttr") &&
!Attr->isValueUnset("ParseKind")) {
AN = std::string(Attr->getValueAsString("ParseKind"));
// If this attribute has already been handled, it does not need to be
// handled again.
if (Seen.find(AN) != Seen.end()) {
if (Dupes)
Dupes->push_back(std::make_pair(AN, Attr));
continue;
}
Seen.insert(AN);
} 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(std::string(Arg)), 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();
}
};
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" + std::string(getUpperName()) + "()");
}
std::string getIsOmitted() const override {
if (type == "IdentifierInfo *")
return "!get" + getUpperName().str() + "()";
if (type == "TypeSourceInfo *")
return "!get" + getUpperName().str() + "Loc()";
if (type == "ParamIdx")
return "!get" + getUpperName().str() + "().isValid()";
return "false";
}
void writeValue(raw_ostream &OS) const override {
if (type == "FunctionDecl *")
OS << "\" << get" << getUpperName()
<< "()->getNameInfo().getAsString() << \"";
else if (type == "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).endswith("Decl *")) {
OS << " OS << \" \";\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.
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, 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()";
}
};
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(std::string(getLowerName())) {}
VariadicArgument(StringRef Arg, StringRef Attr, std::string T)
: Argument(Arg, Attr), Type(std::move(T)),
ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"),
RangeName(std::string(getLowerName())) {}
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 = std::string(getStorageType(getType()));
std::string StorageName = std::string(getLowerName());
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);
}
};
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 << " " << getLowerName()
<< ".emplace_back(IsTarget, IsTargetSync);\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 << " }\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 type;
std::vector<StringRef> values, enums, uniques;
public:
EnumArgument(const Record &Arg, StringRef Attr)
: 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());
}
bool isEnumArg() const override { return true; }
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() << "(" << type << "(0))";
}
void writeCtorParameters(raw_ostream &OS) const override {
OS << type << " " << getUpperName();
}
void writeDeclarations(raw_ostream &OS) const override {
auto i = uniques.cbegin(), e = uniques.cend();
// The last one needs to not have a comma.
--e;
OS << "public:\n";
OS << " enum " << type << " {\n";
for (; i != e; ++i)
OS << " " << *i << ",\n";
OS << " " << *e << "\n";
OS << " };\n";
OS << "private:\n";
OS << " " << type << " " << getLowerName() << ";";
}
void writePCHReadDecls(raw_ostream &OS) const override {
OS << " " << getAttrName() << "Attr::" << type << " " << getLowerName()
<< "(static_cast<" << getAttrName() << "Attr::" << type
<< ">(Record.readInt()));\n";
}
void writePCHReadArgs(raw_ostream &OS) const override {
OS << getLowerName();
}
void writePCHWrite(raw_ostream &OS) const override {
OS << "Record.push_back(SA->get" << getUpperName() << "());\n";
}
void writeValue(raw_ostream &OS) const override {
// FIXME: this isn't 100% correct -- some enum arguments require printing
// as a string literal, while others require printing as an identifier.
// Tablegen currently does not distinguish between the two forms.
OS << "\\\"\" << " << getAttrName() << "Attr::Convert" << type << "ToStr(get"
<< getUpperName() << "()) << \"\\\"";
}
void writeDump(raw_ostream &OS) const override {
OS << " switch(SA->get" << getUpperName() << "()) {\n";
for (const auto &I : uniques) {
OS << " case " << getAttrName() << "Attr::" << I << ":\n";
OS << " OS << \" " << I << "\";\n";
OS << " break;\n";
}
OS << " }\n";
}
void writeConversion(raw_ostream &OS, bool Header) const {
if (Header) {
OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type
<< " &Out);\n";
OS << " static const char *Convert" << type << "ToStr(" << type
<< " Val);\n";
return;
}
OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type
<< "(StringRef Val, " << type << " &Out) {\n";
OS << " std::optional<" << type
<< "> R = llvm::StringSwitch<std::optional<";
OS << type << ">>(Val)\n";
for (size_t I = 0; I < enums.size(); ++I) {
OS << " .Case(\"" << values[I] << "\", ";
OS << getAttrName() << "Attr::" << enums[I] << ")\n";
}
OS << " .Default(std::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 << "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 << " }\n"
<< " llvm_unreachable(\"No enumerator with that value\");\n"
<< "}\n";
}
};
class VariadicEnumArgument: public VariadicArgument {
std::string type, QualifiedTypeName;
std::vector<StringRef> values, enums, uniques;
protected:
void writeValueImpl(raw_ostream &OS) const override {
// FIXME: this isn't 100% correct -- some enum arguments require printing
// as a string literal, while others require printing as an identifier.
// Tablegen currently does not distinguish between the two forms.
OS << " OS << \"\\\"\" << " << getAttrName() << "Attr::Convert" << type
<< "ToStr(Val)" << "<< \"\\\"\";\n";
}
public:
VariadicEnumArgument(const Record &Arg, StringRef Attr)
: VariadicArgument(Arg, Attr,
std::string(Arg.getValueAsString("Type"))),
type(std::string(Arg.getValueAsString("Type"))),
values(Arg.getValueAsListOfStrings("Values")),
enums(Arg.getValueAsListOfStrings("Enums")),
uniques(uniqueEnumsInOrder(enums)) {
QualifiedTypeName = getAttrName().str() + "Attr::" + type;
// 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.
--e;
OS << "public:\n";
OS << " enum " << type << " {\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 " << getAttrName() << "Attr::" << UI << ":\n";
OS << " OS << \" " << UI << "\";\n";
OS << " break;\n";
}
OS << " }\n";
OS << " }\n";
}
void writePCHReadDecls(raw_ostream &OS) const override {
OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n";
OS << " SmallVector<" << QualifiedTypeName << ", 4> " << getLowerName()
<< ";\n";
OS << " " << getLowerName() << ".reserve(" << getLowerName()
<< "Size);\n";
OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n";
OS << " " << getLowerName() << ".push_back(" << "static_cast<"
<< QualifiedTypeName << ">(Record.readInt()));\n";
}
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(QualifiedTypeName, "(*i)");
}
void writeConversion(raw_ostream &OS, bool Header) const {
if (Header) {
OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type
<< " &Out);\n";
OS << " static const char *Convert" << type << "ToStr(" << type
<< " Val);\n";
return;
}
OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type
<< "(StringRef Val, ";
OS << type << " &Out) {\n";
OS << " std::optional<" << type
<< "> R = llvm::StringSwitch<std::optional<";
OS << type << ">>(Val)\n";
for (size_t I = 0; I < enums.size(); ++I) {
OS << " .Case(\"" << values[I] << "\", ";
OS << getAttrName() << "Attr::" << enums[I] << ")\n";
}
OS << " .Default(std::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 << " }\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() << ", "
<< "A->" << getLowerName() << "_size()";
}
void writeTemplateInstantiation(raw_ostream &OS) const override {
OS << " auto *tempInst" << getUpperName()
<< " = new (C, 16) " << getType()
<< "[A->" << getLowerName() << "_size()];\n";
OS << " {\n";
OS << " EnterExpressionEvaluationContext "
<< "Unevaluated(S, Sema::ExpressionEvaluationContext::Unevaluated);\n";
OS << " " << getType() << " *TI = tempInst" << getUpperName()
<< ";\n";
OS << " " << getType() << " *I = A->" << getLowerName()
<< "_begin();\n";
OS << " " << getType() << " *E = A->" << getLowerName()
<< "_end();\n";
OS << " for (; I != E; ++I, ++TI) {\n";
OS << " ExprResult Result = S.SubstExpr(*I, TemplateArgs);\n";
OS << " if (Result.isInvalid())\n";
OS << " return nullptr;\n";
OS << " *TI = Result.get();\n";
OS << " }\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, "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" + std::string(getUpperName()) + "Loc()");
}
};
} // 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;
llvm::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, "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 == "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.
ArrayRef<std::pair<Record*, SMRange>> Bases = Search->getSuperClasses();
for (const auto &Base : llvm::reverse(Bases)) {
if ((Ptr = createArgument(Arg, Attr, Base.first)))
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, std::string &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 (unsigned I = 0; I < Spellings.size(); ++I)
OS << " case " << I << ":\n"
" return \"" << Spellings[I].name() << "\";\n";
// 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 (unsigned I = 0; I < Spellings.size(); ++ I) {
llvm::SmallString<16> Prefix;
llvm::SmallString<8> Suffix;
// The actual spelling of the name and namespace (if applicable)
// of an attribute without considering prefix and suffix.
llvm::SmallString<64> Spelling;
std::string Name = Spellings[I].name();
std::string Variety = Spellings[I].variety();
if (Variety == "GNU") {
Prefix = " __attribute__((";
Suffix = "))";
} else if (Variety == "CXX11" || Variety == "C2x") {
Prefix = " [[";
Suffix = "]]";
std::string Namespace = Spellings[I].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";
std::string Namespace = Spellings[I].nameSpace();
if (!Namespace.empty()) {
Spelling += Namespace;
Spelling += " ";
}
} else if (Variety == "HLSLSemantic") {
Prefix = ":";
Suffix = "";
} else {
llvm_unreachable("Unknown attribute syntax variety!");
}
Spelling += Name;
OS << " case " << I << " : {\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 : llvm::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(const std::vector<FlattenedSpelling> &SpellingList,
const FlattenedSpelling &Spelling) {
assert(!SpellingList.empty() && "Spelling list is empty!");
for (unsigned Index = 0; Index < SpellingList.size(); ++Index) {
const FlattenedSpelling &S = SpellingList[Index];
if (S.variety() != Spelling.variety())
continue;
if (S.nameSpace() != Spelling.nameSpace())
continue;
if (S.name() != Spelling.name())
continue;
return Index;
}
llvm_unreachable("Unknown spelling!");
}
static void writeAttrAccessorDefinition(const Record &R, raw_ostream &OS) {
std::vector<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");
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 =
std::string(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;
const std::string &Variety = S.variety();
const std::string &Spelling = S.name();
const std::string &Namespace = S.nameSpace();
std::string EnumName;
EnumName += (Variety + "_");
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.find(EnumName) != Uniques.end())
continue;
Uniques.insert(EnumName);
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 + " = " + llvm::utostr(Idx);
}
Ret += ",\n SpellingNotCalculated = 15\n";
Ret += "\n };\n\n";
return Ret;
}
void WriteSemanticSpellingSwitch(const std::string &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";
}
// Emits the LateParsed property for attributes.
static void emitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS) {
OS << "#if defined(CLANG_ATTR_LATE_PARSED_LIST)\n";
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
for (const auto *Attr : Attrs) {
bool LateParsed = Attr->getValueAsBit("LateParsed");
if (LateParsed) {
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() << "\", " << LateParsed << ")\n";
}
}
}
OS << "#endif // CLANG_ATTR_LATE_PARSED_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<Record *> getSubjects() const {
return (Constraint ? Constraint : MetaSubject)
->getValueAsListOfDefs("Subjects");
}
std::vector<Record *> getLangOpts() const {
if (Constraint) {
// Lookup the options in the sub-rule first, in case the sub-rule
// overrides the rules options.
std::vector<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 = std::string(MetaSubject->getValueAsString("Name"));
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.str());
}
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);
}
};
llvm::DenseMap<const Record *, RuleOrAggregateRuleSet> SubjectsToRules;
PragmaClangAttributeSupport(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(
RecordKeeper &Records) {
std::vector<Record *> MetaSubjects =
Records.getAllDerivedDefinitions("AttrSubjectMatcherRule");
auto MapFromSubjectsToRules = [this](const Record *SubjectContainer,
const Record *MetaSubject,
const Record *Constraint) {
Rules.emplace_back(MetaSubject, Constraint);
std::vector<Record *> ApplicableSubjects =
SubjectContainer->getValueAsListOfDefs("Subjects");
for (const auto *Subject : ApplicableSubjects) {
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 : MetaSubjects) {
MapFromSubjectsToRules(MetaSubject, MetaSubject, /*Constraints=*/nullptr);
std::vector<Record *> Constraints =
MetaSubject->getValueAsListOfDefs("Constraints");
for (const auto *Constraint : Constraints)
MapFromSubjectsToRules(Constraint, MetaSubject, Constraint);
}
std::vector<Record *> Aggregates =
Records.getAllDerivedDefinitions("AttrSubjectMatcherAggregateRule");
std::vector<Record *> DeclNodes =
Records.getAllDerivedDefinitions(DeclNodeClassName);
for (const auto *Aggregate : Aggregates) {
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(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)
if (Attribute.getValueAsBit("LateParsed"))
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");
std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
bool HasAtLeastOneValidSubject = false;
for (const auto *Subject : Subjects) {
if (!isSupportedPragmaClangAttributeSubject(*Subject))
continue;
if (!SubjectsToRules.contains(Subject))
return false;
HasAtLeastOneValidSubject = true;
}
return HasAtLeastOneValidSubject;
}
static std::string GenerateTestExpression(ArrayRef<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");
std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
for (const auto *Subject : 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<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";
llvm::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 forEachUniqueSpelling(const Record &Attr, Fn &&F) {
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr);
SmallDenseSet<StringRef, 8> Seen;
for (const FlattenedSpelling &S : Spellings) {
if (Seen.insert(S.name()).second)
F(S);
}
}
static bool isTypeArgument(const Record *Arg) {
return !Arg->getSuperClasses().empty() &&
Arg->getSuperClasses().back().first->getName() == "TypeArgument";
}
/// Emits the first-argument-is-type property for attributes.
static void emitClangAttrTypeArgList(RecordKeeper &Records, raw_ostream &OS) {
OS << "#if defined(CLANG_ATTR_TYPE_ARG_LIST)\n";
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
for (const auto *Attr : Attrs) {
// Determine whether the first argument is a type.
std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args");
if (Args.empty())
continue;
if (!isTypeArgument(Args[0]))
continue;
// All these spellings take a single type argument.
forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) {
OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n";
});
}
OS << "#endif // CLANG_ATTR_TYPE_ARG_LIST\n\n";
}
/// Emits the parse-arguments-in-unevaluated-context property for
/// attributes.
static void emitClangAttrArgContextList(RecordKeeper &Records, raw_ostream &OS) {
OS << "#if defined(CLANG_ATTR_ARG_CONTEXT_LIST)\n";
ParsedAttrMap Attrs = getParsedAttrList(Records);
for (const auto &I : Attrs) {
const Record &Attr = *I.second;
if (!Attr.getValueAsBit("ParseArgumentsAsUnevaluated"))
continue;
// All these spellings take are parsed unevaluated.
forEachUniqueSpelling(Attr, [&](const FlattenedSpelling &S) {
OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n";
});
}
OS << "#endif // CLANG_ATTR_ARG_CONTEXT_LIST\n\n";
}
static bool isIdentifierArgument(const Record *Arg) {
return !Arg->getSuperClasses().empty() &&
llvm::StringSwitch<bool>(Arg->getSuperClasses().back().first->getName())
.Case("IdentifierArgument", true)
.Case("EnumArgument", true)
.Case("VariadicEnumArgument", true)
.Default(false);
}
static bool isVariadicIdentifierArgument(const Record *Arg) {
return !Arg->getSuperClasses().empty() &&
llvm::StringSwitch<bool>(
Arg->getSuperClasses().back().first->getName())
.Case("VariadicIdentifierArgument", true)
.Case("VariadicParamOrParamIdxArgument", true)
.Default(false);
}
static bool isVariadicExprArgument(const Record *Arg) {
return !Arg->getSuperClasses().empty() &&
llvm::StringSwitch<bool>(
Arg->getSuperClasses().back().first->getName())
.Case("VariadicExprArgument", true)
.Default(false);
}
static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records,
raw_ostream &OS) {
OS << "#if defined(CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST)\n";
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
for (const auto *A : Attrs) {
// Determine whether the first argument is a variadic identifier.
std::vector<Record *> Args = A->getValueAsListOfDefs("Args");
if (Args.empty() || !isVariadicIdentifierArgument(Args[0]))
continue;
// All these spellings take an identifier argument.
forEachUniqueSpelling(*A, [&](const FlattenedSpelling &S) {
OS << ".Case(\"" << S.name() << "\", "
<< "true"
<< ")\n";
});
}
OS << "#endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST\n\n";
}
// Emits the first-argument-is-identifier property for attributes.
static void emitClangAttrIdentifierArgList(RecordKeeper &Records, raw_ostream &OS) {
OS << "#if defined(CLANG_ATTR_IDENTIFIER_ARG_LIST)\n";
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
for (const auto *Attr : Attrs) {
// Determine whether the first argument is an identifier.
std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args");
if (Args.empty() || !isIdentifierArgument(Args[0]))
continue;
// All these spellings take an identifier argument.
forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) {
OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n";
});
}
OS << "#endif // CLANG_ATTR_IDENTIFIER_ARG_LIST\n\n";
}
static bool keywordThisIsaIdentifierInArgument(const Record *Arg) {
return !Arg->getSuperClasses().empty() &&
llvm::StringSwitch<bool>(
Arg->getSuperClasses().back().first->getName())
.Case("VariadicParamOrParamIdxArgument", true)
.Default(false);
}
static void emitClangAttrThisIsaIdentifierArgList(RecordKeeper &Records,
raw_ostream &OS) {
OS << "#if defined(CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST)\n";
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
for (const auto *A : Attrs) {
// Determine whether the first argument is a variadic identifier.
std::vector<Record *> Args = A->getValueAsListOfDefs("Args");
if (Args.empty() || !keywordThisIsaIdentifierInArgument(Args[0]))
continue;
// All these spellings take an identifier argument.
forEachUniqueSpelling(*A, [&](const FlattenedSpelling &S) {
OS << ".Case(\"" << S.name() << "\", "
<< "true"
<< ")\n";
});
}
OS << "#endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST\n\n";
}
static void emitClangAttrAcceptsExprPack(RecordKeeper &Records,
raw_ostream &OS) {
OS << "#if defined(CLANG_ATTR_ACCEPTS_EXPR_PACK)\n";
ParsedAttrMap Attrs = getParsedAttrList(Records);
for (const auto &I : Attrs) {
const Record &Attr = *I.second;
if (!Attr.getValueAsBit("AcceptsExprPack"))
continue;
forEachUniqueSpelling(Attr, [&](const FlattenedSpelling &S) {
OS << ".Case(\"" << S.name() << "\", true)\n";
});
}
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(RecordKeeper &Records, raw_ostream &OS,
bool Header) {
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
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 : Attrs) {
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;
ArrayRef<std::pair<Record *, SMRange>> Supers = R.getSuperClasses();
assert(!Supers.empty() && "Forgot to specify a superclass for the attr");
std::string SuperName;
bool Inheritable = false;
for (const auto &Super : llvm::reverse(Supers)) {
const Record *R = Super.first;
if (R->getName() != "TargetSpecificAttr" &&
R->getName() != "DeclOrTypeAttr" && SuperName.empty())
SuperName = std::string(R->getName());
if (R->getName() == "InheritableAttr")
Inheritable = true;
}
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;
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 = llvm::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";
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 << " S == " << Name << " ? AttributeCommonInfo::Form";
emitFormInitializer(OS, S, Name);
OS << " :\n";
}
}
OS << " (llvm_unreachable(\"Unknown attribute spelling!\"), "
<< " AttributeCommonInfo::Form";
emitFormInitializer(OS, Spellings[0], "0");
OS << "))";
}
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() << ", "
<< (R.getValueAsBit("LateParsed") ? "true" : "false");
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 =
llvm::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);
}
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(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";
}
// Emits the class method definitions for attributes.
void clang::EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("Attribute classes' member function definitions", OS);
emitAttributes(Records, OS, false);
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.
auto EmitFunc = [&](const char *Method) {
OS << " switch (getKind()) {\n";
for (const auto *Attr : Attrs) {
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)");
}
static void emitAttrList(raw_ostream &OS, StringRef Class,
const std::vector<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 llvm::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" },
{ "PARAMETER_ABI_ATTR", "ParameterABIAttr" },
{ "HLSL_ANNOTATION_ATTR", "HLSLAnnotationAttr"}
};
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;
Record *TheRecord;
AttrClass *SuperClass = nullptr;
std::vector<AttrClass*> SubClasses;
std::vector<Record*> Attrs;
AttrClass(const AttrClassDescriptor &Descriptor, 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(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(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;
}
Record *getFirstAttr() const {
if (!SubClasses.empty())
return SubClasses.front()->getFirstAttr();
return Attrs.front();
}
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(RecordKeeper &Records) {
// Find records for all the classes.
for (auto &Descriptor : AttrClassDescriptors) {
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(Record *Attr) {
// Add the attribute to the root class.
Classes[0]->classifyAttrOnRoot(Attr);
}
private:
AttrClass *findClassByRecord(Record *R) const {
for (auto &Class : Classes) {
if (Class->TheRecord == R)
return Class.get();
}
return nullptr;
}
AttrClass *findSuperClass(Record *R) const {
// TableGen flattens the superclass list, so we just need to walk it
// in reverse.
auto SuperClasses = R->getSuperClasses();
for (signed i = 0, e = SuperClasses.size(); i != e; ++i) {
auto SuperClass = findClassByRecord(SuperClasses[e - i - 1].first);
if (SuperClass) return SuperClass;
}
return nullptr;
}
};
} // end anonymous namespace
namespace clang {
// Emits the enumeration list for attributes.
void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("List of all attributes that Clang recognizes", OS);
AttrClassHierarchy Hierarchy(Records);
// Add defaulting macro definitions.
Hierarchy.emitDefaultDefines(OS);
emitDefaultDefine(OS, "PRAGMA_SPELLING_ATTR", nullptr);
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
std::vector<Record *> PragmaAttrs;
for (auto *Attr : Attrs) {
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(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader(
"List of all attribute subject matching rules that Clang recognizes", OS);
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(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("Attribute deserialization code", OS);
Record *InhClass = Records.getClass("InheritableAttr");
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"),
ArgRecords;
std::vector<std::unique_ptr<Argument>> Args;
std::unique_ptr<VariadicExprArgument> DelayedArgs;
OS << " switch (Kind) {\n";
for (const auto *Attr : Attrs) {
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";
}
OS << " break;\n";
OS << " }\n";
}
OS << " }\n";
}
// Emits the code to write an attribute to a precompiled header.
void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("Attribute serialization code", OS);
Record *InhClass = Records.getClass("InheritableAttr");
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), Args;
OS << " switch (A->getKind()) {\n";
for (const auto *Attr : Attrs) {
const Record &R = *Attr;
if (!R.getValueAsBit("ASTNode"))
continue;
OS << " case attr::" << R.getName() << ": {\n";
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);
OS << " break;\n";
OS << " }\n";
}
OS << " }\n";
}
// 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 llvm::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(
const std::vector<Record *> &Attrs, raw_ostream &OS,
const std::string &Variety = "", const std::string &Scope = "") {
for (const auto *Attr : 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
//