steffenlarsen updated this revision to Diff 404889.
steffenlarsen added a comment.
Added tests for redeclarations and template specialization using
`clang::annoate` with packs.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D114439/new/
https://reviews.llvm.org/D114439
Files:
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseExpr.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/test/Parser/cxx0x-attributes.cpp
clang/test/Sema/annotate.c
clang/test/SemaCXX/attr-annotate.cpp
clang/test/SemaTemplate/attributes.cpp
clang/utils/TableGen/ClangAttrEmitter.cpp
Index: clang/utils/TableGen/ClangAttrEmitter.cpp
===================================================================
--- clang/utils/TableGen/ClangAttrEmitter.cpp
+++ clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -202,9 +202,9 @@
bool Fake;
public:
- Argument(const Record &Arg, StringRef Attr)
- : lowerName(std::string(Arg.getValueAsString("Name"))),
- upperName(lowerName), attrName(Attr), isOpt(false), Fake(false) {
+ 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]);
@@ -215,6 +215,8 @@
if (lowerName == "interface")
lowerName = "interface_";
}
+ Argument(const Record &Arg, StringRef Attr)
+ : Argument(Arg.getValueAsString("Name"), Attr) {}
virtual ~Argument() = default;
StringRef getLowerName() const { return lowerName; }
@@ -666,6 +668,11 @@
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; }
@@ -688,6 +695,18 @@
<< "); }\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;
}
@@ -1169,6 +1188,9 @@
: 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()
@@ -2138,6 +2160,11 @@
}
}
+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";
@@ -2149,7 +2176,7 @@
if (Args.empty())
continue;
- if (Args[0]->getSuperClasses().back().first->getName() != "TypeArgument")
+ if (!isTypeArgument(Args[0]))
continue;
// All these spellings take a single type argument.
@@ -2179,7 +2206,7 @@
OS << "#endif // CLANG_ATTR_ARG_CONTEXT_LIST\n\n";
}
-static bool isIdentifierArgument(Record *Arg) {
+static bool isIdentifierArgument(const Record *Arg) {
return !Arg->getSuperClasses().empty() &&
llvm::StringSwitch<bool>(Arg->getSuperClasses().back().first->getName())
.Case("IdentifierArgument", true)
@@ -2188,7 +2215,7 @@
.Default(false);
}
-static bool isVariadicIdentifierArgument(Record *Arg) {
+static bool isVariadicIdentifierArgument(const Record *Arg) {
return !Arg->getSuperClasses().empty() &&
llvm::StringSwitch<bool>(
Arg->getSuperClasses().back().first->getName())
@@ -2264,6 +2291,26 @@
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;
+
+ // All these spellings take are parsed unevaluated.
+ forEachUniqueSpelling(Attr, [&](const FlattenedSpelling &S) {
+ OS << ".Case(\"" << S.name() << "\", "
+ << "true"
+ << ")\n";
+ });
+ }
+ OS << "#endif // CLANG_ATTR_ACCEPTS_EXPR_PACK\n\n";
+}
+
static void emitAttributes(RecordKeeper &Records, raw_ostream &OS,
bool Header) {
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
@@ -2320,6 +2367,16 @@
std::vector<std::unique_ptr<Argument>> Args;
Args.reserve(ArgRecords.size());
+ bool AttrAcceptsExprPack = Attr->getValueAsBit("AcceptsExprPack");
+ if (AttrAcceptsExprPack &&
+ std::any_of(
+ ArgRecords.begin(), ArgRecords.end(), [&](const Record *ArgR) {
+ return isIdentifierArgument(ArgR) ||
+ isVariadicIdentifierArgument(ArgR) || isTypeArgument(ArgR);
+ }))
+ PrintFatalError(Attr->getLoc(), "Attributes accepting packs cannot also "
+ "have identifier or type arguments.");
+
bool HasOptArg = false;
bool HasFakeArg = false;
for (const auto *ArgRecord : ArgRecords) {
@@ -2337,6 +2394,16 @@
}
}
+ 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";
@@ -2363,7 +2430,7 @@
});
// Emit CreateImplicit factory methods.
- auto emitCreate = [&](bool Implicit, bool emitFake) {
+ auto emitCreate = [&](bool Implicit, bool DelayedArgsOnly, bool emitFake) {
if (Header)
OS << " static ";
OS << R.getName() << "Attr *";
@@ -2372,12 +2439,20 @@
OS << "Create";
if (Implicit)
OS << "Implicit";
+ if (DelayedArgsOnly)
+ OS << "WithDelayedArgs";
OS << "(";
OS << "ASTContext &Ctx";
- for (auto const &ai : Args) {
- if (ai->isFake() && !emitFake) continue;
+ if (!DelayedArgsOnly) {
+ for (auto const &ai : Args) {
+ if (ai->isFake() && !emitFake)
+ continue;
+ OS << ", ";
+ ai->writeCtorParameters(OS);
+ }
+ } else {
OS << ", ";
- ai->writeCtorParameters(OS);
+ DelayedArgs->writeCtorParameters(OS);
}
OS << ", const AttributeCommonInfo &CommonInfo";
if (Header && Implicit)
@@ -2391,10 +2466,13 @@
OS << " {\n";
OS << " auto *A = new (Ctx) " << R.getName();
OS << "Attr(Ctx, CommonInfo";
- for (auto const &ai : Args) {
- if (ai->isFake() && !emitFake) continue;
- OS << ", ";
- ai->writeImplicitCtorArgs(OS);
+ if (!DelayedArgsOnly) {
+ for (auto const &ai : Args) {
+ if (ai->isFake() && !emitFake)
+ continue;
+ OS << ", ";
+ ai->writeImplicitCtorArgs(OS);
+ }
}
OS << ");\n";
if (Implicit) {
@@ -2405,10 +2483,16 @@
"!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 emitFake) {
+ auto emitCreateNoCI = [&](bool Implicit, bool DelayedArgsOnly,
+ bool emitFake) {
if (Header)
OS << " static ";
OS << R.getName() << "Attr *";
@@ -2417,12 +2501,20 @@
OS << "Create";
if (Implicit)
OS << "Implicit";
+ if (DelayedArgsOnly)
+ OS << "WithDelayedArgs";
OS << "(";
OS << "ASTContext &Ctx";
- for (auto const &ai : Args) {
- if (ai->isFake() && !emitFake) continue;
+ if (!DelayedArgsOnly) {
+ for (auto const &ai : Args) {
+ if (ai->isFake() && !emitFake)
+ continue;
+ OS << ", ";
+ ai->writeCtorParameters(OS);
+ }
+ } else {
OS << ", ";
- ai->writeCtorParameters(OS);
+ DelayedArgs->writeCtorParameters(OS);
}
OS << ", SourceRange Range, AttributeCommonInfo::Syntax Syntax";
if (!ElideSpelling) {
@@ -2451,38 +2543,55 @@
OS << " return Create";
if (Implicit)
OS << "Implicit";
+ if (DelayedArgsOnly)
+ OS << "WithDelayedArgs";
OS << "(Ctx";
- for (auto const &ai : Args) {
- if (ai->isFake() && !emitFake) continue;
+ if (!DelayedArgsOnly) {
+ for (auto const &ai : Args) {
+ if (ai->isFake() && !emitFake)
+ continue;
+ OS << ", ";
+ ai->writeImplicitCtorArgs(OS);
+ }
+ } else {
OS << ", ";
- ai->writeImplicitCtorArgs(OS);
+ DelayedArgs->writeImplicitCtorArgs(OS);
}
OS << ", I);\n";
OS << "}\n\n";
};
- auto emitCreates = [&](bool emitFake) {
- emitCreate(true, emitFake);
- emitCreate(false, emitFake);
- emitCreateNoCI(true, emitFake);
- emitCreateNoCI(false, emitFake);
+ 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(true);
+ emitCreates(false, true);
// Emit a CreateImplicit that takes all the non-fake arguments.
if (HasFakeArg)
- emitCreates(false);
+ 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) {
+ auto emitCtor = [&](bool emitOpt, bool emitFake, bool emitNoArgs) {
auto shouldEmitArg = [=](const std::unique_ptr<Argument> &arg) {
- if (arg->isFake()) return emitFake;
- if (arg->isOptional()) return emitOpt;
+ if (emitNoArgs)
+ return false;
+ if (arg->isFake())
+ return emitFake;
+ if (arg->isOptional())
+ return emitOpt;
return true;
};
if (Header)
@@ -2493,7 +2602,8 @@
<< "Attr(ASTContext &Ctx, const AttributeCommonInfo &CommonInfo";
OS << '\n';
for (auto const &ai : Args) {
- if (!shouldEmitArg(ai)) continue;
+ if (!shouldEmitArg(ai))
+ continue;
OS << " , ";
ai->writeCtorParameters(OS);
OS << "\n";
@@ -2523,11 +2633,17 @@
}
OS << "\n";
}
+ if (DelayedArgs) {
+ OS << " , ";
+ DelayedArgs->writeCtorDefaultInitializers(OS);
+ OS << "\n";
+ }
OS << " {\n";
for (auto const &ai : Args) {
- if (!shouldEmitArg(ai)) continue;
+ if (!shouldEmitArg(ai))
+ continue;
ai->writeCtorBody(OS);
}
OS << "}\n\n";
@@ -2538,15 +2654,19 @@
// Emit a constructor that includes all the arguments.
// This is necessary for cloning.
- emitCtor(true, true);
+ emitCtor(true, true, false);
// Emit a constructor that takes all the non-fake arguments.
if (HasFakeArg)
- emitCtor(true, false);
+ emitCtor(true, false, false);
// Emit a constructor that takes all the non-fake, non-optional arguments.
if (HasOptArg)
- emitCtor(false, false);
+ emitCtor(false, false, false);
+
+ // Emit constructors that takes no arguments.
+ if (DelayedArgs)
+ emitCtor(false, false, true);
if (Header) {
OS << '\n';
@@ -2592,6 +2712,11 @@
}
if (Header) {
+ if (DelayedArgs) {
+ DelayedArgs->writeAccessors(OS);
+ DelayedArgs->writeSetter(OS);
+ }
+
OS << R.getValueAsString("AdditionalMembers");
OS << "\n\n";
@@ -2600,6 +2725,9 @@
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";
@@ -2611,6 +2739,11 @@
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);
@@ -2924,6 +3057,7 @@
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) {
@@ -2936,6 +3070,12 @@
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) {
@@ -2952,6 +3092,12 @@
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";
}
@@ -2979,6 +3125,8 @@
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);
@@ -3266,6 +3414,10 @@
for (const auto *Arg : ArgRecords)
createArgument(*Arg, R.getName())->writeASTVisitorTraversal(OS);
+ if (Attr->getValueAsBit("AcceptsExprPack"))
+ VariadicExprArgument("DelayedArgs", R.getName())
+ .writeASTVisitorTraversal(OS);
+
OS << " return true;\n";
OS << "}\n\n";
}
@@ -4173,6 +4325,9 @@
for (const auto *Arg : Args)
createArgument(*Arg, R.getName())->writeDump(SS);
+ if (Attr->getValueAsBit("AcceptsExprPack"))
+ VariadicExprArgument("DelayedArgs", R.getName()).writeDump(OS);
+
if (SS.tell()) {
OS << " void Visit" << R.getName() << "Attr(const " << R.getName()
<< "Attr *A) {\n";
@@ -4200,6 +4355,8 @@
Args = R.getValueAsListOfDefs("Args");
for (const auto *Arg : Args)
createArgument(*Arg, R.getName())->writeDumpChildren(SS);
+ if (Attr->getValueAsBit("AcceptsExprPack"))
+ VariadicExprArgument("DelayedArgs", R.getName()).writeDumpChildren(SS);
if (SS.tell()) {
OS << " void Visit" << R.getName() << "Attr(const " << R.getName()
<< "Attr *A) {\n";
@@ -4219,6 +4376,7 @@
emitClangAttrIdentifierArgList(Records, OS);
emitClangAttrVariadicIdentifierArgList(Records, OS);
emitClangAttrThisIsaIdentifierArgList(Records, OS);
+ emitClangAttrAcceptsExprPack(Records, OS);
emitClangAttrTypeArgList(Records, OS);
emitClangAttrLateParsedList(Records, OS);
}
Index: clang/test/SemaTemplate/attributes.cpp
===================================================================
--- clang/test/SemaTemplate/attributes.cpp
+++ clang/test/SemaTemplate/attributes.cpp
@@ -54,6 +54,7 @@
inline void WBCFRelease(__attribute__((cf_consumed)) T aValue) { if(aValue) CFRelease(aValue); }
}
+namespace attribute_annotate {
// CHECK: FunctionTemplateDecl {{.*}} HasAnnotations
// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOO"
// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAR"
@@ -64,6 +65,189 @@
template<typename T> [[clang::annotate("ANNOTATE_FOO"), clang::annotate("ANNOTATE_BAR")]] void HasAnnotations();
void UseAnnotations() { HasAnnotations<int>(); }
+// CHECK: FunctionTemplateDecl {{.*}} HasPackAnnotations
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAZ"
+// CHECK: FunctionDecl {{.*}} HasPackAnnotations
+// CHECK: TemplateArgument{{.*}} pack
+// CHECK-NEXT: TemplateArgument{{.*}} integral 1
+// CHECK-NEXT: TemplateArgument{{.*}} integral 2
+// CHECK-NEXT: TemplateArgument{{.*}} integral 3
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAZ"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 1
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 2
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 3
+template <int... Is> [[clang::annotate("ANNOTATE_BAZ", Is...)]] void HasPackAnnotations();
+void UsePackAnnotations() { HasPackAnnotations<1, 2, 3>(); }
+
+template <int... Is> [[clang::annotate(Is...)]] void HasOnlyPackAnnotation() {} // expected-error {{'annotate' attribute takes at least 1 argument}} expected-error {{'annotate' attribute requires a string}}
+
+void UseOnlyPackAnnotations() {
+ HasOnlyPackAnnotation<>(); // expected-note {{in instantiation of function template specialization 'attribute_annotate::HasOnlyPackAnnotation<>' requested here}}
+ HasOnlyPackAnnotation<1>(); // expected-note {{in instantiation of function template specialization 'attribute_annotate::HasOnlyPackAnnotation<1>' requested here}}
+}
+
+// CHECK: ClassTemplateDecl {{.*}} AnnotatedPackTemplateStruct
+// CHECK: CXXRecordDecl {{.*}} struct AnnotatedPackTemplateStruct definition
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOZ"
+// CHECK: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition
+// CHECK: TemplateArgument{{.*}} type 'int'
+// CHECK: TemplateArgument{{.*}} pack
+// CHECK-NEXT: TemplateArgument{{.*}} integral 1
+// CHECK-NEXT: TemplateArgument{{.*}} integral 2
+// CHECK-NEXT: TemplateArgument{{.*}} integral 3
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOO"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 1
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 2
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 3
+// CHECK: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition
+// CHECK: TemplateArgument{{.*}} type 'float'
+// CHECK: TemplateArgument{{.*}} pack
+// CHECK-NEXT: TemplateArgument{{.*}} integral 3
+// CHECK-NEXT: TemplateArgument{{.*}} integral 2
+// CHECK-NEXT: TemplateArgument{{.*}} integral 1
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOZ"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 4
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 5
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 6
+// CHECK: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition
+// CHECK: TemplateArgument{{.*}} type 'bool'
+// CHECK: TemplateArgument{{.*}} pack
+// CHECK-NEXT: TemplateArgument{{.*}} integral 7
+// CHECK-NEXT: TemplateArgument{{.*}} integral 8
+// CHECK-NEXT: TemplateArgument{{.*}} integral 9
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOZ"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 7
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 8
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 9
+// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition
+// CHECK: TemplateArgument{{.*}} type 'int'
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOO"
+// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition
+// CHECK: TemplateArgument{{.*}} type 'float'
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOZ"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 4
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 5
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 6
+template <typename T, int... Is> struct [[clang::annotate("ANNOTATE_FOZ", Is...)]] AnnotatedPackTemplateStruct{};
+template <int... Is> struct [[clang::annotate("ANNOTATE_BOO", Is...)]] AnnotatedPackTemplateStruct<int, Is...>{};
+template <int... Is> struct [[clang::annotate("ANNOTATE_FOZ", 4, 5, 6)]] AnnotatedPackTemplateStruct<float, Is...>{};
+template <int... Is> struct [[clang::annotate(Is...)]] AnnotatedPackTemplateStruct<char, Is...>{}; // expected-error {{'annotate' attribute requires a string}} expected-error {{'annotate' attribute takes at least 1 argument}}
+void UseAnnotatedPackTemplateStructSpecializations() {
+ AnnotatedPackTemplateStruct<int, 1, 2, 3> Instance1{};
+ AnnotatedPackTemplateStruct<float, 3, 2, 1> Instance2{};
+ AnnotatedPackTemplateStruct<bool, 7, 8, 9> Instance3{};
+ AnnotatedPackTemplateStruct<char, 1, 2, 3> Instance4{}; // expected-note {{in instantiation of template class 'attribute_annotate::AnnotatedPackTemplateStruct<char, 1, 2, 3>' requested here}}
+ AnnotatedPackTemplateStruct<char> Instance5{}; // expected-note {{in instantiation of template class 'attribute_annotate::AnnotatedPackTemplateStruct<char>' requested here}}
+}
+
+// CHECK: ClassTemplateDecl {{.*}} InvalidAnnotatedPackTemplateStruct
+// CHECK: CXXRecordDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition
+// CHECK: AnnotateAttr {{.*}} ""
+// CHECK: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition
+// CHECK: TemplateArgument{{.*}} type 'int'
+// CHECK: TemplateArgument{{.*}} pack
+// CHECK-NEXT: TemplateArgument{{.*}} integral 1
+// CHECK-NEXT: TemplateArgument{{.*}} integral 2
+// CHECK-NEXT: TemplateArgument{{.*}} integral 3
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BIR"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 1
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 2
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 3
+// CHECK: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition
+// CHECK: TemplateArgument{{.*}} type 'float'
+// CHECK: TemplateArgument{{.*}} pack
+// CHECK-NEXT: TemplateArgument{{.*}} integral 3
+// CHECK-NEXT: TemplateArgument{{.*}} integral 2
+// CHECK-NEXT: TemplateArgument{{.*}} integral 1
+// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition
+// CHECK: TemplateArgument{{.*}} type 'int'
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BIR"
+// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition
+// CHECK: TemplateArgument{{.*}} type 'float'
+// CHECK-NOT: AnnotateAttr {{.*}} ""
+template <typename T, int... Is> struct [[clang::annotate(Is...)]] InvalidAnnotatedPackTemplateStruct{}; // expected-error {{'annotate' attribute requires a string}} expected-error {{'annotate' attribute takes at least 1 argument}}
+template <int... Is> struct [[clang::annotate("ANNOTATE_BIR", Is...)]] InvalidAnnotatedPackTemplateStruct<int, Is...>{};
+template <int... Is> struct InvalidAnnotatedPackTemplateStruct<float, Is...> {};
+template <> struct InvalidAnnotatedPackTemplateStruct<char, 5, 6, 7> {};
+void UseInvalidAnnotatedPackTemplateStruct() {
+ InvalidAnnotatedPackTemplateStruct<int, 1, 2, 3> Instance1{};
+ InvalidAnnotatedPackTemplateStruct<float, 3, 2, 1> Instance2{};
+ InvalidAnnotatedPackTemplateStruct<char, 5, 6, 7> Instance3{};
+ InvalidAnnotatedPackTemplateStruct<bool, 7, 8, 9> Instance4{}; // expected-note {{in instantiation of template class 'attribute_annotate::InvalidAnnotatedPackTemplateStruct<bool, 7, 8, 9>' requested here}}
+ InvalidAnnotatedPackTemplateStruct<bool> Instance5{}; // expected-note {{in instantiation of template class 'attribute_annotate::InvalidAnnotatedPackTemplateStruct<bool>' requested here}}
+}
+
+// CHECK: FunctionTemplateDecl {{.*}} RedeclaredAnnotatedFunc
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FAR"
+// CHECK: FunctionDecl {{.*}} RedeclaredAnnotatedFunc
+// CHECK: TemplateArgument{{.*}} pack
+// CHECK-NEXT: TemplateArgument{{.*}} integral 1
+// CHECK-NEXT: TemplateArgument{{.*}} integral 2
+// CHECK-NEXT: TemplateArgument{{.*}} integral 3
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FAR"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 1
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 2
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 3
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FIZ"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 4
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 5
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOZ"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 6
+// CHECK: FunctionTemplateDecl {{.*}} RedeclaredAnnotatedFunc
+// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR"
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOZ"
+// CHECK: FunctionTemplateDecl {{.*}} RedeclaredAnnotatedFunc
+// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR"
+// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_BOZ"
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FIZ"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 4
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 5
+// CHECK: FunctionTemplateDecl {{.*}} RedeclaredAnnotatedFunc
+// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR"
+// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_FIZ"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 4
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 5
+// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOZ"
+// CHECK: ConstantExpr {{.*}} 'int'
+// CHECK-NEXT: value: Int 6
+template <int... Is> [[clang::annotate("ANNOTATE_FAR", Is...)]] void RedeclaredAnnotatedFunc();
+template <int... Is> [[clang::annotate("ANNOTATE_BOZ", Is...)]] void RedeclaredAnnotatedFunc();
+template <int... Is> [[clang::annotate("ANNOTATE_FIZ", 4, 5)]] void RedeclaredAnnotatedFunc();
+template <int... Is> [[clang::annotate("ANNOTATE_BOZ", 6)]] void RedeclaredAnnotatedFunc(){};
+void UseRedeclaredAnnotatedFunc() {
+ RedeclaredAnnotatedFunc<1, 2, 3>();
+}
+
+} // namespace attribute_annotate
+
namespace preferred_name {
int x [[clang::preferred_name("frank")]]; // expected-error {{expected a type}}
int y [[clang::preferred_name(int)]]; // expected-warning {{'preferred_name' attribute only applies to class templates}}
Index: clang/test/SemaCXX/attr-annotate.cpp
===================================================================
--- clang/test/SemaCXX/attr-annotate.cpp
+++ clang/test/SemaCXX/attr-annotate.cpp
@@ -127,4 +127,10 @@
[[clang::annotate("", foldable_but_invalid())]] void f1() {}
// expected-error@-1 {{'annotate' attribute requires parameter 1 to be a constant expression}}
+
+[[clang::annotate()]] void f2() {}
+// expected-error@-1 {{'annotate' attribute takes at least 1 argument}}
+
+template <typename T> [[clang::annotate()]] void f2() {}
+// expected-error@-1 {{'annotate' attribute takes at least 1 argument}}
}
Index: clang/test/Sema/annotate.c
===================================================================
--- clang/test/Sema/annotate.c
+++ clang/test/Sema/annotate.c
@@ -12,4 +12,7 @@
int v = __builtin_annotation(z, (char*) L"bar"); // expected-error {{second argument to __builtin_annotation must be a non-wide string constant}}
int w = __builtin_annotation(z, "foo");
float b = __builtin_annotation(*a, "foo"); // expected-error {{first argument to __builtin_annotation must be an integer}}
+
+ __attribute__((annotate())) int c; // expected-error {{'annotate' attribute takes at least 1 argument}}
+ [[clang::annotate()]] int c2; // expected-error {{'annotate' attribute takes at least 1 argument}}
}
Index: clang/test/Parser/cxx0x-attributes.cpp
===================================================================
--- clang/test/Parser/cxx0x-attributes.cpp
+++ clang/test/Parser/cxx0x-attributes.cpp
@@ -263,6 +263,19 @@
void bar [[noreturn...]] (); // expected-error {{attribute 'noreturn' cannot be used as an attribute pack}}
}
+template <int... Is> void variadic_nttp() {
+ void bar [[noreturn...]] (); // expected-error {{attribute 'noreturn' cannot be used as an attribute pack}}
+ void baz [[clang::no_sanitize(Is...)]] (); // expected-error {{attribute 'no_sanitize' does not support argument pack expansion}}
+ void bor [[clang::annotate("A", "V" ...)]] (); // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
+ void bir [[clang::annotate("B", {1, 2, 3, 4})]] (); // expected-error {{'annotate' attribute requires parameter 1 to be a constant expression}} expected-note {{subexpression not valid in a constant expression}}
+ void boo [[unknown::foo(Is...)]] (); // expected-warning {{unknown attribute 'foo' ignored}}
+ void faz [[clang::annotate("C", (Is + ...))]] (); // expected-warning {{pack fold expression is a C++17 extension}}
+ void far [[clang::annotate("D", Is...)]] ();
+ void foz [[clang::annotate("E", 1, 2, 3, Is...)]] ();
+ void fiz [[clang::annotate("F", Is..., 1, 2, 3)]] ();
+ void fir [[clang::annotate("G", 1, Is..., 2, 3)]] ();
+}
+
// Expression tests
void bar () {
// FIXME: GCC accepts [[gnu::noreturn]] on a lambda, even though it appertains
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -188,15 +188,25 @@
const AnnotateAttr *Attr, Decl *New) {
EnterExpressionEvaluationContext Unevaluated(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ // If the attribute has delayed arguments it will have to instantiate those
+ // and handle them as new arguments for the attribute.
+ bool HasDelayedArgs = Attr->delayedArgs_size();
+
+ ArrayRef<Expr *> ArgsToInstantiate =
+ HasDelayedArgs
+ ? ArrayRef<Expr *>{Attr->delayedArgs_begin(), Attr->delayedArgs_end()}
+ : ArrayRef<Expr *>{Attr->args_begin(), Attr->args_end()};
+
SmallVector<Expr *, 4> Args;
- Args.reserve(Attr->args_size());
- for (auto *E : Attr->args()) {
- ExprResult Result = S.SubstExpr(E, TemplateArgs);
- if (!Result.isUsable())
- return;
- Args.push_back(Result.get());
- }
- S.AddAnnotationAttr(New, *Attr, Attr->getAnnotation(), Args);
+ if (S.SubstExprs(ArgsToInstantiate,
+ /*IsCall=*/false, TemplateArgs, Args))
+ return;
+
+ if (HasDelayedArgs)
+ S.HandleAnnotateAttr(New, *Attr, Args);
+ else
+ S.AddAnnotationAttr(New, *Attr, Attr->getAnnotation(), Args);
}
static Expr *instantiateDependentFunctionAttrCondition(
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -334,6 +334,25 @@
return true;
}
+/// Check if the argument \p E is a ASCII string literal. If not emit an error
+/// and return false, otherwise set \p Str to the value of the string literal
+/// and return true.
+bool Sema::checkStringLiteralExpr(const AttributeCommonInfo &CI, const Expr *E,
+ StringRef &Str, SourceLocation *ArgLocation) {
+ const auto *Literal = dyn_cast<StringLiteral>(E->IgnoreParenCasts());
+ if (ArgLocation)
+ *ArgLocation = E->getBeginLoc();
+
+ if (!Literal || !Literal->isAscii()) {
+ Diag(E->getBeginLoc(), diag::err_attribute_argument_type)
+ << CI << AANT_ArgumentString;
+ return false;
+ }
+
+ Str = Literal->getString();
+ return true;
+}
+
/// Check if the argument \p ArgNum of \p Attr is a ASCII string literal.
/// If not emit an error and return false. If the argument is an identifier it
/// will emit an error with a fixit hint and treat it as if it was a string
@@ -356,18 +375,7 @@
// Now check for an actual string literal.
Expr *ArgExpr = AL.getArgAsExpr(ArgNum);
- const auto *Literal = dyn_cast<StringLiteral>(ArgExpr->IgnoreParenCasts());
- if (ArgLocation)
- *ArgLocation = ArgExpr->getBeginLoc();
-
- if (!Literal || !Literal->isAscii()) {
- Diag(ArgExpr->getBeginLoc(), diag::err_attribute_argument_type)
- << AL << AANT_ArgumentString;
- return false;
- }
-
- Str = Literal->getString();
- return true;
+ return checkStringLiteralExpr(AL, ArgExpr, Str, ArgLocation);
}
/// Applies the given attribute to the Decl without performing any
@@ -4166,21 +4174,42 @@
D->addAttr(Attr);
}
-static void handleAnnotateAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
- // Make sure that there is a string literal as the annotation's first
- // argument.
+void Sema::HandleAnnotateAttr(Decl *D, const AttributeCommonInfo &CI,
+ MutableArrayRef<Expr *> AllArgs) {
+ if (AllArgs.size() < 1) {
+ Diag(CI.getLoc(), diag::err_attribute_too_few_arguments) << CI << 1;
+ return;
+ }
+
StringRef Str;
- if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
+ if (!checkStringLiteralExpr(CI, AllArgs[0], Str))
return;
llvm::SmallVector<Expr *, 4> Args;
- Args.reserve(AL.getNumArgs() - 1);
- for (unsigned Idx = 1; Idx < AL.getNumArgs(); Idx++) {
- assert(!AL.isArgIdent(Idx));
- Args.push_back(AL.getArgAsExpr(Idx));
+ Args.reserve(AllArgs.size() - 1);
+ for (unsigned I = 1; I < AllArgs.size(); ++I)
+ Args.push_back(AllArgs[I]);
+
+ AddAnnotationAttr(D, CI, Str, Args);
+}
+
+static void handleAnnotateAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ llvm::SmallVector<Expr *, 4> AllArgs;
+ AllArgs.reserve(AL.getNumArgs());
+ for (unsigned I = 0; I < AL.getNumArgs(); ++I) {
+ assert(!AL.isArgIdent(I));
+ AllArgs.push_back(AL.getArgAsExpr(I));
+ }
+
+ // If the first argument is value dependent we delay setting the arguments.
+ if (AllArgs.size() && AllArgs[0]->isValueDependent()) {
+ auto *Attr = AnnotateAttr::CreateWithDelayedArgs(
+ S.getASTContext(), AllArgs.data(), AllArgs.size(), AL);
+ D->addAttr(Attr);
+ return;
}
- S.AddAnnotationAttr(D, AL, Str, Args);
+ S.HandleAnnotateAttr(D, AL, AllArgs);
}
static void handleAlignValueAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ clang/lib/Parse/ParseExpr.cpp
@@ -3356,7 +3356,9 @@
/// \endverbatim
bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
SmallVectorImpl<SourceLocation> &CommaLocs,
- llvm::function_ref<void()> ExpressionStarts) {
+ llvm::function_ref<void()> ExpressionStarts,
+ bool FailImmediatelyOnInvalidExpr,
+ bool EarlyTypoCorrection) {
bool SawError = false;
while (true) {
if (ExpressionStarts)
@@ -3369,6 +3371,9 @@
} else
Expr = ParseAssignmentExpression();
+ if (EarlyTypoCorrection)
+ Expr = Actions.CorrectDelayedTyposInExpr(Expr);
+
if (Tok.is(tok::ellipsis))
Expr = Actions.ActOnPackExpansion(Expr.get(), ConsumeToken());
else if (Tok.is(tok::code_completion)) {
@@ -3382,8 +3387,10 @@
break;
}
if (Expr.isInvalid()) {
- SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch);
SawError = true;
+ if (FailImmediatelyOnInvalidExpr)
+ break;
+ SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch);
} else {
Exprs.push_back(Expr.get());
}
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -300,6 +300,15 @@
#undef CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST
}
+/// Determine if an attribute accepts parameter packs.
+static bool attributeAcceptsExprPack(const IdentifierInfo &II) {
+#define CLANG_ATTR_ACCEPTS_EXPR_PACK
+ return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+ .Default(false);
+#undef CLANG_ATTR_ACCEPTS_EXPR_PACK
+}
+
/// Determine whether the given attribute parses a type argument.
static bool attributeIsTypeArgAttr(const IdentifierInfo &II) {
#define CLANG_ATTR_TYPE_ARG_LIST
@@ -366,6 +375,8 @@
bool ChangeKWThisToIdent = attributeTreatsKeywordThisAsIdentifier(*AttrName);
bool AttributeIsTypeArgAttr = attributeIsTypeArgAttr(*AttrName);
+ bool AttributeHasVariadicIdentifierArg =
+ attributeHasVariadicIdentifierArg(*AttrName);
// Interpret "kw_this" as an identifier if the attributed requests it.
if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
@@ -374,8 +385,8 @@
ArgsVector ArgExprs;
if (Tok.is(tok::identifier)) {
// If this attribute wants an 'identifier' argument, make it so.
- bool IsIdentifierArg = attributeHasIdentifierArg(*AttrName) ||
- attributeHasVariadicIdentifierArg(*AttrName);
+ bool IsIdentifierArg = AttributeHasVariadicIdentifierArg ||
+ attributeHasIdentifierArg(*AttrName);
ParsedAttr::Kind AttrKind =
ParsedAttr::getParsedKind(AttrName, ScopeName, Syntax);
@@ -397,42 +408,80 @@
if (!ArgExprs.empty())
ConsumeToken();
- // Parse the non-empty comma-separated list of expressions.
- do {
- // Interpret "kw_this" as an identifier if the attributed requests it.
- if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
- Tok.setKind(tok::identifier);
+ if (AttributeIsTypeArgAttr) {
+ // FIXME: Multiple type arguments are not implemented.
+ TypeResult T = ParseTypeName();
+ if (T.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return 0;
+ }
+ if (T.isUsable())
+ TheParsedType = T.get();
+ } else if (AttributeHasVariadicIdentifierArg) {
+ // Parse variadic identifier arg. This can either consume identifiers or
+ // expressions.
+ // FIXME: Expression parameter pack expansion is not currently supported
+ // inside variadic identifier args.
+ do {
+ // Interpret "kw_this" as an identifier if the attributed requests it.
+ if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
+ Tok.setKind(tok::identifier);
+
+ ExprResult ArgExpr;
+ if (Tok.is(tok::identifier)) {
+ ArgExprs.push_back(ParseIdentifierLoc());
+ } else {
+ bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions,
+ Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
+ : Sema::ExpressionEvaluationContext::ConstantEvaluated);
- ExprResult ArgExpr;
- if (AttributeIsTypeArgAttr) {
- TypeResult T = ParseTypeName();
- if (T.isInvalid()) {
- SkipUntil(tok::r_paren, StopAtSemi);
- return 0;
+ ExprResult ArgExpr(
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
+
+ if (ArgExpr.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return 0;
+ }
+ ArgExprs.push_back(ArgExpr.get());
}
- if (T.isUsable())
- TheParsedType = T.get();
- break; // FIXME: Multiple type arguments are not implemented.
- } else if (Tok.is(tok::identifier) &&
- attributeHasVariadicIdentifierArg(*AttrName)) {
- ArgExprs.push_back(ParseIdentifierLoc());
- } else {
- bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
- EnterExpressionEvaluationContext Unevaluated(
- Actions,
- Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
- : Sema::ExpressionEvaluationContext::ConstantEvaluated);
-
- ExprResult ArgExpr(
- Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
- if (ArgExpr.isInvalid()) {
+ // Eat the comma, move to the next argument
+ } while (TryConsumeToken(tok::comma));
+ } else {
+ // General case. Parse all available expressions.
+ bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Uneval
+ ? Sema::ExpressionEvaluationContext::Unevaluated
+ : Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ CommaLocsTy CommaLocs;
+ ExprVector ParsedExprs;
+ if (ParseExpressionList(ParsedExprs, CommaLocs,
+ llvm::function_ref<void()>(),
+ /*FailImmediatelyOnInvalidExpr=*/true,
+ /*EarlyTypoCorrection=*/true)) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return 0;
+ }
+
+ // Pack expansion must currently be explicitly supported by an attribute.
+ for (size_t I = 0; I < ParsedExprs.size(); ++I) {
+ if (!isa<PackExpansionExpr>(ParsedExprs[I]))
+ continue;
+
+ if (!attributeAcceptsExprPack(*AttrName)) {
+ Diag(Tok.getLocation(),
+ diag::err_attribute_argument_parm_pack_not_supported)
+ << AttrName;
SkipUntil(tok::r_paren, StopAtSemi);
return 0;
}
- ArgExprs.push_back(ArgExpr.get());
}
- // Eat the comma, move to the next argument
- } while (TryConsumeToken(tok::comma));
+
+ ArgExprs.insert(ArgExprs.end(), ParsedExprs.begin(), ParsedExprs.end());
+ }
}
SourceLocation RParen = Tok.getLocation();
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -4378,6 +4378,9 @@
const FunctionDecl *FD = nullptr);
bool CheckAttrTarget(const ParsedAttr &CurrAttr);
bool CheckAttrNoArgs(const ParsedAttr &CurrAttr);
+ bool checkStringLiteralExpr(const AttributeCommonInfo &CI, const Expr *E,
+ StringRef &Str,
+ SourceLocation *ArgLocation = nullptr);
bool checkStringLiteralArgumentAttr(const ParsedAttr &Attr, unsigned ArgNum,
StringRef &Str,
SourceLocation *ArgLocation = nullptr);
@@ -10263,6 +10266,10 @@
/// AddAnnotationAttr - Adds an annotation Annot with Args arguments to D.
void AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Annot, MutableArrayRef<Expr *> Args);
+ /// HandleAnnotationAttr - Verifies the arguments in AllArgs and adds an
+ /// an annotation with the verified arguments to D.
+ void HandleAnnotateAttr(Decl *D, const AttributeCommonInfo &CI,
+ MutableArrayRef<Expr *> AllArgs);
/// AddLaunchBoundsAttr - Adds a launch_bounds attribute to a particular
/// declaration.
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -1814,7 +1814,9 @@
bool ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
SmallVectorImpl<SourceLocation> &CommaLocs,
llvm::function_ref<void()> ExpressionStarts =
- llvm::function_ref<void()>());
+ llvm::function_ref<void()>(),
+ bool FailImmediatelyOnInvalidExpr = false,
+ bool EarlyTypoCorrection = false);
/// ParseSimpleExpressionList - A simple comma-separated list of expressions,
/// used for misc language extensions.
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -719,6 +719,8 @@
def err_l_square_l_square_not_attribute : Error<
"C++11 only allows consecutive left square brackets when "
"introducing an attribute">;
+def err_attribute_argument_parm_pack_not_supported : Error<
+ "attribute %0 does not support argument pack expansion">;
def err_ms_declspec_type : Error<
"__declspec attributes must be an identifier or string literal">;
def err_ms_property_no_getter_or_putter : Error<
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -541,6 +541,8 @@
// match rules.
// - It has GNU/CXX11 spelling and doesn't require delayed parsing.
bit PragmaAttributeSupport;
+ // Set to true if this attribute accepts parameter pack expansion expressions.
+ bit AcceptsExprPack = 0;
// Lists language options, one of which is required to be true for the
// attribute to be applicable. If empty, no language options are required.
list<LangOpt> LangOpts = [];
@@ -784,6 +786,7 @@
}
}];
let PragmaAttributeSupport = 1;
+ let AcceptsExprPack = 1;
let Documentation = [Undocumented];
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits