mboehme updated this revision to Diff 426977.
mboehme added a comment.
Rebased to a more recent base change.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D111548/new/
https://reviews.llvm.org/D111548
Files:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/AST/TypePrinter.cpp
clang/lib/Parse/ParseDecl.cpp
clang/lib/Sema/SemaAttr.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaType.cpp
clang/test/AST/attr-annotate-type.c
clang/test/CodeGenCXX/annotate-type.cpp
clang/test/Sema/annotate-type.c
clang/test/SemaCXX/annotate-type.cpp
clang/unittests/AST/AttrTest.cpp
Index: clang/unittests/AST/AttrTest.cpp
===================================================================
--- clang/unittests/AST/AttrTest.cpp
+++ clang/unittests/AST/AttrTest.cpp
@@ -7,7 +7,10 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/Attr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/AttrKinds.h"
+#include "clang/Tooling/Tooling.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -15,10 +18,154 @@
namespace {
+using clang::ast_matchers::constantExpr;
+using clang::ast_matchers::equals;
+using clang::ast_matchers::functionDecl;
+using clang::ast_matchers::has;
+using clang::ast_matchers::hasDescendant;
+using clang::ast_matchers::hasName;
+using clang::ast_matchers::integerLiteral;
+using clang::ast_matchers::match;
+using clang::ast_matchers::selectFirst;
+using clang::ast_matchers::stringLiteral;
+using clang::ast_matchers::varDecl;
+using clang::tooling::buildASTFromCode;
+using clang::tooling::buildASTFromCodeWithArgs;
+
TEST(Attr, Doc) {
EXPECT_THAT(Attr::getDocumentation(attr::Used).str(),
testing::HasSubstr("The compiler must emit the definition even "
"if it appears to be unused"));
}
+const FunctionDecl *getFunctionNode(ASTUnit *AST, const std::string &Name) {
+ auto Result =
+ match(functionDecl(hasName(Name)).bind("fn"), AST->getASTContext());
+ EXPECT_EQ(Result.size(), 1u);
+ return Result[0].getNodeAs<FunctionDecl>("fn");
+}
+
+const VarDecl *getVariableNode(ASTUnit *AST, const std::string &Name) {
+ auto Result = match(varDecl(hasName(Name)).bind("var"), AST->getASTContext());
+ EXPECT_EQ(Result.size(), 1u);
+ return Result[0].getNodeAs<VarDecl>("var");
+}
+
+template <class ModifiedTypeLoc>
+void AssertAnnotatedAs(TypeLoc TL, llvm::StringRef annotation,
+ ModifiedTypeLoc &ModifiedTL,
+ const AnnotateTypeAttr **AnnotateOut = nullptr) {
+ const auto AttributedTL = TL.getAs<AttributedTypeLoc>();
+ ASSERT_FALSE(AttributedTL.isNull());
+ ModifiedTL = AttributedTL.getModifiedLoc().getAs<ModifiedTypeLoc>();
+ ASSERT_TRUE(ModifiedTL);
+
+ ASSERT_NE(AttributedTL.getAttr(), nullptr);
+ const auto *Annotate = dyn_cast<AnnotateTypeAttr>(AttributedTL.getAttr());
+ ASSERT_NE(Annotate, nullptr);
+ EXPECT_EQ(Annotate->getAnnotation(), annotation);
+ if (AnnotateOut) {
+ *AnnotateOut = Annotate;
+ }
+}
+
+TEST(Attr, AnnotateType) {
+
+ // Test that the AnnotateType attribute shows up in the right places and that
+ // it stores its arguments correctly.
+
+ auto AST = buildASTFromCode(R"cpp(
+ void f(int* [[clang::annotate_type("foo", "arg1", 2)]] *,
+ int [[clang::annotate_type("bar")]]);
+
+ int [[clang::annotate_type("int")]] * [[clang::annotate_type("ptr")]]
+ array[10] [[clang::annotate_type("arr")]];
+
+ void (* [[clang::annotate_type("funcptr")]] fp)(void);
+
+ struct S { int mem; };
+ int [[clang::annotate_type("int")]]
+ S::* [[clang::annotate_type("ptr_to_mem")]] ptr_to_member = &S::mem;
+)cpp");
+
+ {
+ const FunctionDecl *Func = getFunctionNode(AST.get(), "f");
+
+ // First parameter.
+ const auto PointerTL = Func->getParamDecl(0)
+ ->getTypeSourceInfo()
+ ->getTypeLoc()
+ .getAs<PointerTypeLoc>();
+ ASSERT_FALSE(PointerTL.isNull());
+ PointerTypeLoc PointerPointerTL;
+ const AnnotateTypeAttr *Annotate;
+ AssertAnnotatedAs(PointerTL.getPointeeLoc(), "foo", PointerPointerTL,
+ &Annotate);
+
+ EXPECT_EQ(Annotate->args_size(), 2);
+ const auto *StringLit = selectFirst<StringLiteral>(
+ "str", match(constantExpr(hasDescendant(stringLiteral().bind("str"))),
+ *Annotate->args_begin()[0], AST->getASTContext()));
+ ASSERT_NE(StringLit, nullptr);
+ EXPECT_EQ(StringLit->getString(), "arg1");
+ EXPECT_EQ(match(constantExpr(has(integerLiteral(equals(2)).bind("int"))),
+ *Annotate->args_begin()[1], AST->getASTContext())
+ .size(),
+ 1);
+
+ // Second parameter.
+ BuiltinTypeLoc IntTL;
+ AssertAnnotatedAs(Func->getParamDecl(1)->getTypeSourceInfo()->getTypeLoc(),
+ "bar", IntTL);
+ EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy);
+ }
+
+ {
+ const VarDecl *Var = getVariableNode(AST.get(), "array");
+
+ ArrayTypeLoc ArrayTL;
+ AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "arr", ArrayTL);
+ PointerTypeLoc PointerTL;
+ AssertAnnotatedAs(ArrayTL.getElementLoc(), "ptr", PointerTL);
+ BuiltinTypeLoc IntTL;
+ AssertAnnotatedAs(PointerTL.getPointeeLoc(), "int", IntTL);
+ EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy);
+ }
+
+ {
+ const VarDecl *Var = getVariableNode(AST.get(), "fp");
+
+ PointerTypeLoc PointerTL;
+ AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "funcptr",
+ PointerTL);
+ ASSERT_TRUE(
+ PointerTL.getPointeeLoc().IgnoreParens().getAs<FunctionTypeLoc>());
+ }
+
+ {
+ const VarDecl *Var = getVariableNode(AST.get(), "ptr_to_member");
+
+ MemberPointerTypeLoc MemberPointerTL;
+ AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "ptr_to_mem",
+ MemberPointerTL);
+ BuiltinTypeLoc IntTL;
+ AssertAnnotatedAs(MemberPointerTL.getPointeeLoc(), "int", IntTL);
+ EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy);
+ }
+
+ // Test type annotation on an `__auto_type` type in C mode.
+ AST = buildASTFromCodeWithArgs(R"c(
+ __auto_type [[clang::annotate_type("auto")]] auto_var = 1;
+)c",
+ {"-fdouble-square-bracket-attributes"},
+ "input.c");
+
+ {
+ const VarDecl *Var = getVariableNode(AST.get(), "auto_var");
+
+ AutoTypeLoc AutoTL;
+ AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "auto", AutoTL);
+ }
+}
+
} // namespace
Index: clang/test/SemaCXX/annotate-type.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/annotate-type.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 %s -std=c++17 -fsyntax-only -verify
+
+struct S1 {
+ void f() [[clang::annotate_type("foo")]];
+ // FIXME: We would want to prohibit the attribute in the following location.
+ // However, Clang currently generally doesn't prohibit type-only C++11
+ // attributes on declarations. This should be fixed more generally.
+ [[clang::annotate_type("foo")]] void g();
+};
+
+template <typename T1, typename T2> struct is_same {
+ static constexpr bool value = false;
+};
+
+template <typename T1> struct is_same<T1, T1> {
+ static constexpr bool value = true;
+};
+
+static_assert(is_same<int, int [[clang::annotate_type("foo")]]>::value);
+static_assert(is_same<int [[clang::annotate_type("foo")]],
+ int [[clang::annotate_type("bar")]]>::value);
+static_assert(is_same<int *, int *[[clang::annotate_type("foo")]]>::value);
+
+// Cannot overload on types that only differ by `annotate_type` attribute.
+void f(int) {} // expected-note {{previous definition is here}}
+void f(int [[clang::annotate_type("foo")]]) {
+} // expected-error {{redefinition of 'f'}}
+
+// Cannot specialize on types that only differ by `annotate_type` attribute.
+template <class T> struct S2 {};
+
+template <> struct S2<int> {}; // expected-note {{previous definition is here}}
+
+template <>
+struct S2<int [[clang::annotate_type("foo")]]> {
+}; // expected-error {{redefinition of 'S2<int>'}}
+
+// Test that the attribute supports parameter pack expansion.
+template <int... Is> void variadic_func_template() {
+ int [[clang::annotate_type("foo", Is...)]] val;
+}
+int f2() { variadic_func_template<1, 2, 3>(); }
+
+// Make sure we correctly diagnose wrong number of arguments for
+// [[clang::annotate_type]] inside a template argument.
+template <typename Ty> void func_template();
+void f3() {
+ func_template<int
+ [[clang::annotate_type()]]>(); // expected-error
+ // {{'annotate_type' attribute
+ // takes at least 1 argument}}
+}
+
+// More error cases: Prohibit adding the attribute to declarations.
+// Different declarations hit different code paths, so they need separate tests.
+// FIXME: Clang currently generally doesn't prohibit type-only C++11
+// attributes on declarations.
+namespace [[clang::annotate_type("foo")]] my_namespace {}
+struct [[clang::annotate_type("foo")]] S3{};
+void f4() {
+ for ([[clang::annotate_type("foo")]] int i = 0; i < 42; ++i) {
+ }
+}
Index: clang/test/Sema/annotate-type.c
===================================================================
--- /dev/null
+++ clang/test/Sema/annotate-type.c
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 %s -fsyntax-only -fdouble-square-bracket-attributes -verify
+
+const char *some_function();
+
+void foo(float *[[clang::annotate_type("foo")]] a) {
+ int [[clang::annotate_type("bar")]] x1;
+ int *[[clang::annotate_type("bar")]] x2;
+ int *[[clang::annotate_type("bar", 1)]] x3;
+ int *[[clang::annotate_type("bar", 1 + 2)]] x4;
+ struct {} [[clang::annotate_type("foo")]] x5;
+ int [[clang::annotate_type("int")]] *[[clang::annotate_type("ptr")]] array[10] [[clang::annotate_type("arr")]];
+
+ typedef int [[clang::annotate_type("bar")]] my_typedef;
+
+ // GNU spelling is not supported
+ int __attribute__((annotate_type("bar"))) y1; // expected-warning {{unknown attribute 'annotate_type' ignored}}
+ int *__attribute__((annotate_type("bar"))) y2; // expected-warning {{unknown attribute 'annotate_type' ignored}}
+
+ // Various error cases
+ // FIXME: We would want to prohibit the attribute in the following location.
+ // However, Clang currently generally doesn't prohibit type-only C++11
+ // attributes on declarations. This should be fixed more generally.
+ [[clang::annotate_type("bar")]] int *z1;
+ int *z2 [[clang::annotate_type("bar")]]; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}}
+ [[clang::annotate_type("bar")]]; // expected-error {{'annotate_type' attribute cannot be applied to a statement}}
+ int *[[clang::annotate_type(1)]] z3; // expected-error {{'annotate_type' attribute requires a string}}
+ int *[[clang::annotate_type()]] z4; // expected-error {{'annotate_type' attribute takes at least 1 argument}}
+ int *[[clang::annotate_type]] z5; // expected-error {{'annotate_type' attribute takes at least 1 argument}}
+ int *[[clang::annotate_type(some_function())]] z6; // expected-error {{'annotate_type' attribute requires a string}}
+ int *[[clang::annotate_type("bar", some_function())]] z7; // expected-error {{'annotate_type' attribute requires parameter 1 to be a constant expression}} expected-note{{subexpression not valid in a constant expression}}
+ int *[[clang::annotate_type("bar", z7)]] z8; // expected-error {{'annotate_type' attribute requires parameter 1 to be a constant expression}} expected-note{{subexpression not valid in a constant expression}}
+ int *[[clang::annotate_type("bar", int)]] z9; // expected-error {{expected expression}}
+}
+// More error cases: Prohibit adding the attribute to declarations.
+// Different declarations hit different code paths, so they need separate tests.
+// FIXME: Clang currently generally doesn't prohibit type-only C++11
+// attributes on declarations.
+[[clang::annotate_type("bar")]] int *global;
+void g([[clang::annotate_type("bar")]] int);
Index: clang/test/CodeGenCXX/annotate-type.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/annotate-type.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm-only %s -emit-llvm -o
+// - | FileCheck %s
+
+// Test that `annotate_type` does not affect mangled names.
+
+int *[[clang::annotate_type("foo")]] f(int *[[clang::annotate_type("foo")]],
+ int [[clang::annotate_type("foo")]]) {
+ return nullptr;
+}
+// CHECK: @_Z1fPii
+
+template <class T> struct S {};
+
+S<int *[[clang::annotate_type("foo")]]>
+g(S<int *[[clang::annotate_type("foo")]]>) {
+ return {};
+}
+// CHECK: @_Z1g1SIPiE
Index: clang/test/AST/attr-annotate-type.c
===================================================================
--- /dev/null
+++ clang/test/AST/attr-annotate-type.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 %s -ast-dump -fdouble-square-bracket-attributes | FileCheck %s
+
+// Verify that we print the [[clang::annotate_type]] attribute.
+// FIXME: The arguments are currently not printed -- see also comments in
+// TypePrinter.cpp.
+
+// Need to escape the `[[` as a regex to avoid it being interpreted as a
+// substitution block.
+// CHECK: VarDecl {{.*}} x1 'int {{\[\[}}clang::annotate_type{{]]}}':'int'
+int [[clang::annotate_type("bar")]] x1;
+// CHECK: VarDecl {{.*}} x2 'int * {{\[\[}}clang::annotate_type{{]]}}':'int *'
+int *[[clang::annotate_type("bar")]] x2;
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -8146,6 +8146,34 @@
CurType = T;
}
+static void HandleAnnotateTypeAttr(TypeProcessingState &State,
+ QualType &CurType, const ParsedAttr &PA) {
+ Sema &S = State.getSema();
+
+ if (PA.getNumArgs() < 1) {
+ S.Diag(PA.getLoc(), diag::err_attribute_too_few_arguments) << PA << 1;
+ return;
+ }
+
+ // Make sure that there is a string literal as the annotation's first
+ // argument.
+ StringRef Str;
+ if (!S.checkStringLiteralArgumentAttr(PA, 0, Str))
+ return;
+
+ llvm::SmallVector<Expr *, 4> Args;
+ Args.reserve(PA.getNumArgs() - 1);
+ for (unsigned Idx = 1; Idx < PA.getNumArgs(); Idx++) {
+ assert(!PA.isArgIdent(Idx));
+ Args.push_back(PA.getArgAsExpr(Idx));
+ }
+ if (!S.ConstantFoldAttrArgs(PA, Args))
+ return;
+ auto *AnnotateTypeAttr =
+ AnnotateTypeAttr::Create(S.Context, Str, Args.data(), Args.size(), PA);
+ CurType = State.getAttributedType(AnnotateTypeAttr, CurType, CurType);
+}
+
static void HandleLifetimeBoundAttr(TypeProcessingState &State,
QualType &CurType,
ParsedAttr &Attr) {
@@ -8208,10 +8236,11 @@
if (!IsTypeAttr)
continue;
}
- } else if (TAL != TAL_DeclChunk && !isAddressSpaceKind(attr)) {
+ } else if (TAL != TAL_DeclChunk && !isAddressSpaceKind(attr) &&
+ attr.getKind() != ParsedAttr::AT_AnnotateType) {
// Otherwise, only consider type processing for a C++11 attribute if
// it's actually been applied to a type.
- // We also allow C++11 address_space and
+ // We also allow C++11 address_space and annotate_type and
// OpenCL language address space attributes to pass through.
continue;
}
@@ -8409,6 +8438,18 @@
attr.setUsedAsTypeAttr();
break;
}
+ case ParsedAttr::AT_AnnotateType: {
+ if (TAL == TAL_DeclName) {
+ state.getSema().Diag(attr.getLoc(),
+ diag::err_type_attribute_invalid_on_decl)
+ << attr;
+ return;
+ }
+
+ HandleAnnotateTypeAttr(state, type, attr);
+ attr.setUsedAsTypeAttr();
+ break;
+ }
}
// Handle attributes that are defined in a macro. We do not want this to be
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -4155,48 +4155,10 @@
void Sema::AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Str, MutableArrayRef<Expr *> Args) {
auto *Attr = AnnotateAttr::Create(Context, Str, Args.data(), Args.size(), CI);
- llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
- for (unsigned Idx = 0; Idx < Attr->args_size(); Idx++) {
- Expr *&E = Attr->args_begin()[Idx];
- assert(E && "error are handled before");
- if (E->isValueDependent() || E->isTypeDependent())
- continue;
-
- if (E->getType()->isArrayType())
- E = ImpCastExprToType(E, Context.getPointerType(E->getType()),
- clang::CK_ArrayToPointerDecay)
- .get();
- if (E->getType()->isFunctionType())
- E = ImplicitCastExpr::Create(Context,
- Context.getPointerType(E->getType()),
- clang::CK_FunctionToPointerDecay, E, nullptr,
- VK_PRValue, FPOptionsOverride());
- if (E->isLValue())
- E = ImplicitCastExpr::Create(Context, E->getType().getNonReferenceType(),
- clang::CK_LValueToRValue, E, nullptr,
- VK_PRValue, FPOptionsOverride());
-
- Expr::EvalResult Eval;
- Notes.clear();
- Eval.Diag = &Notes;
-
- bool Result =
- E->EvaluateAsConstantExpr(Eval, Context);
-
- /// Result means the expression can be folded to a constant.
- /// Note.empty() means the expression is a valid constant expression in the
- /// current language mode.
- if (!Result || !Notes.empty()) {
- Diag(E->getBeginLoc(), diag::err_attribute_argument_n_type)
- << CI << (Idx + 1) << AANT_ArgumentConstantExpr;
- for (auto &Note : Notes)
- Diag(Note.first, Note.second);
- return;
- }
- assert(Eval.Val.hasValue());
- E = ConstantExpr::Create(Context, E, Eval.Val);
- }
- D->addAttr(Attr);
+ if (ConstantFoldAttrArgs(
+ CI, MutableArrayRef<Expr *>(Attr->args_begin(), Attr->args_end()))) {
+ D->addAttr(Attr);
+ }
}
static void handleAnnotateAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
Index: clang/lib/Sema/SemaAttr.cpp
===================================================================
--- clang/lib/Sema/SemaAttr.cpp
+++ clang/lib/Sema/SemaAttr.cpp
@@ -384,6 +384,54 @@
AlignPackStack.Act(PragmaLoc, Action, SlotLabel, Info);
}
+bool Sema::ConstantFoldAttrArgs(const AttributeCommonInfo &CI,
+ MutableArrayRef<Expr *> Args) {
+ llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
+ for (unsigned Idx = 0; Idx < Args.size(); Idx++) {
+ Expr *&E = Args.begin()[Idx];
+ assert(E && "error are handled before");
+ if (E->isValueDependent() || E->isTypeDependent())
+ continue;
+
+ // FIXME: Use DefaultFunctionArrayLValueConversion() in place of the logic
+ // that adds implicit casts here.
+ if (E->getType()->isArrayType())
+ E = ImpCastExprToType(E, Context.getPointerType(E->getType()),
+ clang::CK_ArrayToPointerDecay)
+ .get();
+ if (E->getType()->isFunctionType())
+ E = ImplicitCastExpr::Create(Context,
+ Context.getPointerType(E->getType()),
+ clang::CK_FunctionToPointerDecay, E, nullptr,
+ VK_PRValue, FPOptionsOverride());
+ if (E->isLValue())
+ E = ImplicitCastExpr::Create(Context, E->getType().getNonReferenceType(),
+ clang::CK_LValueToRValue, E, nullptr,
+ VK_PRValue, FPOptionsOverride());
+
+ Expr::EvalResult Eval;
+ Notes.clear();
+ Eval.Diag = &Notes;
+
+ bool Result = E->EvaluateAsConstantExpr(Eval, Context);
+
+ /// Result means the expression can be folded to a constant.
+ /// Note.empty() means the expression is a valid constant expression in the
+ /// current language mode.
+ if (!Result || !Notes.empty()) {
+ Diag(E->getBeginLoc(), diag::err_attribute_argument_n_type)
+ << CI << (Idx + 1) << AANT_ArgumentConstantExpr;
+ for (auto &Note : Notes)
+ Diag(Note.first, Note.second);
+ return false;
+ }
+ assert(Eval.Val.hasValue());
+ E = ConstantExpr::Create(Context, E, Eval.Val);
+ }
+
+ return true;
+}
+
void Sema::DiagnoseNonDefaultPragmaAlignPack(PragmaAlignPackDiagnoseKind Kind,
SourceLocation IncludeLoc) {
if (Kind == PragmaAlignPackDiagnoseKind::NonDefaultStateAtInclude) {
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -3164,10 +3164,24 @@
if (!AttrsLastTime)
ProhibitAttributes(attrs);
else {
- // Reject C++11 attributes that appertain to decl specifiers as
- // we don't support any C++11 attributes that appertain to decl
- // specifiers. This also conforms to what g++ 4.8 is doing.
- ProhibitCXX11Attributes(attrs, diag::err_attribute_not_type_attr);
+ // Reject C++11 / C2x attributes that aren't type attributes.
+ for (const ParsedAttr &PA : attrs) {
+ if (!PA.isCXX11Attribute() && !PA.isC2xAttribute())
+ continue;
+ // We reject AT_LifetimeBound and AT_AnyX86NoCfCheck, even though they
+ // are type attributes, because we historically haven't allowed these
+ // to be used as type attributes in C++11 / C2x syntax.
+ if (PA.isTypeAttr() && PA.getKind() != ParsedAttr::AT_LifetimeBound &&
+ PA.getKind() != ParsedAttr::AT_AnyX86NoCfCheck)
+ continue;
+ if (PA.getKind() == ParsedAttr::UnknownAttribute)
+ Diag(PA.getLoc(), diag::warn_unknown_attribute_ignored)
+ << PA << PA.getRange();
+ else {
+ Diag(PA.getLoc(), diag::err_attribute_not_type_attr) << PA;
+ PA.setInvalid();
+ }
+ }
DS.takeAttributesFrom(attrs);
}
Index: clang/lib/AST/TypePrinter.cpp
===================================================================
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1681,6 +1681,15 @@
if (T->getAttrKind() == attr::AddressSpace)
return;
+ if (T->getAttrKind() == attr::AnnotateType) {
+ // FIXME: Print the attribute arguments once we have a way to retrieve these
+ // here. For the meantime, we just print `[[clang::annotate_type]]` without
+ // the arguments so that we know at least that we had _some_ annotation on
+ // the type.
+ OS << " [[clang::annotate_type]]";
+ return;
+ }
+
OS << " __attribute__((";
switch (T->getAttrKind()) {
#define TYPE_ATTR(NAME)
@@ -1718,6 +1727,7 @@
case attr::UPtr:
case attr::AddressSpace:
case attr::CmseNSCall:
+ case attr::AnnotateType:
llvm_unreachable("This attribute should have been handled already");
case attr::NSReturnsRetained:
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -10354,6 +10354,13 @@
void AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Annot, MutableArrayRef<Expr *> Args);
+ /// ConstantFoldAttrArgs - Folds attribute arguments into ConstantExprs
+ /// (unless they are value dependent or type dependent). Returns false
+ /// and emits a diagnostic if one or more of the arguments could not be
+ /// folded into a constant.
+ bool ConstantFoldAttrArgs(const AttributeCommonInfo &CI,
+ MutableArrayRef<Expr *> Args);
+
/// AddLaunchBoundsAttr - Adds a launch_bounds attribute to a particular
/// declaration.
void AddLaunchBoundsAttr(Decl *D, const AttributeCommonInfo &CI,
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3383,6 +3383,8 @@
"%0 attribute cannot be applied to a statement">;
def err_stmt_attribute_invalid_on_decl : Error<
"%0 attribute cannot be applied to a declaration">;
+def err_type_attribute_invalid_on_decl : Error<
+ "%0 attribute cannot be applied to a declaration">;
def warn_declspec_attribute_ignored : Warning<
"attribute %0 is ignored, place it after "
"\"%select{class|struct|interface|union|enum}1\" to apply attribute to "
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -6443,3 +6443,37 @@
The full documentation is available here: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sv-groupindex
}];
}
+
+def AnnotateTypeDocs : Documentation {
+ let Category = DocCatType;
+ let Heading = "annotate_type";
+ let Content = [{
+This attribute is used to add annotations to types, typically for use by static
+analysis tools that are not integrated into the core Clang compiler (e.g.,
+Clang-Tidy checks or out-of-tree Clang-based tools). It is a counterpart to the
+`annotate` attribute, which serves the same purpose, but for declarations.
+
+The attribute takes a mandatory string literal argument specifying the
+annotation category and an arbitrary number of optional arguments that provide
+additional information specific to the annotation category. The optional
+arguments must be constant expressions of arbitrary type.
+
+For example:
+
+.. code-block:: c++
+
+ int* [[clang::annotate("category1", "foo", 1)]] f(int[[clang::annotate("category2")]] *);
+
+The attribute does not have any effect on the semantics of C++ code, neither
+type checking rules, nor runtime semantics. In particular:
+
+- ``std::is_same<T, T [[clang::annotate_type("foo")]]`` is true for all types
+ ``T``.
+
+- It is not permissible for overloaded functions or template specializations
+ to differ merely by an ``annotate_type`` attribute.
+
+- The presence of an ``annotate_type`` attribute will not affect name
+ mangling.
+ }];
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -804,6 +804,14 @@
let Documentation = [Undocumented];
}
+def AnnotateType : TypeAttr {
+ let Spellings = [CXX11<"clang", "annotate_type">, C2x<"clang", "annotate_type">];
+ let Args = [StringArgument<"Annotation">, VariadicExprArgument<"Args">];
+ let HasCustomParsing = 1;
+ let AcceptsExprPack = 1;
+ let Documentation = [AnnotateTypeDocs];
+}
+
def ARMInterrupt : InheritableAttr, TargetSpecificAttr<TargetARM> {
// NOTE: If you add any additional spellings, M68kInterrupt's,
// MSP430Interrupt's, MipsInterrupt's and AnyX86Interrupt's spellings
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -225,6 +225,9 @@
- The ``__declspec(naked)`` attribute can no longer be written on a member
function in Microsoft compatibility mode, matching the behavior of cl.exe.
+- Added the `clang::annotate_type` attribute, which can be used to add
+ annotations to types (see documentation for details).
+
Windows Support
---------------
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits