https://github.com/zebullax updated 
https://github.com/llvm/llvm-project/pull/166287

>From e2f13c0d8c6e5c6bed1fe445d16eed609e5f03e2 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Sat, 1 Nov 2025 11:11:25 +0900
Subject: [PATCH 01/16] Add annotation to attribute tablegen

Recognize annotation starting token in attr parsing

Signed-off-by: acassagnes <[email protected]>
---
 clang/include/clang/Basic/Attr.td             | 45 +++++++++++++++++++
 .../clang/Basic/DiagnosticParseKinds.td       |  5 +++
 clang/lib/Parse/ParseDeclCXX.cpp              | 11 +++++
 clang/utils/TableGen/ClangAttrEmitter.cpp     |  8 ++--
 4 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 749f531ec9ab1..06b9b1ff3e295 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -744,6 +744,9 @@ class Attr {
   // Note: Any additional data members will leak and should be constructed
   // externally on the ASTContext.
   code AdditionalMembers = [{}];
+    // Any additional text that should be included verbatim after instantiating
+  // an attribute on a template.
+  code PostInstantiationStmts =[{}];
   // Any documentation that should be associated with the attribute. Since an
   // attribute may be documented under multiple categories, more than one
   // Documentation entry may be listed.
@@ -930,6 +933,48 @@ def AlwaysInline : DeclOrStmtAttr {
   let Documentation = [AlwaysInlineDocs];
 }
 
+def CXX26Annotation : InheritableParamAttr {
+  let Spellings = [];
+  let Args = [ExprArgument<"Arg">];
+  let AdditionalMembers = [{
+private:
+  APValue Value;
+  SourceLocation EqLoc;
+
+public:
+  static CXX26AnnotationAttr *Create(ASTContext &Ctx, \
+                                     const AttributeCommonInfo &CommonInfo) {
+    return CXX26AnnotationAttr::Create(Ctx, nullptr,  CommonInfo);
+  }
+  static CXX26AnnotationAttr *CreateImplicit( \
+          ASTContext &Ctx, \
+          const AttributeCommonInfo &CommonInfo) {
+    return CXX26AnnotationAttr::CreateImplicit(Ctx, nullptr, CommonInfo);
+  }
+
+  APValue getValue() const { return Value; }
+  void setValue(APValue V) { Value = V; }
+
+  SourceLocation getEqLoc() const { return EqLoc; }
+  void setEqLoc(SourceLocation Loc) { EqLoc = Loc; }
+  }];
+
+  let PostInstantiationStmts = [{
+    Expr::EvalResult V;
+    if (!Result->getArg()->isValueDependent() &&
+        !Result->getArg()->EvaluateAsRValue(V, C, true))
+      llvm_unreachable("failed to evaluate annotation expression");
+
+    Result->setValue(V.Val);
+    Result->setEqLoc(A->getEqLoc());
+  }];
+
+  let HasCustomParsing = 1;
+  let TemplateDependent = 1;
+  let MeaningfulToClassTemplateDefinition = 1;
+  let Documentation = [InternalOnly];
+}
+
 def Artificial : InheritableAttr {
   let Spellings = [GCC<"artificial">];
   let Subjects = SubjectList<[InlineFunction]>;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index e5e071f43fa75..c7e59ad21b3bb 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1848,6 +1848,11 @@ def err_placeholder_expected_auto_or_decltype_auto : 
Error<
   "expected 'auto' or 'decltype(auto)' after concept name">;
 }
 
+let CategoryName = "Reflection Issue" in {
+def err_annotation_with_using : Error<
+  "annotations are not permitted following an attribute-using-prefix">;
+}
+
 def warn_max_tokens : Warning<
   "the number of preprocessor source tokens (%0) exceeds this token limit 
(%1)">,
   InGroup<MaxTokens>, DefaultIgnore;
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index b96968d4592f5..4ca0568b37b12 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -17,6 +17,7 @@
 #include "clang/Basic/Attributes.h"
 #include "clang/Basic/CharInfo.h"
 #include "clang/Basic/DiagnosticParse.h"
+#include "clang/Basic/LangOptions.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Lex/LiteralSupport.h"
@@ -4700,6 +4701,16 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
     SourceLocation ScopeLoc, AttrLoc;
     IdentifierInfo *ScopeName = nullptr, *AttrName = nullptr;
 
+    // '=' token marks the beginning of an annotation
+    if (getLangOpts().CPlusPlus26 && Tok.is(tok::equal)) {
+      if (CommonScopeName) {
+        Diag(Tok.getLocation(), diag::err_annotation_with_using);
+        SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
+        continue;
+      }
+      continue;
+    }
+
     AttrName = TryParseCXX11AttributeIdentifier(
         AttrLoc, SemaCodeCompletion::AttributeCompletion::Attribute,
         CommonScopeName);
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 183952af590e1..7bc4719289441 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -4091,13 +4091,15 @@ EmitClangAttrTemplateInstantiateHelper(ArrayRef<const 
Record *> Attrs,
     for (auto const &ai : Args)
       ai->writeTemplateInstantiation(OS);
 
-    OS << "      return new (C) " << R.getName() << "Attr(C, *A";
+    OS << "      auto* Result = new (C) " << R.getName() << "Attr(C, *A";
     for (auto const &ai : Args) {
       OS << ", ";
       ai->writeTemplateInstantiationArgs(OS);
     }
-    OS << ");\n"
-       << "    }\n";
+    OS << ");\n";
+    OS << R.getValueAsString("PostInstantiationStmts");
+    OS << "return Result;\n";
+    OS << "    }\n";
   }
   OS << "  } // end switch\n"
      << "  llvm_unreachable(\"Unknown attribute!\");\n"

>From 76337f8ae60947346fe4fe2f2569f5affe8f0722 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Sun, 2 Nov 2025 10:52:44 +0900
Subject: [PATCH 02/16] Build logic around parsing of annotation inside list

Signed-off-by: acassagnes <[email protected]>
---
 .../clang/Basic/DiagnosticParseKinds.td       |  4 ++
 clang/include/clang/Parse/Parser.h            |  4 ++
 clang/lib/Parse/ParseDeclCXX.cpp              | 40 ++++++++++++++-----
 3 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index c7e59ad21b3bb..e87b605632d19 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -795,6 +795,10 @@ def err_cxx11_attribute_forbids_ellipsis : Error<
 def warn_cxx14_compat_using_attribute_ns : Warning<
   "default scope specifier for attributes is incompatible with C++ standards "
   "before C++17">, InGroup<CXXPre17Compat>, DefaultIgnore;
+def warn_cxx26_compat_annotation : Warning<
+  "annotation is a C++26 extension">, InGroup<CXXPre26Compat>, DefaultIgnore;
+def err_mixed_attributes_and_annotations : Error<
+  "attribute specifier cannot contain both attributes and annotations">;
 def ext_using_attribute_ns : ExtWarn<
   "default scope specifier for attributes is a C++17 extension">,
   InGroup<CXX17>;
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index dad8efd0f017f..dd1e774316ca0 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3046,6 +3046,10 @@ class Parser : public CodeCompletionHandler {
                                SourceLocation ScopeLoc,
                                CachedTokens &OpenMPTokens);
 
+  /// TODO doc
+  void ParseAnnotationSpecifier(ParsedAttributes &Attrs,
+                                SourceLocation *EndLoc);
+
   /// Parse the argument to C++23's [[assume()]] attribute. Returns true on
   /// error.
   bool
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 4ca0568b37b12..4a82a365bb0b8 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4443,6 +4443,12 @@ static bool 
IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
   }
 }
 
+void Parser::ParseAnnotationSpecifier(ParsedAttributes &Attrs,
+                                      SourceLocation *EndLoc)
+{
+  // TODO
+}
+
 bool Parser::ParseCXXAssumeAttributeArg(
     ParsedAttributes &Attrs, IdentifierInfo *AttrName,
     SourceLocation AttrNameLoc, IdentifierInfo *ScopeName,
@@ -4682,16 +4688,21 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
       Diag(Tok.getLocation(), diag::err_expected) << tok::colon;
   }
 
-  bool AttrParsed = false;
+  bool hasAttribute = false;
+  bool hasAnnotation = false;
   while (!Tok.isOneOf(tok::r_square, tok::semi, tok::eof)) {
-    if (AttrParsed) {
-      // If we parsed an attribute, a comma is required before parsing any
-      // additional attributes.
+    // If we parsed an attribute/annotation, a comma is required before parsing
+    //  any additional ones.
+    if (hasAttribute || hasAnnotation) {
       if (ExpectAndConsume(tok::comma)) {
         SkipUntil(tok::r_square, StopAtSemi | StopBeforeMatch);
         continue;
       }
-      AttrParsed = false;
+      if (hasAttribute && hasAnnotation) {
+        Diag(Tok.getLocation(), diag::err_mixed_attributes_and_annotations);
+        SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
+        continue;
+      }
     }
 
     // Eat all remaining superfluous commas before parsing the next attribute.
@@ -4702,12 +4713,19 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
     IdentifierInfo *ScopeName = nullptr, *AttrName = nullptr;
 
     // '=' token marks the beginning of an annotation
-    if (getLangOpts().CPlusPlus26 && Tok.is(tok::equal)) {
+    if (Tok.is(tok::equal)) {
+      if (!getLangOpts().CPlusPlus26) {
+        Diag(Tok.getLocation(), diag::warn_cxx26_compat_annotation);
+        SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
+        continue;
+      }
       if (CommonScopeName) {
         Diag(Tok.getLocation(), diag::err_annotation_with_using);
         SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
         continue;
       }
+      ParseAnnotationSpecifier(Attrs, EndLoc);
+      hasAnnotation = true;
       continue;
     }
 
@@ -4744,11 +4762,11 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
     }
 
     // Parse attribute arguments
-    if (Tok.is(tok::l_paren))
-      AttrParsed = ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
-                                           ScopeName, ScopeLoc, OpenMPTokens);
+    hasAttribute = Tok.is(tok::l_paren)
+      && ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
+                                 ScopeName, ScopeLoc, OpenMPTokens);
 
-    if (!AttrParsed) {
+    if (!hasAttribute) {
       Attrs.addNew(AttrName,
                    SourceRange(ScopeLoc.isValid() && CommonScopeLoc.isInvalid()
                                    ? ScopeLoc
@@ -4758,7 +4776,7 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
                    nullptr, 0,
                    getLangOpts().CPlusPlus ? ParsedAttr::Form::CXX11()
                                            : ParsedAttr::Form::C23());
-      AttrParsed = true;
+      hasAttribute = true;
     }
 
     if (TryConsumeToken(tok::ellipsis))

>From cc405d31f4a9c0eb3d931b7d92729c98b7170889 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Tue, 4 Nov 2025 09:19:07 +0900
Subject: [PATCH 03/16] Add sema handler for annotation

Add parsing for annotation

Signed-off-by: acassagnes <[email protected]>
---
 .../include/clang/Basic/AttributeCommonInfo.h |  8 ++-
 clang/lib/Parse/ParseDeclCXX.cpp              | 28 +++++++++-
 clang/lib/Sema/SemaDeclAttr.cpp               | 56 +++++++++++++++++++
 3 files changed, 88 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h 
b/clang/include/clang/Basic/AttributeCommonInfo.h
index 77b5eb8a1a7cc..c404d840555fd 100644
--- a/clang/include/clang/Basic/AttributeCommonInfo.h
+++ b/clang/include/clang/Basic/AttributeCommonInfo.h
@@ -61,7 +61,10 @@ class AttributeCommonInfo {
 
     /// The attibute has no source code manifestation and is only created
     /// implicitly.
-    AS_Implicit
+    AS_Implicit,
+
+    /// The attribute is a C++26 annotation.
+    AS_Annotation,
   };
 
   enum Kind {
@@ -133,6 +136,7 @@ class AttributeCommonInfo {
     static Form ContextSensitiveKeyword() { return AS_ContextSensitiveKeyword; 
}
     static Form HLSLAnnotation() { return AS_HLSLAnnotation; }
     static Form Implicit() { return AS_Implicit; }
+    static Form Annotation() { return AS_Annotation; }
 
   private:
     constexpr Form(Syntax SyntaxUsed)
@@ -156,7 +160,7 @@ class AttributeCommonInfo {
         SpellingIndex(FormUsed.getSpellingIndex()),
         IsAlignas(FormUsed.isAlignas()),
         IsRegularKeywordAttribute(FormUsed.isRegularKeywordAttribute()) {
-    assert(SyntaxUsed >= AS_GNU && SyntaxUsed <= AS_Implicit &&
+    assert(SyntaxUsed >= AS_GNU && SyntaxUsed <= AS_Annotation &&
            "Invalid syntax!");
   }
 
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 4a82a365bb0b8..df33948790fd3 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4446,7 +4446,23 @@ static bool 
IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
 void Parser::ParseAnnotationSpecifier(ParsedAttributes &Attrs,
                                       SourceLocation *EndLoc)
 {
-  // TODO
+  assert(Tok.is(tok::equal) && "not an annotation");
+  SourceLocation EqLoc = ConsumeToken();
+
+  ExprResult AnnotExpr = ParseConstantExpression();
+  if (AnnotExpr.isInvalid() || AnnotExpr.get()->containsErrors())
+    return;
+
+  IdentifierTable &IT = Actions.PP.getIdentifierTable();
+  IdentifierInfo &Placeholder = IT.get("__annotation_placeholder");
+
+  ArgsVector ArgExprs;
+  ArgExprs.push_back(AnnotExpr.get());
+  Attrs.addNew(&Placeholder, EqLoc, {}, ArgExprs.data(), 1,
+               ParsedAttr::Form::Annotation());
+
+  if (EndLoc)
+    *EndLoc = AnnotExpr.get()->getEndLoc();
 }
 
 bool Parser::ParseCXXAssumeAttributeArg(
@@ -4712,7 +4728,10 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
     SourceLocation ScopeLoc, AttrLoc;
     IdentifierInfo *ScopeName = nullptr, *AttrName = nullptr;
 
-    // '=' token marks the beginning of an annotation
+    // A '=' token marks the beginning of an annotation
+    //  - We must not be in C++ < 26
+    //  - We must not have seen 'using X::'
+    //  - We must not mix with an attribute
     if (Tok.is(tok::equal)) {
       if (!getLangOpts().CPlusPlus26) {
         Diag(Tok.getLocation(), diag::warn_cxx26_compat_annotation);
@@ -4724,6 +4743,11 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
         SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
         continue;
       }
+      if (hasAttribute) {
+        Diag(Tok.getLocation(), diag::err_mixed_attributes_and_annotations);
+        SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
+        continue;
+      }
       ParseAnnotationSpecifier(Attrs, EndLoc);
       hasAnnotation = true;
       continue;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a9e7b44ac9d73..725a84fd85c3c 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6409,6 +6409,59 @@ static void handleRequiresCapabilityAttr(Sema &S, Decl 
*D,
   D->addAttr(RCA);
 }
 
+static void handleCxx26AnnotationAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  Expr *CE = AL.getArgAsExpr(0);
+  if (CE->isLValue()) {
+    if (CE->getType()->isRecordType()) {
+      InitializedEntity Entity =
+          InitializedEntity::InitializeTemporary(
+              CE->getType().getUnqualifiedType());
+      InitializationKind Kind =
+          InitializationKind::CreateCopy(CE->getExprLoc(), SourceLocation());
+      InitializationSequence Seq(S, Entity, Kind, CE);
+
+      ExprResult CopyResult = Seq.Perform(S, Entity, Kind, CE);
+      if (CopyResult.isInvalid())
+        return;
+
+      CE = CopyResult.get();
+    } else {
+      ExprResult RVExprResult = S.DefaultLvalueConversion(AL.getArgAsExpr(0));
+      assert(!RVExprResult.isInvalid() && RVExprResult.get());
+
+      CE = RVExprResult.get();
+    }
+  }
+
+  Expr::EvalResult Result;
+
+  SmallVector<PartialDiagnosticAt, 4> Notes;
+  Result.Diag = &Notes;
+
+  if (!CE->isValueDependent()) {
+    ConstantExprKind CEKind = (CE->getType()->isClassType() ?
+                               ConstantExprKind::ClassTemplateArgument :
+                               ConstantExprKind::NonClassTemplateArgument);
+
+    if (!CE->EvaluateAsConstantExpr(Result, S.Context, CEKind)) {
+      S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)
+          << "C++26 annotation" << 4 << CE->getSourceRange();
+      for (auto P : Notes)
+        S.Diag(P.first, P.second);
+
+      return;
+    } else if (!CE->getType()->isStructuralType()) {
+      S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)
+          << "C++26 annotation" << 5 << CE->getSourceRange();
+      return;
+    }
+  }
+  auto *Annot = CXX26AnnotationAttr::Create(S.Context, CE, AL);
+  Annot->setValue(Result.Val);
+  Annot->setEqLoc(AL.getLoc());
+  D->addAttr(Annot);
+}
+
 static void handleDeprecatedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   if (const auto *NSD = dyn_cast<NamespaceDecl>(D)) {
     if (NSD->isAnonymousNamespace()) {
@@ -7179,6 +7232,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 
const ParsedAttr &AL,
   case ParsedAttr::AT_Constructor:
       handleConstructorAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_CXX26Annotation:
+    handleCxx26AnnotationAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_Deprecated:
     handleDeprecatedAttr(S, D, AL);
     break;

>From 0a8e12564aae79285802f6d6598038c1f74de41c Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Tue, 4 Nov 2025 09:43:08 +0900
Subject: [PATCH 04/16] Fix the copy around of common info for attribute

Signed-off-by: acassagnes <[email protected]>
---
 clang/lib/Sema/ParsedAttr.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp
index 2b5ad33ad7b29..888a5d42a0ec6 100644
--- a/clang/lib/Sema/ParsedAttr.cpp
+++ b/clang/lib/Sema/ParsedAttr.cpp
@@ -107,6 +107,13 @@ const ParsedAttrInfo &ParsedAttrInfo::get(const 
AttributeCommonInfo &A) {
   if ((size_t)A.getParsedKind() < std::size(AttrInfoMap))
     return *AttrInfoMap[A.getParsedKind()];
 
+  // If this is an annotation then return an appropriate ParsedAttrInfo.
+  static const ParsedAttrInfo AnnotationAttrInfo(
+      AttributeCommonInfo::AT_CXX26Annotation, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 
{},
+      nullptr);
+  if (A.getSyntax() == AttributeCommonInfo::AS_Annotation)
+    return AnnotationAttrInfo;
+
   // If this is an ignored attribute then return an appropriate ParsedAttrInfo.
   static const ParsedAttrInfo IgnoredParsedAttrInfo(
       AttributeCommonInfo::IgnoredAttribute);

>From c579bd993143b0c281fd1e7b4e7e3cb028eb0f4a Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Tue, 4 Nov 2025 11:21:59 +0900
Subject: [PATCH 05/16] Fill the doc for ParseAnnotationSpecifier

Signed-off-by: acassagnes <[email protected]>
---
 clang/include/clang/Parse/Parser.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index dd1e774316ca0..52b909ffcae91 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3046,7 +3046,7 @@ class Parser : public CodeCompletionHandler {
                                SourceLocation ScopeLoc,
                                CachedTokens &OpenMPTokens);
 
-  /// TODO doc
+  /// Parse an annotation as specified from C++26
   void ParseAnnotationSpecifier(ParsedAttributes &Attrs,
                                 SourceLocation *EndLoc);
 

>From 7673caba149e586a84f0c8e371680a38ccc7cce8 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Tue, 4 Nov 2025 11:45:06 +0900
Subject: [PATCH 06/16] Apply formatter

Signed-off-by: acassagnes <[email protected]>
---
 clang/lib/Parse/ParseDeclCXX.cpp |  9 ++++-----
 clang/lib/Sema/SemaDeclAttr.cpp  | 12 ++++++------
 2 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index df33948790fd3..04eea962a0208 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4444,8 +4444,7 @@ static bool 
IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
 }
 
 void Parser::ParseAnnotationSpecifier(ParsedAttributes &Attrs,
-                                      SourceLocation *EndLoc)
-{
+                                      SourceLocation *EndLoc) {
   assert(Tok.is(tok::equal) && "not an annotation");
   SourceLocation EqLoc = ConsumeToken();
 
@@ -4786,9 +4785,9 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
     }
 
     // Parse attribute arguments
-    hasAttribute = Tok.is(tok::l_paren)
-      && ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
-                                 ScopeName, ScopeLoc, OpenMPTokens);
+    hasAttribute = Tok.is(tok::l_paren) &&
+                   ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
+                                           ScopeName, ScopeLoc, OpenMPTokens);
 
     if (!hasAttribute) {
       Attrs.addNew(AttrName,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 725a84fd85c3c..f72328facddfc 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6413,9 +6413,8 @@ static void handleCxx26AnnotationAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
   Expr *CE = AL.getArgAsExpr(0);
   if (CE->isLValue()) {
     if (CE->getType()->isRecordType()) {
-      InitializedEntity Entity =
-          InitializedEntity::InitializeTemporary(
-              CE->getType().getUnqualifiedType());
+      InitializedEntity Entity = InitializedEntity::InitializeTemporary(
+          CE->getType().getUnqualifiedType());
       InitializationKind Kind =
           InitializationKind::CreateCopy(CE->getExprLoc(), SourceLocation());
       InitializationSequence Seq(S, Entity, Kind, CE);
@@ -6439,9 +6438,10 @@ static void handleCxx26AnnotationAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
   Result.Diag = &Notes;
 
   if (!CE->isValueDependent()) {
-    ConstantExprKind CEKind = (CE->getType()->isClassType() ?
-                               ConstantExprKind::ClassTemplateArgument :
-                               ConstantExprKind::NonClassTemplateArgument);
+    ConstantExprKind CEKind =
+        (CE->getType()->isClassType()
+             ? ConstantExprKind::ClassTemplateArgument
+             : ConstantExprKind::NonClassTemplateArgument);
 
     if (!CE->EvaluateAsConstantExpr(Result, S.Context, CEKind)) {
       S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)

>From c28d0666990ffdc06286fa2515714fcb81e6cca8 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Tue, 4 Nov 2025 14:39:00 +0900
Subject: [PATCH 07/16] Address review

Signed-off-by: acassagnes <[email protected]>

Undo dumb edit

Signed-off-by: acassagnes <[email protected]>
---
 clang/lib/Parse/ParseDeclCXX.cpp | 18 +++++++++---------
 clang/lib/Sema/SemaDeclAttr.cpp  |  3 ++-
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 04eea962a0208..920b7b9429c4c 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4703,17 +4703,17 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
       Diag(Tok.getLocation(), diag::err_expected) << tok::colon;
   }
 
-  bool hasAttribute = false;
-  bool hasAnnotation = false;
+  bool HasAttribute = false;
+  bool HasAnnotation = false;
   while (!Tok.isOneOf(tok::r_square, tok::semi, tok::eof)) {
     // If we parsed an attribute/annotation, a comma is required before parsing
     //  any additional ones.
-    if (hasAttribute || hasAnnotation) {
+    if (HasAttribute || HasAnnotation) {
       if (ExpectAndConsume(tok::comma)) {
         SkipUntil(tok::r_square, StopAtSemi | StopBeforeMatch);
         continue;
       }
-      if (hasAttribute && hasAnnotation) {
+      if (HasAttribute && HasAnnotation) {
         Diag(Tok.getLocation(), diag::err_mixed_attributes_and_annotations);
         SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
         continue;
@@ -4742,13 +4742,13 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
         SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
         continue;
       }
-      if (hasAttribute) {
+      if (HasAttribute) {
         Diag(Tok.getLocation(), diag::err_mixed_attributes_and_annotations);
         SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
         continue;
       }
       ParseAnnotationSpecifier(Attrs, EndLoc);
-      hasAnnotation = true;
+      HasAnnotation = true;
       continue;
     }
 
@@ -4785,11 +4785,11 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
     }
 
     // Parse attribute arguments
-    hasAttribute = Tok.is(tok::l_paren) &&
+    HasAttribute = Tok.is(tok::l_paren) &&
                    ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
                                            ScopeName, ScopeLoc, OpenMPTokens);
 
-    if (!hasAttribute) {
+    if (!HasAttribute) {
       Attrs.addNew(AttrName,
                    SourceRange(ScopeLoc.isValid() && CommonScopeLoc.isInvalid()
                                    ? ScopeLoc
@@ -4799,7 +4799,7 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
                    nullptr, 0,
                    getLangOpts().CPlusPlus ? ParsedAttr::Form::CXX11()
                                            : ParsedAttr::Form::C23());
-      hasAttribute = true;
+      HasAttribute = true;
     }
 
     if (TryConsumeToken(tok::ellipsis))
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index f72328facddfc..2e293b15b97d0 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6450,7 +6450,8 @@ static void handleCxx26AnnotationAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
         S.Diag(P.first, P.second);
 
       return;
-    } else if (!CE->getType()->isStructuralType()) {
+    }
+    if (!CE->getType()->isStructuralType()) {
       S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)
           << "C++26 annotation" << 5 << CE->getSourceRange();
       return;

>From 4898a6387781065a1556091757cff9dfa76b1e03 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Tue, 4 Nov 2025 19:19:51 +0900
Subject: [PATCH 08/16] Verify we are not emitting hasAttribute for Annotation
 spelling

Signed-off-by: acassagnes <[email protected]>
---
 clang/utils/TableGen/ClangAttrEmitter.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 7bc4719289441..17e53ac944201 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -3860,6 +3860,11 @@ void EmitClangAttrHasAttrImpl(const RecordKeeper 
&Records, raw_ostream &OS) {
         Pragma.emplace_back(R, SI);
       else if (Variety == "HLSLAnnotation")
         HLSLAnnotation.emplace_back(R, SI);
+      else if (Variety == "AS_Annotation") {
+        // We should not be code gening anything with a C++26
+        // annotation syntax.
+        // TODO do we want to warn here ?
+      }
     }
   }
 
@@ -3904,6 +3909,10 @@ void EmitClangAttrHasAttrImpl(const RecordKeeper 
&Records, raw_ostream &OS) {
   OS << "  llvm_unreachable (\"hasAttribute not supported for "
         "AS_Implicit\");\n";
   OS << "  return 0;\n";
+  OS << "case AttributeCommonInfo::Syntax::AS_Annotation:\n";
+  OS << "  llvm_unreachable (\"hasAttribute not supported for "
+        "AS_Annotation\");\n";
+  OS << "  return 0;\n";
 
   OS << "}\n";
 }

>From 41511c0dc7ebf0877ea4b60d5d0ccca9c3fe1798 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Tue, 11 Nov 2025 12:00:33 +0900
Subject: [PATCH 09/16] Fix checks to allow annotation on declaration

Add smoke test

Signed-off-by: acassagnes <[email protected]>
---
 .../include/clang/Basic/AttributeCommonInfo.h |  2 ++
 clang/include/clang/Sema/DeclSpec.h           |  3 +-
 clang/lib/Sema/ParsedAttr.cpp                 |  2 +-
 clang/test/SemaCXX/cxx26-annotation.cpp       | 35 +++++++++++++++++++
 4 files changed, 40 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/SemaCXX/cxx26-annotation.cpp

diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h 
b/clang/include/clang/Basic/AttributeCommonInfo.h
index c404d840555fd..0baee12071e3c 100644
--- a/clang/include/clang/Basic/AttributeCommonInfo.h
+++ b/clang/include/clang/Basic/AttributeCommonInfo.h
@@ -230,6 +230,8 @@ class AttributeCommonInfo {
 
   bool isCXX11Attribute() const { return SyntaxUsed == AS_CXX11 || IsAlignas; }
 
+  bool isCXX26Annotation() const { return SyntaxUsed == AS_Annotation; }
+
   bool isC23Attribute() const { return SyntaxUsed == AS_C23; }
 
   bool isAlignas() const {
diff --git a/clang/include/clang/Sema/DeclSpec.h 
b/clang/include/clang/Sema/DeclSpec.h
index 43a48c92fc305..a5d1f155605e4 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -2008,7 +2008,8 @@ class Declarator {
     assert(llvm::all_of(DeclarationAttrs,
                         [](const ParsedAttr &AL) {
                           return (AL.isStandardAttributeSyntax() ||
-                                  AL.isRegularKeywordAttribute());
+                                  AL.isRegularKeywordAttribute() ||
+                                  AL.isCXX26Annotation());
                         }) &&
            "DeclarationAttrs may only contain [[]] and keyword attributes");
   }
diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp
index 888a5d42a0ec6..8f648d90e5742 100644
--- a/clang/lib/Sema/ParsedAttr.cpp
+++ b/clang/lib/Sema/ParsedAttr.cpp
@@ -222,7 +222,7 @@ bool ParsedAttr::slidesFromDeclToDeclSpecLegacyBehavior() 
const {
     // atributes.
     return false;
 
-  assert(isStandardAttributeSyntax() || isAlignas());
+  assert(isStandardAttributeSyntax() || isAlignas() || isCXX26Annotation());
 
   // We have historically allowed some type attributes with standard attribute
   // syntax to slide to the decl-specifier-seq, so we have to keep supporting
diff --git a/clang/test/SemaCXX/cxx26-annotation.cpp 
b/clang/test/SemaCXX/cxx26-annotation.cpp
new file mode 100644
index 0000000000000..333d420114666
--- /dev/null
+++ b/clang/test/SemaCXX/cxx26-annotation.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -std=c++26  -x c++ %s -verify
+
+struct F {
+  bool V;
+};
+
+// Type
+struct [[=1]] f1 {};
+struct [[=1, =F{true}]] f2 {};
+struct [[=1]] [[=2]] f3 {};
+
+// Declaration
+const [[=1]] F ff{};
+
+// Redeclaration
+[[=2, =3, =2]] void g();
+void g [[=4, =2]] ();
+
+// Error case
+struct [[nodiscard, =1]] f4 {};  // expected-error {{attribute specifier 
cannot contain both attributes and annotations}}
+struct [[=1, nodiscard, ]] f5 {};  // expected-error {{attribute specifier 
cannot contain both attributes and annotations}}
+
+struct G {
+  [[using CC: =1]] [[=2]] int f;  // expected-error {{annotations are not 
permitted following an attribute-using-prefix}}
+};
+
+template<class T>
+  [[=T::type()]] void h(T t); // expected-error {{type 'char' cannot be used 
prior to '::' because it has no members}}
+
+void h(int);
+
+void hh() {
+  h(0);
+  h('0'); // expected-note {{in instantiation of function template 
specialization 'h<char>' requested here}}
+}

>From dd45005c1ac6251d1f6ab8ad8c5d6ab9a6f7f4b4 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Fri, 14 Nov 2025 17:08:28 +0900
Subject: [PATCH 10/16] Fix logic on mixed attribute and annotation

Improve documentation

Emit error if annotation syntax found in tablegen

Signed-off-by: acassagnes <[email protected]>
---
 clang/include/clang/Basic/Attr.td             | 14 ++------
 .../clang/Basic/DiagnosticParseKinds.td       |  4 +--
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +-
 clang/lib/Parse/ParseDeclCXX.cpp              | 17 +++++-----
 clang/lib/Sema/ParsedAttr.cpp                 | 16 ++++++++--
 clang/lib/Sema/SemaDeclAttr.cpp               |  2 ++
 clang/test/SemaCXX/cxx26-annotation.cpp       | 32 +++++++++----------
 clang/utils/TableGen/ClangAttrEmitter.cpp     |  4 ++-
 8 files changed, 51 insertions(+), 41 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index fe838bbeec5a6..7003a75e3e82e 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -748,8 +748,9 @@ class Attr {
   // Note: Any additional data members will leak and should be constructed
   // externally on the ASTContext.
   code AdditionalMembers = [{}];
-    // Any additional text that should be included verbatim after instantiating
-  // an attribute on a template.
+  // Any additional text that should be included verbatim after instantiating
+  // an attribute on a template. This can be used if additional bookkeeping is
+  // required for the data members
   code PostInstantiationStmts =[{}];
   // Any documentation that should be associated with the attribute. Since an
   // attribute may be documented under multiple categories, more than one
@@ -946,15 +947,6 @@ private:
   SourceLocation EqLoc;
 
 public:
-  static CXX26AnnotationAttr *Create(ASTContext &Ctx, \
-                                     const AttributeCommonInfo &CommonInfo) {
-    return CXX26AnnotationAttr::Create(Ctx, nullptr,  CommonInfo);
-  }
-  static CXX26AnnotationAttr *CreateImplicit( \
-          ASTContext &Ctx, \
-          const AttributeCommonInfo &CommonInfo) {
-    return CXX26AnnotationAttr::CreateImplicit(Ctx, nullptr, CommonInfo);
-  }
 
   APValue getValue() const { return Value; }
   void setValue(APValue V) { Value = V; }
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index c9b9b00eddac6..adbd6c55883ea 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -798,8 +798,8 @@ def err_cxx11_attribute_forbids_ellipsis : Error<
 def warn_cxx14_compat_using_attribute_ns : Warning<
   "default scope specifier for attributes is incompatible with C++ standards "
   "before C++17">, InGroup<CXXPre17Compat>, DefaultIgnore;
-def warn_cxx26_compat_annotation : Warning<
-  "annotation is a C++26 extension">, InGroup<CXXPre26Compat>, DefaultIgnore;
+def err_cxx26_compat_annotation : ExtWarn<
+  "annotations are a C++26 extension">, InGroup<CXXPre26Compat>, DefaultError;
 def err_mixed_attributes_and_annotations : Error<
   "attribute specifier cannot contain both attributes and annotations">;
 def ext_using_attribute_ns : ExtWarn<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3e864475f22a1..423dbfdb5a557 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3424,7 +3424,8 @@ def err_attribute_argument_n_type : Error<
   "constant|a string|an identifier|a constant expression|a builtin 
function}2">;
 def err_attribute_argument_type : Error<
   "%0 attribute requires %select{int or bool|an integer "
-  "constant|a string|an identifier}1">;
+  "constant|a string|an identifier|an expression usable as a template 
argument|"
+  "a value of structural type}1">;
 def err_attribute_argument_out_of_range : Error<
   "%0 attribute requires integer constant between %1 and %2 inclusive">;
 def err_init_priority_object_attr : Error<
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 920b7b9429c4c..2ef393dc228bf 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4707,20 +4707,16 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
   bool HasAnnotation = false;
   while (!Tok.isOneOf(tok::r_square, tok::semi, tok::eof)) {
     // If we parsed an attribute/annotation, a comma is required before parsing
-    //  any additional ones.
+    // any additional ones.
     if (HasAttribute || HasAnnotation) {
       if (ExpectAndConsume(tok::comma)) {
         SkipUntil(tok::r_square, StopAtSemi | StopBeforeMatch);
         continue;
       }
-      if (HasAttribute && HasAnnotation) {
-        Diag(Tok.getLocation(), diag::err_mixed_attributes_and_annotations);
-        SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
-        continue;
-      }
     }
 
-    // Eat all remaining superfluous commas before parsing the next attribute.
+    // Eat all remaining superfluous commas before parsing the next attribute
+    // or annotation
     while (TryConsumeToken(tok::comma))
       ;
 
@@ -4733,7 +4729,7 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
     //  - We must not mix with an attribute
     if (Tok.is(tok::equal)) {
       if (!getLangOpts().CPlusPlus26) {
-        Diag(Tok.getLocation(), diag::warn_cxx26_compat_annotation);
+        Diag(Tok.getLocation(), diag::err_cxx26_compat_annotation);
         SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
         continue;
       }
@@ -4790,6 +4786,11 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
                                            ScopeName, ScopeLoc, OpenMPTokens);
 
     if (!HasAttribute) {
+      if (HasAnnotation) {
+        Diag(Tok.getLocation(), diag::err_mixed_attributes_and_annotations);
+        SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
+        continue;
+      }
       Attrs.addNew(AttrName,
                    SourceRange(ScopeLoc.isValid() && CommonScopeLoc.isInvalid()
                                    ? ScopeLoc
diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp
index 8f648d90e5742..4a9586de3d4ff 100644
--- a/clang/lib/Sema/ParsedAttr.cpp
+++ b/clang/lib/Sema/ParsedAttr.cpp
@@ -109,8 +109,20 @@ const ParsedAttrInfo &ParsedAttrInfo::get(const 
AttributeCommonInfo &A) {
 
   // If this is an annotation then return an appropriate ParsedAttrInfo.
   static const ParsedAttrInfo AnnotationAttrInfo(
-      AttributeCommonInfo::AT_CXX26Annotation, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 
{},
-      nullptr);
+    AttributeCommonInfo::AT_CXX26Annotation, // AttrKind
+    1, // NumArgs
+    0, // OptArgs
+    1, // NumArgMembers
+    1, // HasCustomParsing
+    0, // AcceptsExprPack
+    0, // IsTargetSpecific
+    1, // IsType
+    1, // IsStmt
+    0, // IsKnownToGCC,
+    0, // IsSupportedByPragmaAttribute
+    {}, // Spellings
+    nullptr // ArgNames
+  );
   if (A.getSyntax() == AttributeCommonInfo::AS_Annotation)
     return AnnotationAttrInfo;
 
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 2e293b15b97d0..6b18ca6c3d957 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6443,6 +6443,7 @@ static void handleCxx26AnnotationAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
              ? ConstantExprKind::ClassTemplateArgument
              : ConstantExprKind::NonClassTemplateArgument);
 
+    // Argument to annotation must be usable as template argument
     if (!CE->EvaluateAsConstantExpr(Result, S.Context, CEKind)) {
       S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)
           << "C++26 annotation" << 4 << CE->getSourceRange();
@@ -6451,6 +6452,7 @@ static void handleCxx26AnnotationAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
 
       return;
     }
+    // Argument to annotation must evaluate to structural type
     if (!CE->getType()->isStructuralType()) {
       S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)
           << "C++26 annotation" << 5 << CE->getSourceRange();
diff --git a/clang/test/SemaCXX/cxx26-annotation.cpp 
b/clang/test/SemaCXX/cxx26-annotation.cpp
index 333d420114666..13d420e5814f4 100644
--- a/clang/test/SemaCXX/cxx26-annotation.cpp
+++ b/clang/test/SemaCXX/cxx26-annotation.cpp
@@ -1,35 +1,35 @@
 // RUN: %clang_cc1 -std=c++26  -x c++ %s -verify
 
-struct F {
-  bool V;
-};
-
+// Nominal cases
 // Type
 struct [[=1]] f1 {};
 struct [[=1, =F{true}]] f2 {};
 struct [[=1]] [[=2]] f3 {};
-
 // Declaration
-const [[=1]] F ff{};
-
+struct F {
+  bool V;
+};
+const [[=1]] F f4{};
 // Redeclaration
-[[=2, =3, =2]] void g();
-void g [[=4, =2]] ();
+[[=2, =3, =2]] void f5();
+void f5 [[=4, =2]] ();
 
 // Error case
-struct [[nodiscard, =1]] f4 {};  // expected-error {{attribute specifier 
cannot contain both attributes and annotations}}
-struct [[=1, nodiscard, ]] f5 {};  // expected-error {{attribute specifier 
cannot contain both attributes and annotations}}
-
+// Mixing annotation and attributes, with or without trailing characters
+struct [[nodiscard, =1]] f6 {};  // expected-error {{attribute specifier 
cannot contain both attributes and annotations}}
+struct [[nodiscard, =1,]] f7 {};  // expected-error {{attribute specifier 
cannot contain both attributes and annotations}}
+struct [[=1, nodiscard, ]] f8 {};  // expected-error {{attribute specifier 
cannot contain both attributes and annotations}}
+struct [[=1, nodiscard ]] f9 {};  // expected-error {{attribute specifier 
cannot contain both attributes and annotations}}
+// Mixing attribute using and annotation
 struct G {
   [[using CC: =1]] [[=2]] int f;  // expected-error {{annotations are not 
permitted following an attribute-using-prefix}}
 };
-
+// Substituting into an annotation is not in the immediate context
 template<class T>
   [[=T::type()]] void h(T t); // expected-error {{type 'char' cannot be used 
prior to '::' because it has no members}}
-
+                              // expected-note@#inst {{in instantiation of 
function template specialization 'h<char>' requested here}}
 void h(int);
-
 void hh() {
   h(0);
-  h('0'); // expected-note {{in instantiation of function template 
specialization 'h<char>' requested here}}
+  h('0'); // #inst
 }
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 17e53ac944201..cf648ad7848b8 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -3863,7 +3863,9 @@ void EmitClangAttrHasAttrImpl(const RecordKeeper 
&Records, raw_ostream &OS) {
       else if (Variety == "AS_Annotation") {
         // We should not be code gening anything with a C++26
         // annotation syntax.
-        // TODO do we want to warn here ?
+        PrintError(R->getLoc(),
+          "Invalid syntax 'Annotation' used on the node '" +
+          R->getName() + "'");
       }
     }
   }

>From 098155533457ac1c0247e3f1a33ef659cf717cb1 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Fri, 14 Nov 2025 17:20:37 +0900
Subject: [PATCH 11/16] Apply formatter

Signed-off-by: acassagnes <[email protected]>
---
 clang/lib/Sema/ParsedAttr.cpp             | 26 +++++++++++------------
 clang/utils/TableGen/ClangAttrEmitter.cpp |  4 ++--
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp
index 4a9586de3d4ff..ddfb43601d1db 100644
--- a/clang/lib/Sema/ParsedAttr.cpp
+++ b/clang/lib/Sema/ParsedAttr.cpp
@@ -109,19 +109,19 @@ const ParsedAttrInfo &ParsedAttrInfo::get(const 
AttributeCommonInfo &A) {
 
   // If this is an annotation then return an appropriate ParsedAttrInfo.
   static const ParsedAttrInfo AnnotationAttrInfo(
-    AttributeCommonInfo::AT_CXX26Annotation, // AttrKind
-    1, // NumArgs
-    0, // OptArgs
-    1, // NumArgMembers
-    1, // HasCustomParsing
-    0, // AcceptsExprPack
-    0, // IsTargetSpecific
-    1, // IsType
-    1, // IsStmt
-    0, // IsKnownToGCC,
-    0, // IsSupportedByPragmaAttribute
-    {}, // Spellings
-    nullptr // ArgNames
+      AttributeCommonInfo::AT_CXX26Annotation, // AttrKind
+      1,                                       // NumArgs
+      0,                                       // OptArgs
+      1,                                       // NumArgMembers
+      1,                                       // HasCustomParsing
+      0,                                       // AcceptsExprPack
+      0,                                       // IsTargetSpecific
+      1,                                       // IsType
+      1,                                       // IsStmt
+      0,                                       // IsKnownToGCC,
+      0,                                       // IsSupportedByPragmaAttribute
+      {},                                      // Spellings
+      nullptr                                  // ArgNames
   );
   if (A.getSyntax() == AttributeCommonInfo::AS_Annotation)
     return AnnotationAttrInfo;
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index cf648ad7848b8..7c21bc48593a2 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -3864,8 +3864,8 @@ void EmitClangAttrHasAttrImpl(const RecordKeeper 
&Records, raw_ostream &OS) {
         // We should not be code gening anything with a C++26
         // annotation syntax.
         PrintError(R->getLoc(),
-          "Invalid syntax 'Annotation' used on the node '" +
-          R->getName() + "'");
+                   "Invalid syntax 'Annotation' used on the node '" +
+                       R->getName() + "'");
       }
     }
   }

>From 0eaf4dc67fd830c019923f70c4efe2023de534ce Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Fri, 14 Nov 2025 17:55:16 +0900
Subject: [PATCH 12/16] Fix UT

Signed-off-by: acassagnes <[email protected]>
---
 clang/test/SemaCXX/cxx26-annotation.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/test/SemaCXX/cxx26-annotation.cpp 
b/clang/test/SemaCXX/cxx26-annotation.cpp
index 13d420e5814f4..c310f9f69893f 100644
--- a/clang/test/SemaCXX/cxx26-annotation.cpp
+++ b/clang/test/SemaCXX/cxx26-annotation.cpp
@@ -1,14 +1,14 @@
 // RUN: %clang_cc1 -std=c++26  -x c++ %s -verify
 
+struct F {
+  bool V;
+};
 // Nominal cases
 // Type
 struct [[=1]] f1 {};
 struct [[=1, =F{true}]] f2 {};
 struct [[=1]] [[=2]] f3 {};
 // Declaration
-struct F {
-  bool V;
-};
 const [[=1]] F f4{};
 // Redeclaration
 [[=2, =3, =2]] void f5();

>From 8dd0e8bbafbc6f2680f22ad5642f05890279dd23 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Fri, 14 Nov 2025 18:21:19 +0900
Subject: [PATCH 13/16] Close comments with .

Signed-off-by: acassagnes <[email protected]>
---
 clang/include/clang/Basic/Attr.td  |  2 +-
 clang/include/clang/Parse/Parser.h |  2 +-
 clang/lib/Parse/ParseDeclCXX.cpp   | 12 ++++++------
 clang/lib/Sema/ParsedAttr.cpp      |  2 +-
 clang/lib/Sema/SemaDeclAttr.cpp    |  4 ++--
 5 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 7003a75e3e82e..1771d7fe4d5a5 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -750,7 +750,7 @@ class Attr {
   code AdditionalMembers = [{}];
   // Any additional text that should be included verbatim after instantiating
   // an attribute on a template. This can be used if additional bookkeeping is
-  // required for the data members
+  // required for the data members.
   code PostInstantiationStmts =[{}];
   // Any documentation that should be associated with the attribute. Since an
   // attribute may be documented under multiple categories, more than one
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 52b909ffcae91..9ca8b69265878 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3046,7 +3046,7 @@ class Parser : public CodeCompletionHandler {
                                SourceLocation ScopeLoc,
                                CachedTokens &OpenMPTokens);
 
-  /// Parse an annotation as specified from C++26
+  /// Parse an annotation as specified from C++26.
   void ParseAnnotationSpecifier(ParsedAttributes &Attrs,
                                 SourceLocation *EndLoc);
 
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 2ef393dc228bf..0368b6d63a576 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4716,17 +4716,17 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
     }
 
     // Eat all remaining superfluous commas before parsing the next attribute
-    // or annotation
+    // or annotation.
     while (TryConsumeToken(tok::comma))
       ;
 
     SourceLocation ScopeLoc, AttrLoc;
     IdentifierInfo *ScopeName = nullptr, *AttrName = nullptr;
 
-    // A '=' token marks the beginning of an annotation
-    //  - We must not be in C++ < 26
-    //  - We must not have seen 'using X::'
-    //  - We must not mix with an attribute
+    // A '=' token marks the beginning of an annotation with the restriction
+    //  - must not be in C++ < 26,
+    //  - must not have seen 'using X::',
+    //  - must not mix with an attribute.
     if (Tok.is(tok::equal)) {
       if (!getLangOpts().CPlusPlus26) {
         Diag(Tok.getLocation(), diag::err_cxx26_compat_annotation);
@@ -4780,7 +4780,7 @@ void 
Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
       }
     }
 
-    // Parse attribute arguments
+    // Parse attribute arguments.
     HasAttribute = Tok.is(tok::l_paren) &&
                    ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
                                            ScopeName, ScopeLoc, OpenMPTokens);
diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp
index ddfb43601d1db..409862631e7fa 100644
--- a/clang/lib/Sema/ParsedAttr.cpp
+++ b/clang/lib/Sema/ParsedAttr.cpp
@@ -118,7 +118,7 @@ const ParsedAttrInfo &ParsedAttrInfo::get(const 
AttributeCommonInfo &A) {
       0,                                       // IsTargetSpecific
       1,                                       // IsType
       1,                                       // IsStmt
-      0,                                       // IsKnownToGCC,
+      0,                                       // IsKnownToGCC
       0,                                       // IsSupportedByPragmaAttribute
       {},                                      // Spellings
       nullptr                                  // ArgNames
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 6b18ca6c3d957..55e24e52e4f15 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6443,7 +6443,7 @@ static void handleCxx26AnnotationAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
              ? ConstantExprKind::ClassTemplateArgument
              : ConstantExprKind::NonClassTemplateArgument);
 
-    // Argument to annotation must be usable as template argument
+    // Argument to annotation must be usable as template argument.
     if (!CE->EvaluateAsConstantExpr(Result, S.Context, CEKind)) {
       S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)
           << "C++26 annotation" << 4 << CE->getSourceRange();
@@ -6452,7 +6452,7 @@ static void handleCxx26AnnotationAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
 
       return;
     }
-    // Argument to annotation must evaluate to structural type
+    // Argument to annotation must evaluate to structural type.
     if (!CE->getType()->isStructuralType()) {
       S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)
           << "C++26 annotation" << 5 << CE->getSourceRange();

>From 8559a6eba58ce64104d5a62f37f69ab44939e55f Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Tue, 18 Nov 2025 08:34:45 +0900
Subject: [PATCH 14/16] Add test covering deleted copy on record type for
 annotation

Signed-off-by: acassagnes <[email protected]>
---
 clang/lib/Sema/SemaDeclAttr.cpp         |  5 +++--
 clang/test/SemaCXX/cxx26-annotation.cpp | 20 ++++++++++++++++++--
 2 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 55e24e52e4f15..c49008a89d505 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6446,7 +6446,7 @@ static void handleCxx26AnnotationAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
     // Argument to annotation must be usable as template argument.
     if (!CE->EvaluateAsConstantExpr(Result, S.Context, CEKind)) {
       S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)
-          << "C++26 annotation" << 4 << CE->getSourceRange();
+          << "C++26 annotation" << /*template arg=*/4 << CE->getSourceRange();
       for (auto P : Notes)
         S.Diag(P.first, P.second);
 
@@ -6455,7 +6455,8 @@ static void handleCxx26AnnotationAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
     // Argument to annotation must evaluate to structural type.
     if (!CE->getType()->isStructuralType()) {
       S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type)
-          << "C++26 annotation" << 5 << CE->getSourceRange();
+          << "C++26 annotation" << /*value or structural type*/ 5
+          << CE->getSourceRange();
       return;
     }
   }
diff --git a/clang/test/SemaCXX/cxx26-annotation.cpp 
b/clang/test/SemaCXX/cxx26-annotation.cpp
index c310f9f69893f..cd9f46ec69f48 100644
--- a/clang/test/SemaCXX/cxx26-annotation.cpp
+++ b/clang/test/SemaCXX/cxx26-annotation.cpp
@@ -10,6 +10,7 @@ struct [[=1, =F{true}]] f2 {};
 struct [[=1]] [[=2]] f3 {};
 // Declaration
 const [[=1]] F f4{};
+void foo([[=F{false}]]int i) {}
 // Redeclaration
 [[=2, =3, =2]] void f5();
 void f5 [[=4, =2]] ();
@@ -27,9 +28,24 @@ struct G {
 // Substituting into an annotation is not in the immediate context
 template<class T>
   [[=T::type()]] void h(T t); // expected-error {{type 'char' cannot be used 
prior to '::' because it has no members}}
-                              // expected-note@#inst {{in instantiation of 
function template specialization 'h<char>' requested here}}
+                              // expected-note@#inst-H {{in instantiation of 
function template specialization 'h<char>' requested here}}
+struct T {
+  static constexpr int type() { return 0; }
+};
+
 void h(int);
 void hh() {
   h(0);
-  h('0'); // #inst
+  h('0'); // #inst-H
+  h(T{});
 }
+
+// Handle copying lvalue
+struct U {
+  bool V;
+  constexpr U(bool v) : V(v) {}
+  U(const U&) = delete; // #del-U
+};
+constexpr U u(true);
+struct [[ =u ]] h2{}; // expected-error {{call to deleted constructor of 'U'}}
+                      // expected-note@#del-U {{'U' has been explicitly marked 
deleted here}}

>From 3319fc6cca7e7b68487c76fa0c49ba8b8a5cb602 Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Tue, 18 Nov 2025 12:44:33 +0900
Subject: [PATCH 15/16] Add more test on structural type

Signed-off-by: acassagnes <[email protected]>
---
 clang/test/SemaCXX/cxx26-annotation.cpp | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/clang/test/SemaCXX/cxx26-annotation.cpp 
b/clang/test/SemaCXX/cxx26-annotation.cpp
index cd9f46ec69f48..df01beded26ad 100644
--- a/clang/test/SemaCXX/cxx26-annotation.cpp
+++ b/clang/test/SemaCXX/cxx26-annotation.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++26  -x c++ %s -verify
+// RUN: %clang_cc1 -std=c++26 -fexperimental-new-constant-interpreter -x c++ 
%s -verify
 
 struct F {
   bool V;
@@ -10,7 +10,7 @@ struct [[=1, =F{true}]] f2 {};
 struct [[=1]] [[=2]] f3 {};
 // Declaration
 const [[=1]] F f4{};
-void foo([[=F{false}]]int i) {}
+void foo([[=F{false}]]int i) {} // function parameters
 // Redeclaration
 [[=2, =3, =2]] void f5();
 void f5 [[=4, =2]] ();
@@ -49,3 +49,11 @@ struct U {
 constexpr U u(true);
 struct [[ =u ]] h2{}; // expected-error {{call to deleted constructor of 'U'}}
                       // expected-note@#del-U {{'U' has been explicitly marked 
deleted here}}
+
+// Non structural
+struct [[="notstructural"]] h3{}; // expected-error {{C++26 annotation 
attribute requires an expression usable as a template argument}} \
+                                     expected-note {{reference to string 
literal is not allowed in a template argument}}
+
+// Pointer into string literal
+struct [[=&"foo"[0]]] h4{}; // expected-error {{C++26 annotation attribute 
requires an expression usable as a template argument}} \
+                               expected-note {{pointer to subobject of string 
literal is not allowed in a template argument}}

>From 91095572881f9f3f646ef19c555415602dd173bd Mon Sep 17 00:00:00 2001
From: acassagnes <[email protected]>
Date: Wed, 19 Nov 2025 08:47:18 +0900
Subject: [PATCH 16/16] Remove placeholder attr name for annotation

Signed-off-by: acassagnes <[email protected]>
---
 .../include/clang/Basic/AttributeCommonInfo.h |  1 +
 clang/include/clang/Sema/ParsedAttr.h         | 41 +++++++++++++++++++
 clang/lib/Parse/ParseDeclCXX.cpp              |  6 +--
 clang/lib/Sema/ParsedAttr.cpp                 |  8 +++-
 4 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h 
b/clang/include/clang/Basic/AttributeCommonInfo.h
index 0baee12071e3c..4825da7d7f1b1 100644
--- a/clang/include/clang/Basic/AttributeCommonInfo.h
+++ b/clang/include/clang/Basic/AttributeCommonInfo.h
@@ -193,6 +193,7 @@ class AttributeCommonInfo {
     return Form(getSyntax(), SpellingIndex, IsAlignas,
                 IsRegularKeywordAttribute);
   }
+
   const IdentifierInfo *getAttrName() const { return AttrName; }
   void setAttrName(const IdentifierInfo *AttrNameII) { AttrName = AttrNameII; }
   SourceLocation getLoc() const { return AttrRange.getBegin(); }
diff --git a/clang/include/clang/Sema/ParsedAttr.h 
b/clang/include/clang/Sema/ParsedAttr.h
index 5387f9fad6cd2..92659e1dafd9f 100644
--- a/clang/include/clang/Sema/ParsedAttr.h
+++ b/clang/include/clang/Sema/ParsedAttr.h
@@ -216,6 +216,20 @@ class ParsedAttr final
       memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion));
   }
 
+  /// Constructor for an annotation with expression argument
+  ParsedAttr(SourceRange attrRange, AttributeScopeInfo scope, ArgsUnion *args,
+             unsigned numArgs, Form formUsed, SourceLocation ellipsisLoc)
+      : AttributeCommonInfo(attrRange, ParsedAttr::Kind::AT_CXX26Annotation,
+                            ParsedAttr::Form::Annotation()),
+        EllipsisLoc(ellipsisLoc), NumArgs(numArgs), Invalid(false),
+        UsedAsTypeAttr(false), IsAvailability(false),
+        IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
+        HasProcessingCache(false), IsPragmaClangAttribute(false),
+        Info(ParsedAttrInfo::get(*this)) {
+    assert(numArgs == 1);
+    memcpy(getArgsBuffer(), args, sizeof(ArgsUnion));
+  }
+
   /// Constructor for availability attributes.
   ParsedAttr(IdentifierInfo *attrName, SourceRange attrRange,
              AttributeScopeInfo scope, IdentifierLoc *Parm,
@@ -504,6 +518,7 @@ class ParsedAttr final
   /// error. Returns false if a diagnostic is produced.
   bool checkAtMostNumArgs(class Sema &S, unsigned Num) const;
 
+  bool isAnnotationAttr() const;
   bool isTargetSpecificAttr() const;
   bool isTypeAttr() const;
   bool isStmtAttr() const;
@@ -732,6 +747,22 @@ class AttributePool {
   /// them at the end of this \c AttributePool.
   void takeFrom(ParsedAttributesView &List, AttributePool &Pool);
 
+  // Create a Cxx26 annotation
+  ParsedAttr *create(SourceRange attrRange, AttributeScopeInfo scope,
+                     ArgsUnion *args, unsigned numArgs, ParsedAttr::Form form,
+                     SourceLocation ellipsisLoc = SourceLocation()) {
+    // Only annotations are allowed to pass through without an identifier
+    assert(form.getSyntax() == ParsedAttr::Syntax::AS_Annotation);
+
+    void *memory = allocate(
+        ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
+                                     detail::TypeTagForDatatypeData, 
ParsedType,
+                                     detail::PropertyData>(numArgs, 0, 0, 0,
+                                                           0));
+    return add(new (memory) ParsedAttr(attrRange, scope, args, numArgs, form,
+                                       ellipsisLoc));
+  }
+
   ParsedAttr *create(IdentifierInfo *attrName, SourceRange attrRange,
                      AttributeScopeInfo scope, ArgsUnion *args,
                      unsigned numArgs, ParsedAttr::Form form,
@@ -974,6 +1005,16 @@ class ParsedAttributes : public ParsedAttributesView {
     Range = SourceRange();
   }
 
+  /// Add annotation with expression argument
+  ParsedAttr *addNew(SourceRange attrRange, AttributeScopeInfo scope,
+                     ArgsUnion *args, unsigned numArgs, ParsedAttr::Form form,
+                     SourceLocation ellipsisLoc = SourceLocation()) {
+    ParsedAttr *attr =
+        pool.create(attrRange, scope, args, numArgs, form, ellipsisLoc);
+    addAtEnd(attr);
+    return attr;
+  }
+
   /// Add attribute with expression arguments.
   ParsedAttr *addNew(IdentifierInfo *attrName, SourceRange attrRange,
                      AttributeScopeInfo scope, ArgsUnion *args,
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 0368b6d63a576..3e922eeb6273f 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4452,13 +4452,9 @@ void Parser::ParseAnnotationSpecifier(ParsedAttributes 
&Attrs,
   if (AnnotExpr.isInvalid() || AnnotExpr.get()->containsErrors())
     return;
 
-  IdentifierTable &IT = Actions.PP.getIdentifierTable();
-  IdentifierInfo &Placeholder = IT.get("__annotation_placeholder");
-
   ArgsVector ArgExprs;
   ArgExprs.push_back(AnnotExpr.get());
-  Attrs.addNew(&Placeholder, EqLoc, {}, ArgExprs.data(), 1,
-               ParsedAttr::Form::Annotation());
+  Attrs.addNew(EqLoc, {}, ArgExprs.data(), 1, ParsedAttr::Form::Annotation());
 
   if (EndLoc)
     *EndLoc = AnnotExpr.get()->getEndLoc();
diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp
index 409862631e7fa..2dd432b8943ba 100644
--- a/clang/lib/Sema/ParsedAttr.cpp
+++ b/clang/lib/Sema/ParsedAttr.cpp
@@ -203,6 +203,10 @@ bool ParsedAttr::isTargetSpecificAttr() const {
   return getInfo().IsTargetSpecific;
 }
 
+bool ParsedAttr::isAnnotationAttr() const {
+  return getParsedKind() == AT_CXX26Annotation;
+}
+
 bool ParsedAttr::isTypeAttr() const { return getInfo().IsType; }
 
 bool ParsedAttr::isStmtAttr() const { return getInfo().IsStmt; }
@@ -213,8 +217,8 @@ bool ParsedAttr::existsInTarget(const TargetInfo &Target) 
const {
   // If the attribute has a target-specific spelling, check that it exists.
   // Only call this if the attr is not ignored/unknown. For most targets, this
   // function just returns true.
-  bool HasSpelling = K != IgnoredAttribute && K != UnknownAttribute &&
-                     K != NoSemaHandlerAttribute;
+  bool HasSpelling = !isAnnotationAttr() && K != IgnoredAttribute &&
+                     K != UnknownAttribute && K != NoSemaHandlerAttribute;
   bool TargetSpecificSpellingExists =
       !HasSpelling ||
       getInfo().spellingExistsInTarget(Target, 
getAttributeSpellingListIndex());

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to