https://github.com/rapidsna updated 
https://github.com/llvm/llvm-project/pull/166491

>From f0ec14089464ef30a08f9d0646a3b0b7a4fdf5f0 Mon Sep 17 00:00:00 2001
From: Yeoul Na <[email protected]>
Date: Wed, 22 Oct 2025 10:46:46 -0700
Subject: [PATCH 1/3] [BoundsSafety] Support late parsing for `counted_by` in
 type positions

Previously, late parsing only supported attributes in declaration
position and could not resolve references to member declarations that
appeared later in the struct. Additionally, the compiler incorrectly
accepted `counted_by` on nested pointer types, which should be rejected.
Instead, these attributes were incorrectly attached to the outermost
pointer type because they were being treated as GNU-style declaration
attributes rather than type attributes.

This commit adds a vector of `LateParsedAttribute *` to
`DeclaratorChunk`, similar to how `ParsedAttr` is attached to
`DeclaratorChunk`. Since DeclSpec doesn't see the definition of
`LateParsedAttribute`, it is treated as an opaque pointer type. When
the late-attribute is actually parsed, it is now correctly attached
to the appropriate nested type level, enabling references to members
declared later when the late-parsed attribute is in type position, and
rejection of invalid nested pointer cases.

Issue #166411
---
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/include/clang/Parse/Parser.h            |  31 ++--
 clang/include/clang/Sema/DeclSpec.h           |   8 +-
 clang/include/clang/Sema/Sema.h               |  10 +-
 clang/lib/Parse/ParseDecl.cpp                 |  81 +++++---
 clang/lib/Sema/SemaBoundsSafety.cpp           |  10 +-
 clang/lib/Sema/SemaDecl.cpp                   |   6 +-
 clang/lib/Sema/SemaDeclAttr.cpp               |  32 +++-
 .../AST/attr-counted-by-or-null-struct-ptrs.c |  23 ---
 clang/test/AST/attr-counted-by-struct-ptrs.c  |  26 ---
 .../AST/attr-sized-by-or-null-struct-ptrs.c   |  24 ---
 clang/test/AST/attr-sized-by-struct-ptrs.c    |  24 ---
 .../attr-bounds-safety-function-ptr-param.c   | 173 ++++++++++++++++++
 .../attr-counted-by-late-parsed-struct-ptrs.c |  49 +----
 ...unted-by-or-null-late-parsed-struct-ptrs.c |  49 +----
 ...ruct-ptrs-completable-incomplete-pointee.c |  33 ++--
 .../attr-counted-by-or-null-struct-ptrs.c     |   8 +-
 ...ruct-ptrs-completable-incomplete-pointee.c |  32 ++--
 clang/test/Sema/attr-counted-by-struct-ptrs.c |   8 +-
 .../attr-sized-by-late-parsed-struct-ptrs.c   |  39 +---
 ...sized-by-or-null-late-parsed-struct-ptrs.c |  46 +----
 .../Sema/attr-sized-by-or-null-struct-ptrs.c  |   8 +-
 clang/test/Sema/attr-sized-by-struct-ptrs.c   |   6 +-
 23 files changed, 368 insertions(+), 361 deletions(-)
 create mode 100644 clang/test/Sema/attr-bounds-safety-function-ptr-param.c

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fa509536bf021..98b470a525d11 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7012,6 +7012,9 @@ def err_builtin_counted_by_ref_invalid_use : Error<
   "value returned by '__builtin_counted_by_ref' cannot be used in "
   "%select{an array subscript|a binary}0 expression">;
 
+def err_counted_by_on_nested_pointer : Error<
+  "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' 
attribute on nested pointer type is not allowed">;
+
 let CategoryName = "ARC Semantic Issue" in {
 
 // ARC-mode diagnostics.
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index dad8efd0f017f..8f7c921fb2b1d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1161,10 +1161,11 @@ class Parser : public CodeCompletionHandler {
     IdentifierInfo *MacroII = nullptr;
     SourceLocation AttrNameLoc;
     SmallVector<Decl *, 2> Decls;
+    unsigned NestedTypeLevel;
 
     explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
-                                 SourceLocation Loc)
-        : Self(P), AttrName(Name), AttrNameLoc(Loc) {}
+                                 SourceLocation Loc, unsigned Level = 0)
+        : Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(Level) {}
 
     void ParseLexedAttributes() override;
 
@@ -1889,10 +1890,12 @@ class Parser : public CodeCompletionHandler {
       DeclSpec &DS, AccessSpecifier AS, DeclSpecContext DSContext,
       LateParsedAttrList *LateAttrs = nullptr);
 
-  void ParseSpecifierQualifierList(
-      DeclSpec &DS, AccessSpecifier AS = AS_none,
-      DeclSpecContext DSC = DeclSpecContext::DSC_normal) {
-    ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC);
+  void
+  ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS = AS_none,
+                              DeclSpecContext DSC = 
DeclSpecContext::DSC_normal,
+                              LateParsedAttrList *LateAttrs = nullptr) {
+    ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC,
+                                LateAttrs);
   }
 
   /// ParseSpecifierQualifierList
@@ -1903,10 +1906,12 @@ class Parser : public CodeCompletionHandler {
   /// [GNU]    attributes     specifier-qualifier-list[opt]
   /// \endverbatim
   ///
-  void ParseSpecifierQualifierList(
-      DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
-      AccessSpecifier AS = AS_none,
-      DeclSpecContext DSC = DeclSpecContext::DSC_normal);
+  void
+  ParseSpecifierQualifierList(DeclSpec &DS,
+                              ImplicitTypenameContext AllowImplicitTypename,
+                              AccessSpecifier AS = AS_none,
+                              DeclSpecContext DSC = 
DeclSpecContext::DSC_normal,
+                              LateParsedAttrList *LateAttrs = nullptr);
 
   /// ParseEnumSpecifier
   /// \verbatim
@@ -2444,7 +2449,8 @@ class Parser : public CodeCompletionHandler {
                                  SourceLocation ScopeLoc,
                                  ParsedAttr::Form Form);
 
-  void DistributeCLateParsedAttrs(Decl *Dcl, LateParsedAttrList *LateAttrs);
+  void DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
+                                  LateParsedAttrList *LateAttrs);
 
   /// Bounds attributes (e.g., counted_by):
   /// \verbatim
@@ -2610,7 +2616,8 @@ class Parser : public CodeCompletionHandler {
   void ParseTypeQualifierListOpt(
       DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed,
       bool AtomicOrPtrauthAllowed = true, bool IdentifierRequired = false,
-      llvm::function_ref<void()> CodeCompletionHandler = {});
+      llvm::function_ref<void()> CodeCompletionHandler = {},
+      LateParsedAttrList *LateAttrs = nullptr);
 
   /// ParseDirectDeclarator
   /// \verbatim
diff --git a/clang/include/clang/Sema/DeclSpec.h 
b/clang/include/clang/Sema/DeclSpec.h
index 43a48c92fc305..9f633dd71c3f6 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1238,6 +1238,9 @@ struct DeclaratorChunk {
 
   ParsedAttributesView AttrList;
 
+  using LateAttrListTy = SmallVector<void *, 1>;
+  LateAttrListTy LateAttrList;
+
   struct PointerTypeInfo {
     /// The type qualifiers: const/volatile/restrict/unaligned/atomic.
     LLVM_PREFERRED_TYPE(DeclSpec::TQ)
@@ -2325,13 +2328,16 @@ class Declarator {
   /// This function takes attrs by R-Value reference because it takes ownership
   /// of those attributes from the parameter.
   void AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
-                   SourceLocation EndLoc) {
+                   SourceLocation EndLoc,
+                   const DeclaratorChunk::LateAttrListTy &LateAttrs = {}) {
     DeclTypeInfo.push_back(TI);
     DeclTypeInfo.back().getAttrs().prepend(attrs.begin(), attrs.end());
     getAttributePool().takeAllFrom(attrs.getPool());
 
     if (!EndLoc.isInvalid())
       SetRangeEnd(EndLoc);
+
+    DeclTypeInfo.back().LateAttrList.assign(LateAttrs);
   }
 
   /// AddTypeInfo - Add a chunk to this declarator. Also extend the range to
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index c67ed99b1f49e..becadbb1a39d6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2457,8 +2457,8 @@ class Sema final : public SemaBase {
   /// `counted_by_or_null` attribute.
   ///
   /// \returns false iff semantically valid.
-  bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
-                                 bool OrNull);
+  bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, unsigned Level,
+                                 bool CountInBytes, bool OrNull);
 
   /// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` 
or
   /// `__counted_by_or_null` pointer type \param LHSTy.
@@ -4198,7 +4198,8 @@ class Sema final : public SemaBase {
 
   /// ActOnFinishDelayedAttribute - Invoked when we have finished parsing an
   /// attribute for which parsing is delayed.
-  void ActOnFinishDelayedAttribute(Scope *S, Decl *D, ParsedAttributes &Attrs);
+  void ActOnFinishDelayedAttribute(Scope *S, Decl *D, ParsedAttributes &Attrs,
+                                   unsigned NestedTypeLevel = 0);
 
   /// Diagnose any unused parameters in the given sequence of
   /// ParmVarDecl pointers.
@@ -5071,7 +5072,8 @@ class Sema final : public SemaBase {
   void ProcessDeclAttributeList(Scope *S, Decl *D,
                                 const ParsedAttributesView &AttrList,
                                 const ProcessDeclAttributeOptions &Options =
-                                    ProcessDeclAttributeOptions());
+                                    ProcessDeclAttributeOptions(),
+                                unsigned NestedTypeLevel = 0);
 
   /// Annotation attributes are the only attributes allowed after an access
   /// specifier.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7e4a164e34eda..514f6daf44e2f 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -1943,7 +1943,11 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(
 
   ParsedTemplateInfo TemplateInfo;
   DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context);
-  ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext);
+  // FIXME: Why is PSoon true?
+  LateParsedAttrList BoundsSafetyLateAttrs(
+      /*PSoon=*/true, /*LateAttrParseExperimentalExtOnly=*/true);
+  ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext,
+                             &BoundsSafetyLateAttrs);
 
   // If we had a free-standing type definition with a missing semicolon, we
   // may get this far before the problem becomes obvious.
@@ -2725,12 +2729,12 @@ Decl 
*Parser::ParseDeclarationAfterDeclaratorAndAttributes(
 
 void Parser::ParseSpecifierQualifierList(
     DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
-    AccessSpecifier AS, DeclSpecContext DSC) {
+    AccessSpecifier AS, DeclSpecContext DSC, LateParsedAttrList *LateAttrs) {
   ParsedTemplateInfo TemplateInfo;
   /// specifier-qualifier-list is a subset of declaration-specifiers.  Just
   /// parse declaration-specifiers and complain about extra stuff.
   /// TODO: diagnose attribute-specifiers and alignment-specifiers.
-  ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, nullptr,
+  ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, LateAttrs,
                              AllowImplicitTypename);
 
   // Validate declspec for type-name.
@@ -3136,15 +3140,37 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes 
&Attrs,
   }
 }
 
-void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
+void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
                                         LateParsedAttrList *LateAttrs) {
   if (!LateAttrs)
     return;
 
+  unsigned NestedLevel = 0;
+  for (unsigned i = 0; i < D.getNumTypeObjects(); ++i) {
+    DeclaratorChunk &DC = D.getTypeObject(i);
+
+    switch (DC.Kind) {
+    case DeclaratorChunk::Pointer:
+    case DeclaratorChunk::Array:
+      break;
+    default:
+      continue;
+    }
+
+    // Extract `LateParsedAttribute *` from `DeclaratorChunk`.
+    for (auto *OpaqueLA : DC.LateAttrList) {
+      auto *LA = static_cast<LateParsedAttribute *>(OpaqueLA);
+      LA->NestedTypeLevel = NestedLevel;
+      LateAttrs->push_back(LA);
+    }
+    NestedLevel++;
+  }
+
+  // Attach `Decl *` to each `LateParsedAttribute *`.
   if (Dcl) {
-    for (auto *LateAttr : *LateAttrs) {
-      if (LateAttr->Decls.empty())
-        LateAttr->addDecl(Dcl);
+    for (auto *LA : *LateAttrs) {
+      if (LA->Decls.empty())
+        LA->addDecl(Dcl);
     }
   }
 }
@@ -3217,12 +3243,6 @@ void Parser::ParseBoundsAttribute(IdentifierInfo 
&AttrName,
   ArgExprs.push_back(ArgExpr.get());
   Parens.consumeClose();
 
-  ASTContext &Ctx = Actions.getASTContext();
-
-  ArgExprs.push_back(IntegerLiteral::Create(
-      Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.getSizeType()), 0),
-      Ctx.getSizeType(), SourceLocation()));
-
   Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, Parens.getCloseLocation()),
                AttributeScopeInfo(), ArgExprs.data(), ArgExprs.size(), Form);
 }
@@ -4706,7 +4726,8 @@ void Parser::ParseStructDeclaration(
   MaybeParseCXX11Attributes(Attrs);
 
   // Parse the common specifier-qualifiers-list piece.
-  ParseSpecifierQualifierList(DS);
+  ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_normal,
+                              LateFieldAttrs);
 
   // If there are no declarators, this is a free-standing declaration
   // specifier. Let the actions module cope with it.
@@ -4768,7 +4789,7 @@ void Parser::ParseStructDeclaration(
     // We're done with this declarator;  invoke the callback.
     Decl *Field = FieldsCallback(DeclaratorInfo);
     if (Field)
-      DistributeCLateParsedAttrs(Field, LateFieldAttrs);
+      DistributeCLateParsedAttrs(DeclaratorInfo.D, Field, LateFieldAttrs);
 
     // If we don't have a comma, it is either the end of the list (a ';')
     // or an error, bail out.
@@ -4825,7 +4846,8 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute 
&LA, bool EnterScope,
                         SourceLocation(), ParsedAttr::Form::GNU(), nullptr);
 
   for (auto *D : LA.Decls)
-    Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs);
+    Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs,
+                                        LA.NestedTypeLevel);
 
   // Due to a parsing error, we either went over the cached tokens or
   // there are still cached tokens left, so we skip the leftover tokens.
@@ -6124,7 +6146,8 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, 
bool DeductionGuide,
 
 void Parser::ParseTypeQualifierListOpt(
     DeclSpec &DS, unsigned AttrReqs, bool AtomicOrPtrauthAllowed,
-    bool IdentifierRequired, llvm::function_ref<void()> CodeCompletionHandler) 
{
+    bool IdentifierRequired, llvm::function_ref<void()> CodeCompletionHandler,
+    LateParsedAttrList *LateAttrs) {
   if ((AttrReqs & AR_CXX11AttributesParsed) &&
       isAllowedCXX11AttributeSpecifier()) {
     ParsedAttributes Attrs(AttrFactory);
@@ -6266,7 +6289,9 @@ void Parser::ParseTypeQualifierListOpt(
       // recovery is graceful.
       if (AttrReqs & AR_GNUAttributesParsed ||
           AttrReqs & AR_GNUAttributesParsedAndRejected) {
-        ParseGNUAttributes(DS.getAttributes());
+
+        assert(!LateAttrs || LateAttrs->lateAttrParseExperimentalExtOnly());
+        ParseGNUAttributes(DS.getAttributes(), LateAttrs);
         continue; // do *not* consume the next token!
       }
       // otherwise, FALL THROUGH!
@@ -6447,21 +6472,33 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
                     ((D.getContext() != DeclaratorContext::CXXNew)
                          ? AR_GNUAttributesParsed
                          : AR_GNUAttributesParsedAndRejected);
+    LateParsedAttrList LateAttrs(/*PSoon=*/true,
+                                 /*LateAttrParseExperimentalExtOnly=*/true);
     ParseTypeQualifierListOpt(DS, Reqs, /*AtomicOrPtrauthAllowed=*/true,
-                              !D.mayOmitIdentifier());
+                              !D.mayOmitIdentifier(), {}, &LateAttrs);
     D.ExtendWithDeclSpec(DS);
 
     // Recursively parse the declarator.
     Actions.runWithSufficientStackSpace(
         D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); 
});
-    if (Kind == tok::star)
+    if (Kind == tok::star) {
+      DeclaratorChunk::LateAttrListTy OpaqueLateAttrList;
+      if (getLangOpts().ExperimentalLateParseAttributes && !LateAttrs.empty()) 
{
+        if (!D.isFunctionDeclarator()) {
+          for (auto LA : LateAttrs) {
+            OpaqueLateAttrList.push_back(LA);
+          }
+        }
+        LateAttrs.clear();
+      }
       // Remember that we parsed a pointer type, and remember the type-quals.
       D.AddTypeInfo(DeclaratorChunk::getPointer(
                         DS.getTypeQualifiers(), Loc, DS.getConstSpecLoc(),
                         DS.getVolatileSpecLoc(), DS.getRestrictSpecLoc(),
                         DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc()),
-                    std::move(DS.getAttributes()), SourceLocation());
-    else
+                    std::move(DS.getAttributes()), SourceLocation(),
+                    OpaqueLateAttrList);
+    } else
       // Remember that we parsed a Block type, and remember the type-quals.
       D.AddTypeInfo(
           DeclaratorChunk::getBlockPointer(DS.getTypeQualifiers(), Loc),
diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp 
b/clang/lib/Sema/SemaBoundsSafety.cpp
index 39ab13653f5fe..8411eec7dfb3f 100644
--- a/clang/lib/Sema/SemaBoundsSafety.cpp
+++ b/clang/lib/Sema/SemaBoundsSafety.cpp
@@ -50,8 +50,8 @@ enum class CountedByInvalidPointeeTypeKind {
   VALID,
 };
 
-bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
-                                     bool OrNull) {
+bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, unsigned Level,
+                                     bool CountInBytes, bool OrNull) {
   // Check the context the attribute is used in
 
   unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
@@ -62,6 +62,12 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, 
bool CountInBytes,
     return true;
   }
 
+  if (Level != 0) {
+    Diag(FD->getBeginLoc(), diag::err_counted_by_on_nested_pointer)
+        << Kind << FD->getSourceRange();
+    return true;
+  }
+
   const auto FieldTy = FD->getType();
   if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
     Diag(FD->getBeginLoc(),
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index fc3aabf5741ca..85e75d4e4b91a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16873,11 +16873,13 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt 
*Body, bool IsInstantiation,
 /// When we finish delayed parsing of an attribute, we must attach it to the
 /// relevant Decl.
 void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D,
-                                       ParsedAttributes &Attrs) {
+                                       ParsedAttributes &Attrs,
+                                       unsigned NestedTypeLevel) {
   // Always attach attributes to the underlying decl.
   if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D))
     D = TD->getTemplatedDecl();
-  ProcessDeclAttributeList(S, D, Attrs);
+  ProcessDeclAttributeList(S, D, Attrs, ProcessDeclAttributeOptions(),
+                           NestedTypeLevel);
   ProcessAPINotes(D);
 
   if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D))
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a9e7b44ac9d73..2369180713ffb 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6546,7 +6546,8 @@ static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, 
const ParsedAttr &AL) {
   D->addAttr(ZeroCallUsedRegsAttr::Create(S.Context, Kind, AL));
 }
 
-static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
+static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL,
+                                     unsigned Level) {
   auto *FD = dyn_cast<FieldDecl>(D);
   assert(FD);
 
@@ -6577,7 +6578,7 @@ static void handleCountedByAttrField(Sema &S, Decl *D, 
const ParsedAttr &AL) {
     llvm_unreachable("unexpected counted_by family attribute");
   }
 
-  if (S.CheckCountedByAttrOnField(FD, CountExpr, CountInBytes, OrNull))
+  if (S.CheckCountedByAttrOnField(FD, CountExpr, Level, CountInBytes, OrNull))
     return;
 
   QualType CAT = S.BuildCountAttributedArrayOrPointerType(
@@ -6951,7 +6952,8 @@ static bool MustDelayAttributeArguments(const ParsedAttr 
&AL) {
 /// silently ignore it if a GNU attribute.
 static void
 ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
-                     const Sema::ProcessDeclAttributeOptions &Options) {
+                     const Sema::ProcessDeclAttributeOptions &Options,
+                     unsigned NestedTypeLevel = 0) {
   if (AL.isInvalid() || AL.getKind() == ParsedAttr::IgnoredAttribute)
     return;
 
@@ -7578,7 +7580,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 
const ParsedAttr &AL,
   case ParsedAttr::AT_CountedByOrNull:
   case ParsedAttr::AT_SizedBy:
   case ParsedAttr::AT_SizedByOrNull:
-    handleCountedByAttrField(S, D, AL);
+    handleCountedByAttrField(S, D, AL, NestedTypeLevel);
     break;
 
   // Microsoft attributes:
@@ -7855,14 +7857,15 @@ static bool isKernelDecl(Decl *D) {
          D->hasAttr<CUDAGlobalAttr>();
 }
 
-void Sema::ProcessDeclAttributeList(
-    Scope *S, Decl *D, const ParsedAttributesView &AttrList,
-    const ProcessDeclAttributeOptions &Options) {
+void Sema::ProcessDeclAttributeList(Scope *S, Decl *D,
+                                    const ParsedAttributesView &AttrList,
+                                    const ProcessDeclAttributeOptions &Options,
+                                    unsigned NestedTypeLevel) {
   if (AttrList.empty())
     return;
 
   for (const ParsedAttr &AL : AttrList)
-    ProcessDeclAttribute(*this, S, D, AL, Options);
+    ProcessDeclAttribute(*this, S, D, AL, Options, NestedTypeLevel);
 
   // FIXME: We should be able to handle these cases in TableGen.
   // GCC accepts
@@ -8171,11 +8174,22 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, 
const Declarator &PD) {
   // position to the decl itself.  This handles cases like:
   //   int *__attr__(x)** D;
   // when X is a decl attribute.
+  unsigned NestedTypeLevel = 0;
   for (unsigned i = 0, e = PD.getNumTypeObjects(); i != e; ++i) {
     ProcessDeclAttributeList(S, D, PD.getTypeObject(i).getAttrs(),
                              ProcessDeclAttributeOptions()
                                  .WithIncludeCXX11Attributes(false)
-                                 .WithIgnoreTypeAttributes(true));
+                                 .WithIgnoreTypeAttributes(true),
+                             NestedTypeLevel);
+
+    switch (PD.getTypeObject(i).Kind) {
+    case DeclaratorChunk::Pointer:
+    case DeclaratorChunk::Array:
+      NestedTypeLevel++;
+      break;
+    default:
+      break;
+    }
   }
 
   // Finally, apply any attributes on the decl itself.
diff --git a/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c 
b/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
index d42547003f0b3..ca603402f0a34 100644
--- a/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
+++ b/clang/test/AST/attr-counted-by-or-null-struct-ptrs.c
@@ -56,29 +56,6 @@ struct on_member_pointer_complete_ty_ty_pos {
   struct size_known *__counted_by_or_null(count) buf;
 };
 
-// TODO: This should be forbidden but isn't due to counted_by_or_null being 
treated as a
-// declaration attribute. The attribute ends up on the outer most pointer
-// (allowed by sema) even though syntactically its supposed to be on the inner
-// pointer (would not allowed by sema due to pointee being a function type).
-// CHECK-LABEL: RecordDecl {{.+}} struct 
on_member_pointer_fn_ptr_ty_ty_pos_inner definition
-// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT:  `-FieldDecl {{.+}} fn_ptr 'void (** 
__counted_by_or_null(count))(void)':'void (**)(void)'
-struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
-  int count;
-  void (* __counted_by_or_null(count) * fn_ptr)(void);
-};
-
-// FIXME: The generated AST here is wrong. The attribute should be on the inner
-// pointer.
-// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_inner definition
-// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known ** 
__counted_by_or_null(count)':'struct size_known **'
-struct on_nested_pointer_inner {
-  int count;
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__counted_by_or_null` can only be nested when used in function 
parameters.
-  struct size_known *__counted_by_or_null(count) *buf;
-};
 
 // CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_outer definition
 // CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
diff --git a/clang/test/AST/attr-counted-by-struct-ptrs.c 
b/clang/test/AST/attr-counted-by-struct-ptrs.c
index afef9c8c3b95d..414a7007c7b49 100644
--- a/clang/test/AST/attr-counted-by-struct-ptrs.c
+++ b/clang/test/AST/attr-counted-by-struct-ptrs.c
@@ -45,8 +45,6 @@ struct on_pointer_anon_count {
 
//==============================================================================
 // __counted_by on struct member pointer in type attribute position
 
//==============================================================================
-// TODO: Correctly parse counted_by as a type attribute. Currently it is parsed
-// as a declaration attribute
 
 // CHECK-LABEL: RecordDecl {{.+}} struct on_member_pointer_complete_ty_ty_pos 
definition
 // CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
@@ -56,30 +54,6 @@ struct on_member_pointer_complete_ty_ty_pos {
   struct size_known *__counted_by(count) buf;
 };
 
-// TODO: This should be forbidden but isn't due to counted_by being treated as 
a
-// declaration attribute. The attribute ends up on the outer most pointer
-// (allowed by sema) even though syntactically its supposed to be on the inner
-// pointer (would not allowed by sema due to pointee being a function type).
-// CHECK-LABEL: RecordDecl {{.+}} struct 
on_member_pointer_fn_ptr_ty_ty_pos_inner definition
-// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT:  `-FieldDecl {{.+}} fn_ptr 'void (** 
__counted_by(count))(void)':'void (**)(void)'
-struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
-  int count;
-  void (* __counted_by(count) * fn_ptr)(void);
-};
-
-// FIXME: The generated AST here is wrong. The attribute should be on the inner
-// pointer.
-// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_inner definition
-// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known ** 
__counted_by(count)':'struct size_known **'
-struct on_nested_pointer_inner {
-  int count;
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__counted_by` can only be nested when used in function parameters.
-  struct size_known *__counted_by(count) *buf;
-};
-
 // CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_outer definition
 // CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
 // CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known ** 
__counted_by(count)':'struct size_known **'
diff --git a/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c 
b/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
index 7273280e4b60c..2592d0fd5cb4e 100644
--- a/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
+++ b/clang/test/AST/attr-sized-by-or-null-struct-ptrs.c
@@ -56,30 +56,6 @@ struct on_member_pointer_complete_ty_ty_pos {
   struct size_known *__sized_by_or_null(count) buf;
 };
 
-// TODO: This should be forbidden but isn't due to sized_by_or_null being 
treated as a
-// declaration attribute. The attribute ends up on the outer most pointer
-// (allowed by sema) even though syntactically its supposed to be on the inner
-// pointer (would not allowed by sema due to pointee being a function type).
-// CHECK-LABEL: RecordDecl {{.+}} struct 
on_member_pointer_fn_ptr_ty_ty_pos_inner definition
-// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT:  `-FieldDecl {{.+}} fn_ptr 'void (** 
__sized_by_or_null(count))(void)':'void (**)(void)'
-struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
-  int count;
-  void (* __sized_by_or_null(count) * fn_ptr)(void);
-};
-
-// FIXME: The generated AST here is wrong. The attribute should be on the inner
-// pointer.
-// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_inner definition
-// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known ** 
__sized_by_or_null(count)':'struct size_known **'
-struct on_nested_pointer_inner {
-  int count;
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__sized_by_or_null` can only be nested when used in function parameters.
-  struct size_known *__sized_by_or_null(count) *buf;
-};
-
 // CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_outer definition
 // CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
 // CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known ** 
__sized_by_or_null(count)':'struct size_known **'
diff --git a/clang/test/AST/attr-sized-by-struct-ptrs.c 
b/clang/test/AST/attr-sized-by-struct-ptrs.c
index 738eaf8cbf36b..4d7797fa82395 100644
--- a/clang/test/AST/attr-sized-by-struct-ptrs.c
+++ b/clang/test/AST/attr-sized-by-struct-ptrs.c
@@ -56,30 +56,6 @@ struct on_member_pointer_complete_ty_ty_pos {
   struct size_known *__sized_by(count) buf;
 };
 
-// TODO: This should be forbidden but isn't due to sized_by being treated as a
-// declaration attribute. The attribute ends up on the outer most pointer
-// (allowed by sema) even though syntactically its supposed to be on the inner
-// pointer (would not allowed by sema due to pointee being a function type).
-// CHECK-LABEL: RecordDecl {{.+}} struct 
on_member_pointer_fn_ptr_ty_ty_pos_inner definition
-// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT:  `-FieldDecl {{.+}} fn_ptr 'void (** 
__sized_by(count))(void)':'void (**)(void)'
-struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
-  int count;
-  void (* __sized_by(count) * fn_ptr)(void);
-};
-
-// FIXME: The generated AST here is wrong. The attribute should be on the inner
-// pointer.
-// CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_inner definition
-// CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
-// CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known ** 
__sized_by(count)':'struct size_known **'
-struct on_nested_pointer_inner {
-  int count;
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__sized_by` can only be nested when used in function parameters.
-  struct size_known *__sized_by(count) *buf;
-};
-
 // CHECK-LABEL: RecordDecl {{.+}} struct on_nested_pointer_outer definition
 // CHECK-NEXT:  |-FieldDecl {{.+}} referenced count 'int'
 // CHECK-NEXT:  `-FieldDecl {{.+}} buf 'struct size_known ** 
__sized_by(count)':'struct size_known **'
diff --git a/clang/test/Sema/attr-bounds-safety-function-ptr-param.c 
b/clang/test/Sema/attr-bounds-safety-function-ptr-param.c
new file mode 100644
index 0000000000000..091220e313958
--- /dev/null
+++ b/clang/test/Sema/attr-bounds-safety-function-ptr-param.c
@@ -0,0 +1,173 @@
+// XFAIL: *
+// FIXME: https://github.com/llvm/llvm-project/issues/166454
+
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify 
%s
+
+#define __counted_by(N) __attribute__((counted_by(N)))
+#define __counted_by_or_null(N) __attribute__((counted_by_or_null(N)))
+#define __sized_by(N) __attribute__((sized_by(N)))
+#define __sized_by_or_null(N) __attribute__((sized_by_or_null(N)))
+
+//==============================================================================
+// Test bounds safety attributes on function pointer parameters
+//==============================================================================
+
+struct counted_by_function_pointer_param {
+  // expected-error@+1{{'counted_by' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(int *__counted_by(len));
+  int len;
+};
+
+struct counted_by_or_null_function_pointer_param {
+  // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(int *__counted_by_or_null(len));
+  int len;
+};
+
+struct sized_by_function_pointer_param {
+  // expected-error@+1{{'sized_by' attribute cannot be applied to a parameter 
in a function pointer type}}
+  int (*callback)(char *__sized_by(len));
+  int len;
+};
+
+struct sized_by_or_null_function_pointer_param {
+  // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(char *__sized_by_or_null(len));
+  int len;
+};
+
+//==============================================================================
+// Test multiple parameters with bounds safety attributes
+//==============================================================================
+
+struct multiple_params_with_bounds_safety {
+  // expected-error@+1{{'counted_by' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*multi_callback)(int *__counted_by(len1), char *data, int len1);
+  int len1;
+};
+
+struct mixed_bounds_safety_params {
+  // expected-error@+2{{'counted_by' attribute cannot be applied to a 
parameter in a function pointer type}}
+  // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*mixed_callback)(int *__counted_by(count), char 
*__sized_by_or_null(size), int count, int size);
+  int count;
+  int size;
+};
+
+//==============================================================================
+// Test cases that do not require late parsing (count field defined before use)
+//==============================================================================
+
+struct counted_by_no_late_parse {
+  int len;
+  // expected-error@+1{{'counted_by' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(int *__counted_by(len));
+};
+
+struct counted_by_or_null_no_late_parse {
+  int len;
+  // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(int *__counted_by_or_null(len));
+};
+
+struct sized_by_no_late_parse {
+  int len;
+  // expected-error@+1{{'sized_by' attribute cannot be applied to a parameter 
in a function pointer type}}
+  int (*callback)(char *__sized_by(len));
+};
+
+struct sized_by_or_null_no_late_parse {
+  int len;
+  // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(char *__sized_by_or_null(len));
+};
+
+//==============================================================================
+// Test nested function pointer types
+//==============================================================================
+
+struct nested_function_pointer_with_bounds_safety {
+  // expected-error@+1{{'counted_by' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*outer_callback)(int (*inner)(int *__counted_by(len)), int len);
+  int len;
+};
+
+//==============================================================================
+// Test struct members with anonymous structs/unions (no late parsing needed)
+//==============================================================================
+
+struct with_anonymous_struct_no_late_parse {
+  int len;
+  // expected-error@+1{{'counted_by' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(int *__counted_by(len));
+};
+
+struct with_anonymous_union_no_late_parse {
+  union {
+    int len;
+    float f_len;
+  };
+  // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(int *__counted_by_or_null(len));
+};
+
+//==============================================================================
+// Test with different parameter positions
+//==============================================================================
+
+struct first_param_bounds_safety_no_late_parse {
+  int count;
+  // expected-error@+1{{'counted_by' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(int *__counted_by(count), void *data, int extra);
+};
+
+struct middle_param_bounds_safety_no_late_parse {
+  int size;
+  // expected-error@+1{{'sized_by' attribute cannot be applied to a parameter 
in a function pointer type}}
+  int (*callback)(void *prefix, char *__sized_by(size), int suffix);
+};
+
+struct last_param_bounds_safety_no_late_parse {
+  int len;
+  // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(int a, float b, int *__counted_by_or_null(len));
+};
+
+//==============================================================================
+// Test with const and volatile qualifiers
+//==============================================================================
+
+struct const_param_bounds_safety_no_late_parse {
+  int count;
+  // expected-error@+1{{'counted_by' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(const int *__counted_by(count));
+};
+
+struct volatile_param_bounds_safety_no_late_parse {
+  int size;
+  // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(volatile char *__sized_by_or_null(size));
+};
+
+struct const_volatile_param_bounds_safety_no_late_parse {
+  int len;
+  // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback)(const volatile int *__counted_by_or_null(len));
+};
+
+//==============================================================================
+// Test with multiple function pointers in same struct
+//==============================================================================
+
+struct multiple_function_pointers_no_late_parse {
+  int len1, len2, size1, size2;
+  // expected-error@+1{{'counted_by' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback1)(int *__counted_by(len1));
+  // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  int (*callback2)(int *__counted_by_or_null(len2));
+  // expected-error@+1{{'sized_by' attribute cannot be applied to a parameter 
in a function pointer type}}
+  void (*callback3)(char *__sized_by(size1));
+  // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a 
parameter in a function pointer type}}
+  void (*callback4)(char *__sized_by_or_null(size2));
+};
diff --git a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c 
b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
index 8d4e0c510603a..42974c7d9d45f 100644
--- a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c
@@ -104,35 +104,24 @@ struct on_pointer_anon_count {
 
//==============================================================================
 // __counted_by on struct member pointer in type attribute position
 
//==============================================================================
-// TODO: Correctly parse counted_by as a type attribute. Currently it is parsed
-// as a declaration attribute and is **not** late parsed resulting in the 
`count`
-// field being unavailable.
 
 struct on_member_pointer_complete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   struct size_known *__counted_by(count) buf;
   int count;
 };
 
 struct on_member_pointer_incomplete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   struct size_unknown * __counted_by(count) buf;
   int count;
 };
 
 struct on_member_pointer_const_incomplete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   const struct size_unknown * __counted_by(count) buf;
   int count;
 };
 
 struct on_member_pointer_void_ty_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being an incomplete type.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by' cannot be applied to a pointer with 
pointee of unknown size because 'void' is an incomplete type}}
   void *__counted_by(count) buf;
   int count;
 };
@@ -140,79 +129,57 @@ struct on_member_pointer_void_ty_ty_pos {
 // -
 
 struct on_member_pointer_fn_ptr_ty_pos {
-  // TODO: buffer of `count` function pointers should be allowed
-  // but fails because this isn't late parsed.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   void (** __counted_by(count) fn_ptr)(void);
   int count;
 };
 
 struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
-  // TODO: buffer of `count` function pointers should be allowed
-  // but fails because this isn't late parsed.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   fn_ptr_ty* __counted_by(count) fn_ptr;
   int count;
 };
 
 struct on_member_pointer_fn_ty_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a function type.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by' cannot be applied to a pointer with 
pointee of unknown size because 'void (void)' is a function type}}
   void (* __counted_by(count) fn_ptr)(void);
   int count;
 };
 
 struct on_member_pointer_fn_ptr_ty_ty_pos {
-  // TODO: buffer of `count` function pointers should be allowed
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   void (** __counted_by(count) fn_ptr)(void);
   int count;
 };
 
 struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a function type.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by' cannot be applied to a pointer with 
pointee of unknown size because 'void (void)' is a function type}}
   fn_ptr_ty __counted_by(count) fn_ptr;
   int count;
 };
 
 struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a function type.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by' attribute on nested pointer type is not 
allowed}}
   void (* __counted_by(count) * fn_ptr)(void);
   int count;
 };
 
 struct on_member_pointer_struct_with_vla_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a struct type with a VLA.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by' cannot be applied to a pointer with 
pointee of unknown size because 'struct has_unannotated_vla' is a struct type 
with a flexible array member}}
   struct has_unannotated_vla *__counted_by(count) objects;
   int count;
 };
 
 struct on_member_pointer_struct_with_annotated_vla_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a struct type with a VLA.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by' cannot be applied to a pointer with 
pointee of unknown size because 'struct has_annotated_vla' is a struct type 
with a flexible array member}}
   struct has_annotated_vla* __counted_by(count) objects;
   int count;
 };
 
 struct on_nested_pointer_inner {
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__counted_by` can only be nested when used in function parameters.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by' attribute on nested pointer type is not 
allowed}}
   struct size_known *__counted_by(count) *buf;
   int count;
 };
 
 struct on_nested_pointer_outer {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   struct size_known **__counted_by(count) buf;
   int count;
 };
@@ -227,8 +194,6 @@ struct on_pointer_anon_buf_ty_pos {
 };
 
 struct on_pointer_anon_count_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   struct size_known *__counted_by(count) buf;
   struct {
     int count;
diff --git a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c 
b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
index 2150c81f9e9be..96169e6fbd986 100644
--- a/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c
@@ -105,35 +105,24 @@ struct on_pointer_anon_count {
 
//==============================================================================
 // __counted_by_or_null on struct member pointer in type attribute position
 
//==============================================================================
-// TODO: Correctly parse counted_by_or_null as a type attribute. Currently it 
is parsed
-// as a declaration attribute and is **not** late parsed resulting in the 
`count`
-// field being unavailable.
 
 struct on_member_pointer_complete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   struct size_known *__counted_by_or_null(count) buf;
   int count;
 };
 
 struct on_member_pointer_incomplete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   struct size_unknown * __counted_by_or_null(count) buf;
   int count;
 };
 
 struct on_member_pointer_const_incomplete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   const struct size_unknown * __counted_by_or_null(count) buf;
   int count;
 };
 
 struct on_member_pointer_void_ty_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being an incomplete type.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because 'void' is an incomplete type}}
   void *__counted_by_or_null(count) buf;
   int count;
 };
@@ -141,79 +130,57 @@ struct on_member_pointer_void_ty_ty_pos {
 // -
 
 struct on_member_pointer_fn_ptr_ty_pos {
-  // TODO: buffer of `count` function pointers should be allowed
-  // but fails because this isn't late parsed.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   void (** __counted_by_or_null(count) fn_ptr)(void);
   int count;
 };
 
 struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
-  // TODO: buffer of `count` function pointers should be allowed
-  // but fails because this isn't late parsed.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   fn_ptr_ty* __counted_by_or_null(count) fn_ptr;
   int count;
 };
 
 struct on_member_pointer_fn_ty_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a function type.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because 'void (void)' is a function type}}
   void (* __counted_by_or_null(count) fn_ptr)(void);
   int count;
 };
 
 struct on_member_pointer_fn_ptr_ty_ty_pos {
-  // TODO: buffer of `count` function pointers should be allowed
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   void (** __counted_by_or_null(count) fn_ptr)(void);
   int count;
 };
 
 struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a function type.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because 'void (void)' is a function type}}
   fn_ptr_ty __counted_by_or_null(count) fn_ptr;
   int count;
 };
 
 struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a function type.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by_or_null' attribute on nested pointer type 
is not allowed}}
   void (* __counted_by_or_null(count) * fn_ptr)(void);
   int count;
 };
 
 struct on_member_pointer_struct_with_vla_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a struct type with a VLA.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{cannot be applied to a pointer with pointee of unknown 
size because 'struct has_unannotated_vla' is a struct type with a flexible 
array member}}
   struct has_unannotated_vla *__counted_by_or_null(count) objects;
   int count;
 };
 
 struct on_member_pointer_struct_with_annotated_vla_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a struct type with a VLA.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by_or_null' cannot be applied to a pointer 
with pointee of unknown size because 'struct has_annotated_vla' is a struct 
type with a flexible array member}}
   struct has_annotated_vla* __counted_by_or_null(count) objects;
   int count;
 };
 
 struct on_nested_pointer_inner {
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__counted_by_or_null` can only be nested when used in function 
parameters.
-  // expected-error@+1{{use of undeclared identifier 'count'}}
+  // expected-error@+1{{'counted_by_or_null' attribute on nested pointer type 
is not allowed}}
   struct size_known *__counted_by_or_null(count) *buf;
   int count;
 };
 
 struct on_nested_pointer_outer {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   struct size_known **__counted_by_or_null(count) buf;
   int count;
 };
@@ -228,8 +195,6 @@ struct on_pointer_anon_buf_ty_pos {
 };
 
 struct on_pointer_anon_count_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'count'}}
   struct size_known *__counted_by_or_null(count) buf;
   struct {
     int count;
diff --git 
a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c
 
b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c
index cff5a14c70b99..dc603f652b43d 100644
--- 
a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c
+++ 
b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c
@@ -17,7 +17,7 @@
 // expected-note@+1 24{{forward declaration of 'struct IncompleteTy'}}
 struct IncompleteTy; // expected-note 27{{consider providing a complete 
definition for 'struct IncompleteTy'}}
 
-typedef struct IncompleteTy Incomplete_t; 
+typedef struct IncompleteTy Incomplete_t;
 
 struct CBBufDeclPos {
   int count;
@@ -75,7 +75,7 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
   void* tmp3 = implicit_full_init.buf;
   // expected-error@+1{{cannot use 'implicit_full_init.buf_typedef' with 
'__counted_by_or_null' attributed type 'Incomplete_t * 
__counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee 
type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
   void* tmp4 = implicit_full_init.buf_typedef;
-  
+
   struct CBBufDeclPos explicit_non_desig_init = {
     0,
     // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with 
'__counted_by_or_null' attributed type 'struct IncompleteTy * 
__counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee 
type 'struct IncompleteTy' is incomplete}}
@@ -113,7 +113,7 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
   uninit.buf_typedef++; // // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
   ++uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
   uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
-  
+
   uninit.buf--; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
   --uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
   uninit.buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
@@ -139,16 +139,16 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
   // ## Use of fields in expressions
   // 
===========================================================================
   // expected-error@+2{{cannot use 'uninit.buf' with '__counted_by_or_null' 
attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 
'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is 
incomplete}}
-  void* addr = 
+  void* addr =
     ((char*) uninit.buf ) + 1;
   // expected-error@+2{{cannot use 'uninit.buf_typedef' with 
'__counted_by_or_null' attributed type 'Incomplete_t * 
__counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee 
type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
-  void* addr_typedef = 
+  void* addr_typedef =
     ((char*) uninit.buf_typedef ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' 
attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 
'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is 
incomplete}}
-  void* addr_ptr = 
+  void* addr_ptr =
     ((char*) ptr->buf ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf_typedef' with 
'__counted_by_or_null' attributed type 'Incomplete_t * 
__counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee 
type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
-  void* addr_ptr_typedef = 
+  void* addr_ptr_typedef =
     ((char*) ptr->buf_typedef ) + 1;
 
 
@@ -289,7 +289,7 @@ void test_CBBufDeclPos_completed(struct CBBufDeclPos* ptr) {
   };
 
   struct CBBufDeclPos implicit_full_init = {0};
-  
+
   struct CBBufDeclPos explicit_non_desig_init = {
     0,
     0x0,
@@ -384,10 +384,10 @@ void use_CBBufTyPos(struct CBBufTyPos* ptr) {
 
   // Use
   // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' 
attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 
'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is 
incomplete}}
-  void* addr = 
+  void* addr =
     ((char*) ptr->buf ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf_typedef' with 
'__counted_by_or_null' attributed type 'Incomplete_ty2 * 
__counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee 
type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
-  void* addr_typedef = 
+  void* addr_typedef =
     ((char*) ptr->buf_typedef ) + 1;
 
   // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' 
attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 
'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is 
incomplete}}
@@ -458,10 +458,10 @@ void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) {
 
   // Use
   // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' 
attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 
'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' 
is incomplete}}
-  void* addr = 
+  void* addr =
     ((char*) ptr->buf ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf_typedef' with 
'__counted_by_or_null' attributed type 'IncompleteUnion_ty * 
__counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the 
pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is 
incomplete}}
-  void* addr_typedef = 
+  void* addr_typedef =
     ((char*) ptr->buf_typedef ) + 1;
 
   // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' 
attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 
'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' 
is incomplete}}
@@ -532,10 +532,10 @@ void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) {
 
   // Use
   // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' 
attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 
'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is 
incomplete}}
-  void* addr = 
+  void* addr =
     ((char*) ptr->buf ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf_typedef' with 
'__counted_by_or_null' attributed type 'IncompleteEnum_ty * 
__counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the 
pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
-  void* addr_typedef = 
+  void* addr_typedef =
     ((char*) ptr->buf_typedef ) + 1;
 
   // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' 
attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 
'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is 
incomplete}}
@@ -616,10 +616,7 @@ struct IncompleteTy3;
 
 struct CBBufFAMofCountedByPtrs {
   int size;
-  // TODO: This is misleading. The attribute is written in the type position
-  // but clang currently doesn't treat it like that and it gets treated as
-  // an attribute on the array, rather than on the element type.
-  // expected-error@+1{{'counted_by_or_null' only applies to pointers; did you 
mean to use 'counted_by'?}}
+  // expected-error@+1{{'counted_by_or_null' attribute on nested pointer type 
is not allowed}}
   struct IncompleteTy3* __counted_by_or_null(size) arr[];
 };
 
diff --git a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c 
b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
index 0bb09059c97f9..36ece813defa9 100644
--- a/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c
@@ -104,8 +104,6 @@ struct on_pointer_anon_count {
 
//==============================================================================
 // __counted_by_or_null on struct member pointer in type attribute position
 
//==============================================================================
-// TODO: Correctly parse counted_by_or_null as a type attribute. Currently it 
is parsed
-// as a declaration attribute
 
 struct on_member_pointer_complete_ty_ty_pos {
   int count;
@@ -156,10 +154,9 @@ struct on_member_pointer_fn_ptr_ty_ty_pos {
   fn_ptr_ty __counted_by_or_null(count) fn_ptr;
 };
 
-// TODO: This should be forbidden but isn't due to counted_by_or_null being 
treated
-// as a declaration attribute.
 struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
   int count;
+  // expected-error@+1{{'counted_by_or_null' attribute on nested pointer type 
is not allowed}}
   void (* __counted_by_or_null(count) * fn_ptr)(void);
 };
 
@@ -179,9 +176,8 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos {
 };
 
 struct on_nested_pointer_inner {
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__counted_by_or_null` can only be nested when used in function 
parameters.
   int count;
+  // expected-error@+1{{'counted_by_or_null' attribute on nested pointer type 
is not allowed}}
   struct size_known *__counted_by_or_null(count) *buf;
 };
 
diff --git 
a/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c 
b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
index d28a2086b51b8..f9afe558d0e13 100644
--- 
a/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
+++ 
b/clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c
@@ -17,7 +17,7 @@
 // expected-note@+1 24{{forward declaration of 'struct IncompleteTy'}}
 struct IncompleteTy; // expected-note 27{{consider providing a complete 
definition for 'struct IncompleteTy'}}
 
-typedef struct IncompleteTy Incomplete_t; 
+typedef struct IncompleteTy Incomplete_t;
 
 struct CBBufDeclPos {
   int count;
@@ -75,7 +75,7 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
   void* tmp3 = implicit_full_init.buf;
   // expected-error@+1{{cannot use 'implicit_full_init.buf_typedef' with 
'__counted_by' attributed type 'Incomplete_t * __counted_by(count)' (aka 
'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct 
IncompleteTy') is incomplete}}
   void* tmp4 = implicit_full_init.buf_typedef;
-  
+
   struct CBBufDeclPos explicit_non_desig_init = {
     0,
     // expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with 
'__counted_by' attributed type 'struct IncompleteTy * __counted_by(count)' (aka 
'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is 
incomplete}}
@@ -113,7 +113,7 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
   uninit.buf_typedef++; // // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
   ++uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
   uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an 
incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
-  
+
   uninit.buf--; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
   --uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
   uninit.buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete 
type 'struct IncompleteTy'}}
@@ -139,16 +139,16 @@ void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
   // ## Use of fields in expressions
   // 
===========================================================================
   // expected-error@+2{{cannot use 'uninit.buf' with '__counted_by' attributed 
type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete}}
-  void* addr = 
+  void* addr =
     ((char*) uninit.buf ) + 1;
   // expected-error@+2{{cannot use 'uninit.buf_typedef' with '__counted_by' 
attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy 
*') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is 
incomplete}}
-  void* addr_typedef = 
+  void* addr_typedef =
     ((char*) uninit.buf_typedef ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by' attributed 
type 'struct IncompleteTy * __counted_by(count)' (aka 'struct IncompleteTy *') 
because the pointee type 'struct IncompleteTy' is incomplete}}
-  void* addr_ptr = 
+  void* addr_ptr =
     ((char*) ptr->buf ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by' 
attributed type 'Incomplete_t * __counted_by(count)' (aka 'struct IncompleteTy 
*') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is 
incomplete}}
-  void* addr_ptr_typedef = 
+  void* addr_ptr_typedef =
     ((char*) ptr->buf_typedef ) + 1;
 
 
@@ -289,7 +289,7 @@ void test_CBBufDeclPos_completed(struct CBBufDeclPos* ptr) {
   };
 
   struct CBBufDeclPos implicit_full_init = {0};
-  
+
   struct CBBufDeclPos explicit_non_desig_init = {
     0,
     0x0,
@@ -384,10 +384,10 @@ void use_CBBufTyPos(struct CBBufTyPos* ptr) {
 
   // Use
   // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by' attributed 
type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 
*') because the pointee type 'struct IncompleteTy2' is incomplete}}
-  void* addr = 
+  void* addr =
     ((char*) ptr->buf ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by' 
attributed type 'Incomplete_ty2 * __counted_by(count)' (aka 'struct 
IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct 
IncompleteTy2') is incomplete}}
-  void* addr_typedef = 
+  void* addr_typedef =
     ((char*) ptr->buf_typedef ) + 1;
 
   // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by' attributed 
type 'struct IncompleteTy2 * __counted_by(count)' (aka 'struct IncompleteTy2 
*') because the pointee type 'struct IncompleteTy2' is incomplete}}
@@ -458,10 +458,10 @@ void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) {
 
   // Use
   // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by' attributed 
type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union 
IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is 
incomplete}}
-  void* addr = 
+  void* addr =
     ((char*) ptr->buf ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by' 
attributed type 'IncompleteUnion_ty * __counted_by(count)' (aka 'union 
IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union 
IncompleteUnionTy') is incomplete}}
-  void* addr_typedef = 
+  void* addr_typedef =
     ((char*) ptr->buf_typedef ) + 1;
 
   // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by' attributed 
type 'union IncompleteUnionTy * __counted_by(count)' (aka 'union 
IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is 
incomplete}}
@@ -532,10 +532,10 @@ void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) {
 
   // Use
   // expected-error@+2{{cannot use 'ptr->buf' with '__counted_by' attributed 
type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy 
*') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
-  void* addr = 
+  void* addr =
     ((char*) ptr->buf ) + 1;
   // expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by' 
attributed type 'IncompleteEnum_ty * __counted_by(count)' (aka 'enum 
IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum 
IncompleteEnumTy') is incomplete}}
-  void* addr_typedef = 
+  void* addr_typedef =
     ((char*) ptr->buf_typedef ) + 1;
 
   // expected-error@+1{{cannot use 'ptr->buf' with '__counted_by' attributed 
type 'enum IncompleteEnumTy * __counted_by(count)' (aka 'enum IncompleteEnumTy 
*') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
@@ -616,9 +616,7 @@ struct IncompleteTy3;
 
 struct CBBufFAMofCountedByPtrs {
   int size;
-  // TODO: This is misleading. The attribute is written in the type position
-  // but clang currently doesn't treat it like that and it gets treated as
-  // an attribute on the array, rather than on the element type.
+  // expected-error@+1{{'counted_by' attribute on nested pointer type is not 
allowed}}
   struct IncompleteTy3* __counted_by(size) arr[];
 };
 
diff --git a/clang/test/Sema/attr-counted-by-struct-ptrs.c 
b/clang/test/Sema/attr-counted-by-struct-ptrs.c
index c05d18262e2b7..1dd549b47cea9 100644
--- a/clang/test/Sema/attr-counted-by-struct-ptrs.c
+++ b/clang/test/Sema/attr-counted-by-struct-ptrs.c
@@ -103,8 +103,6 @@ struct on_pointer_anon_count {
 
//==============================================================================
 // __counted_by on struct member pointer in type attribute position
 
//==============================================================================
-// TODO: Correctly parse counted_by as a type attribute. Currently it is parsed
-// as a declaration attribute
 
 struct on_member_pointer_complete_ty_ty_pos {
   int count;
@@ -155,10 +153,9 @@ struct on_member_pointer_fn_ptr_ty_ty_pos {
   fn_ptr_ty __counted_by(count) fn_ptr;
 };
 
-// TODO: This should be forbidden but isn't due to counted_by being treated
-// as a declaration attribute.
 struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
   int count;
+  // expected-error@+1{{'counted_by' attribute on nested pointer type is not 
allowed}}
   void (* __counted_by(count) * fn_ptr)(void);
 };
 
@@ -178,9 +175,8 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos {
 };
 
 struct on_nested_pointer_inner {
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__counted_by` can only be nested when used in function parameters.
   int count;
+  // expected-error@+1{{'counted_by' attribute on nested pointer type is not 
allowed}}
   struct size_known *__counted_by(count) *buf;
 };
 
diff --git a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c 
b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
index 07f8801787d66..0c9ca303be1fd 100644
--- a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
@@ -107,30 +107,21 @@ struct on_pointer_anon_count {
 // field being unavailable.
 
 struct on_member_pointer_complete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   struct size_known *__sized_by(size) buf;
   int size;
 };
 
 struct on_member_pointer_incomplete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   struct size_unknown * __sized_by(size) buf;
   int size;
 };
 
 struct on_member_pointer_const_incomplete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   const struct size_unknown * __sized_by(size) buf;
   int size;
 };
 
 struct on_member_pointer_void_ty_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being an incomplete type.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   void *__sized_by(size) buf;
   int size;
 };
@@ -138,60 +129,46 @@ struct on_member_pointer_void_ty_ty_pos {
 // -
 
 struct on_member_pointer_fn_ptr_ty_pos {
-  // TODO: buffer of `size` function pointers should be allowed
-  // but fails because this isn't late parsed.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   void (** __sized_by(size) fn_ptr)(void);
   int size;
 };
 
 struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
-  // TODO: buffer of `size` function pointers should be allowed
-  // but fails because this isn't late parsed.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   fn_ptr_ty* __sized_by(size) fn_ptr;
   int size;
 };
 
 struct on_member_pointer_fn_ty_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a function type.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee 
of unknown size because 'void (void)' is a function type}}
   void (* __sized_by(size) fn_ptr)(void);
   int size;
 };
 
 struct on_member_pointer_fn_ptr_ty_ty_pos {
-  // TODO: buffer of `size` function pointers should be allowed
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   void (** __sized_by(size) fn_ptr)(void);
   int size;
 };
 
 struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
-  // TODO: This should be allowed with sized_by.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee 
of unknown size because 'void (void)' is a function type}}
   fn_ptr_ty __sized_by(size) fn_ptr;
   int size;
 };
 
 struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
-  // TODO: This should be allowed with sized_by.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{}}
   void (* __sized_by(size) * fn_ptr)(void);
   int size;
 };
 
 struct on_member_pointer_struct_with_vla_ty_pos {
-  // TODO: This should be allowed with sized_by.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee 
of unknown size because 'struct has_unannotated_vla' is a struct type with a 
flexible array member}}
   struct has_unannotated_vla *__sized_by(size) objects;
   int size;
 };
 
 struct on_member_pointer_struct_with_annotated_vla_ty_pos {
-  // TODO: This should be allowed with sized_by.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{'sized_by' cannot be applied to a pointer with pointee 
of unknown size because 'struct has_annotated_vla' is a struct type with a 
flexible array member}}
   struct has_annotated_vla* __sized_by(size) objects;
   int size;
 };
@@ -199,14 +176,12 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos 
{
 struct on_nested_pointer_inner {
   // TODO: This should be disallowed because in the `-fbounds-safety` model
   // `__sized_by` can only be nested when used in function parameters.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{}}
   struct size_known *__sized_by(size) *buf;
   int size;
 };
 
 struct on_nested_pointer_outer {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   struct size_known **__sized_by(size) buf;
   int size;
 };
@@ -221,8 +196,6 @@ struct on_pointer_anon_buf_ty_pos {
 };
 
 struct on_pointer_anon_count_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   struct size_known *__sized_by(size) buf;
   struct {
     int size;
diff --git a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c 
b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
index afe5f0af28083..19e63d74de70b 100644
--- a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
@@ -102,35 +102,23 @@ struct on_pointer_anon_count {
 
//==============================================================================
 // __sized_by_or_null on struct member pointer in type attribute position
 
//==============================================================================
-// TODO: Correctly parse sized_by_or_null as a type attribute. Currently it is 
parsed
-// as a declaration attribute and is **not** late parsed resulting in the 
`size`
-// field being unavailable.
 
 struct on_member_pointer_complete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   struct size_known *__sized_by_or_null(size) buf;
   int size;
 };
 
 struct on_member_pointer_incomplete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   struct size_unknown * __sized_by_or_null(size) buf;
   int size;
 };
 
 struct on_member_pointer_const_incomplete_ty_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   const struct size_unknown * __sized_by_or_null(size) buf;
   int size;
 };
 
 struct on_member_pointer_void_ty_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being an incomplete type.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   void *__sized_by_or_null(size) buf;
   int size;
 };
@@ -138,75 +126,59 @@ struct on_member_pointer_void_ty_ty_pos {
 // -
 
 struct on_member_pointer_fn_ptr_ty_pos {
-  // TODO: buffer of `size` function pointers should be allowed
-  // but fails because this isn't late parsed.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   void (** __sized_by_or_null(size) fn_ptr)(void);
   int size;
 };
 
 struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
-  // TODO: buffer of `size` function pointers should be allowed
-  // but fails because this isn't late parsed.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   fn_ptr_ty* __sized_by_or_null(size) fn_ptr;
   int size;
 };
 
 struct on_member_pointer_fn_ty_ty_pos {
-  // TODO: This should fail because the attribute is
-  // on a pointer with the pointee being a function type.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with 
pointee of unknown size because 'void (void)' is a function type}}
   void (* __sized_by_or_null(size) fn_ptr)(void);
   int size;
 };
 
 struct on_member_pointer_fn_ptr_ty_ty_pos {
-  // TODO: buffer of `size` function pointers should be allowed
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   void (** __sized_by_or_null(size) fn_ptr)(void);
   int size;
 };
 
 struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
-  // TODO: This should be allowed with sized_by_or_null.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with 
pointee of unknown size because 'void (void)' is a function type}}
   fn_ptr_ty __sized_by_or_null(size) fn_ptr;
   int size;
 };
 
 struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
-  // TODO: This should be allowed with sized_by_or_null.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{'sized_by_or_null' attribute on nested pointer type is 
not allowed}}
   void (* __sized_by_or_null(size) * fn_ptr)(void);
   int size;
 };
 
 struct on_member_pointer_struct_with_vla_ty_pos {
-  // TODO: This should be allowed with sized_by_or_null.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // TODO: Allow this
+  // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with 
pointee of unknown size because 'struct has_unannotated_vla' is a struct type 
with a flexible array member}}
   struct has_unannotated_vla *__sized_by_or_null(size) objects;
   int size;
 };
 
 struct on_member_pointer_struct_with_annotated_vla_ty_pos {
-  // TODO: This should be allowed with sized_by_or_null.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // TODO: Allow this
+  // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with 
pointee of unknown size because 'struct has_annotated_vla' is a struct type 
with a flexible array member}}
   struct has_annotated_vla* __sized_by_or_null(size) objects;
   int size;
 };
 
 struct on_nested_pointer_inner {
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__sized_by_or_null` can only be nested when used in function parameters.
-  // expected-error@+1{{use of undeclared identifier 'size'}}
+  // expected-error@+1{{'sized_by_or_null' attribute on nested pointer type is 
not allowed}}
   struct size_known *__sized_by_or_null(size) *buf;
   int size;
 };
 
 struct on_nested_pointer_outer {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   struct size_known **__sized_by_or_null(size) buf;
   int size;
 };
@@ -221,8 +193,6 @@ struct on_pointer_anon_buf_ty_pos {
 };
 
 struct on_pointer_anon_count_ty_pos {
-  // TODO: Allow this
-  // expected-error@+1{{use of undeclared identifier 'size'}}
   struct size_known *__sized_by_or_null(size) buf;
   struct {
     int size;
diff --git a/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c 
b/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
index 4200c9275a180..9ff681c8a863a 100644
--- a/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-or-null-struct-ptrs.c
@@ -102,8 +102,6 @@ struct on_pointer_anon_size {
 
//==============================================================================
 // __sized_by_or_null on struct member pointer in type attribute position
 
//==============================================================================
-// TODO: Correctly parse sized_by_or_null as a type attribute. Currently it is 
parsed
-// as a declaration attribute
 
 struct on_member_pointer_complete_ty_ty_pos {
   int size;
@@ -151,10 +149,9 @@ struct on_member_pointer_fn_ptr_ty_ty_pos {
   fn_ptr_ty __sized_by_or_null(size) fn_ptr;
 };
 
-// TODO: This should be forbidden but isn't due to sized_by_or_null being 
treated
-// as a declaration attribute.
 struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
   int size;
+  // expected-error@+1{{sized_by_or_null' attribute on nested pointer type is 
not allowed}}
   void (* __sized_by_or_null(size) * fn_ptr)(void);
 };
 
@@ -171,9 +168,8 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos {
 };
 
 struct on_nested_pointer_inner {
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__sized_by_or_null` can only be nested when used in function parameters.
   int size;
+  // expected-error@+1{{'sized_by_or_null' attribute on nested pointer type is 
not allowed}}
   struct size_known *__sized_by_or_null(size) *buf;
 };
 
diff --git a/clang/test/Sema/attr-sized-by-struct-ptrs.c 
b/clang/test/Sema/attr-sized-by-struct-ptrs.c
index 07373b247d0f7..9783f31913ba0 100644
--- a/clang/test/Sema/attr-sized-by-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-struct-ptrs.c
@@ -151,10 +151,9 @@ struct on_member_pointer_fn_ptr_ty_ty_pos {
   fn_ptr_ty __sized_by(size) fn_ptr;
 };
 
-// TODO: This should be forbidden but isn't due to sized_by being treated
-// as a declaration attribute.
 struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
   int size;
+  // expected-error@+1{{'sized_by' attribute on nested pointer type is not 
allowed}}
   void (* __sized_by(size) * fn_ptr)(void);
 };
 
@@ -171,9 +170,8 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos {
 };
 
 struct on_nested_pointer_inner {
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__sized_by` can only be nested when used in function parameters.
   int size;
+  // expected-error@+1{{'sized_by' attribute on nested pointer type is not 
allowed}}
   struct size_known *__sized_by(size) *buf;
 };
 

>From 53330a9832161f074eb1d8bb89f420cb8abab167 Mon Sep 17 00:00:00 2001
From: Yeoul Na <[email protected]>
Date: Mon, 10 Nov 2025 10:45:46 -0800
Subject: [PATCH 2/3] Add clarifying comments; Fix empty diagostic text; Use
 ArrayRef for parameter

---
 clang/include/clang/Parse/Parser.h                     |  4 ++--
 clang/include/clang/Sema/DeclSpec.h                    |  8 ++++++--
 clang/lib/Parse/ParseDecl.cpp                          | 10 ++++++----
 .../test/Sema/attr-sized-by-late-parsed-struct-ptrs.c  |  9 ++-------
 .../attr-sized-by-or-null-late-parsed-struct-ptrs.c    |  1 +
 5 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 8f7c921fb2b1d..f2f969e7b2de1 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1164,8 +1164,8 @@ class Parser : public CodeCompletionHandler {
     unsigned NestedTypeLevel;
 
     explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
-                                 SourceLocation Loc, unsigned Level = 0)
-        : Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(Level) {}
+                                 SourceLocation Loc, unsigned NestedTypeLevel 
= 0)
+        : Self(P), AttrName(Name), AttrNameLoc(Loc), 
NestedTypeLevel(NestedTypeLevel) {}
 
     void ParseLexedAttributes() override;
 
diff --git a/clang/include/clang/Sema/DeclSpec.h 
b/clang/include/clang/Sema/DeclSpec.h
index 9f633dd71c3f6..fd5ca9b0d089f 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1238,7 +1238,11 @@ struct DeclaratorChunk {
 
   ParsedAttributesView AttrList;
 
-  using LateAttrListTy = SmallVector<void *, 1>;
+  /// Stores pointers to `Parser::LateParsedAttribute`. We use `void*` here
+  /// because `LateParsedAttribute` is a nested struct of `class Parser` and
+  /// cannot be forward-declared.
+  using LateAttrOpaquePtr = void *;
+  using LateAttrListTy = SmallVector<LateAttrOpaquePtr, 1>;
   LateAttrListTy LateAttrList;
 
   struct PointerTypeInfo {
@@ -2329,7 +2333,7 @@ class Declarator {
   /// of those attributes from the parameter.
   void AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
                    SourceLocation EndLoc,
-                   const DeclaratorChunk::LateAttrListTy &LateAttrs = {}) {
+                   ArrayRef<DeclaratorChunk::LateAttrOpaquePtr> LateAttrs = 
{}) {
     DeclTypeInfo.push_back(TI);
     DeclTypeInfo.back().getAttrs().prepend(attrs.begin(), attrs.end());
     getAttributePool().takeAllFrom(attrs.getPool());
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 514f6daf44e2f..503f99d52f533 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3145,7 +3145,7 @@ void Parser::DistributeCLateParsedAttrs(Declarator &D, 
Decl *Dcl,
   if (!LateAttrs)
     return;
 
-  unsigned NestedLevel = 0;
+  unsigned NestedTypeLevel = 0;
   for (unsigned i = 0; i < D.getNumTypeObjects(); ++i) {
     DeclaratorChunk &DC = D.getTypeObject(i);
 
@@ -3160,10 +3160,10 @@ void Parser::DistributeCLateParsedAttrs(Declarator &D, 
Decl *Dcl,
     // Extract `LateParsedAttribute *` from `DeclaratorChunk`.
     for (auto *OpaqueLA : DC.LateAttrList) {
       auto *LA = static_cast<LateParsedAttribute *>(OpaqueLA);
-      LA->NestedTypeLevel = NestedLevel;
+      LA->NestedTypeLevel = NestedTypeLevel;
       LateAttrs->push_back(LA);
     }
-    NestedLevel++;
+    NestedTypeLevel++;
   }
 
   // Attach `Decl *` to each `LateParsedAttribute *`.
@@ -6484,8 +6484,10 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
     if (Kind == tok::star) {
       DeclaratorChunk::LateAttrListTy OpaqueLateAttrList;
       if (getLangOpts().ExperimentalLateParseAttributes && !LateAttrs.empty()) 
{
+        // TODO: Support `counted_by` in function parameters, return types, and
+        // other contexts (Issue #167365).
         if (!D.isFunctionDeclarator()) {
-          for (auto LA : LateAttrs) {
+          for (LateParsedAttribute *LA : LateAttrs) {
             OpaqueLateAttrList.push_back(LA);
           }
         }
diff --git a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c 
b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
index 0c9ca303be1fd..e442c7ee73aaf 100644
--- a/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-late-parsed-struct-ptrs.c
@@ -102,9 +102,6 @@ struct on_pointer_anon_count {
 
//==============================================================================
 // __sized_by on struct member pointer in type attribute position
 
//==============================================================================
-// TODO: Correctly parse sized_by as a type attribute. Currently it is parsed
-// as a declaration attribute and is **not** late parsed resulting in the 
`size`
-// field being unavailable.
 
 struct on_member_pointer_complete_ty_ty_pos {
   struct size_known *__sized_by(size) buf;
@@ -156,7 +153,7 @@ struct on_member_pointer_fn_ptr_ty_typedef_ty_pos {
 };
 
 struct on_member_pointer_fn_ptr_ty_ty_pos_inner {
-  // expected-error@+1{{}}
+  // expected-error@+1{{'sized_by' attribute on nested pointer type is not 
allowed}}
   void (* __sized_by(size) * fn_ptr)(void);
   int size;
 };
@@ -174,9 +171,7 @@ struct on_member_pointer_struct_with_annotated_vla_ty_pos {
 };
 
 struct on_nested_pointer_inner {
-  // TODO: This should be disallowed because in the `-fbounds-safety` model
-  // `__sized_by` can only be nested when used in function parameters.
-  // expected-error@+1{{}}
+  // expected-error@+1{{'sized_by' attribute on nested pointer type is not 
allowed}}
   struct size_known *__sized_by(size) *buf;
   int size;
 };
diff --git a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c 
b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
index 19e63d74de70b..db9791556b0c1 100644
--- a/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
+++ b/clang/test/Sema/attr-sized-by-or-null-late-parsed-struct-ptrs.c
@@ -136,6 +136,7 @@ struct on_member_pointer_fn_ptr_ty_ptr_ty_pos {
 };
 
 struct on_member_pointer_fn_ty_ty_pos {
+  // TODO: Improve diagnostics (Issue #167368).
   // expected-error@+1{{'sized_by_or_null' cannot be applied to a pointer with 
pointee of unknown size because 'void (void)' is a function type}}
   void (* __sized_by_or_null(size) fn_ptr)(void);
   int size;

>From 00ca1321309a3cc3bd4746f932db15ab120c0e8b Mon Sep 17 00:00:00 2001
From: Yeoul Na <[email protected]>
Date: Mon, 10 Nov 2025 10:50:02 -0800
Subject: [PATCH 3/3] Clang format

---
 clang/include/clang/Parse/Parser.h  | 6 ++++--
 clang/include/clang/Sema/DeclSpec.h | 7 ++++---
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index f2f969e7b2de1..b05badc24149b 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1164,8 +1164,10 @@ class Parser : public CodeCompletionHandler {
     unsigned NestedTypeLevel;
 
     explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
-                                 SourceLocation Loc, unsigned NestedTypeLevel 
= 0)
-        : Self(P), AttrName(Name), AttrNameLoc(Loc), 
NestedTypeLevel(NestedTypeLevel) {}
+                                 SourceLocation Loc,
+                                 unsigned NestedTypeLevel = 0)
+        : Self(P), AttrName(Name), AttrNameLoc(Loc),
+          NestedTypeLevel(NestedTypeLevel) {}
 
     void ParseLexedAttributes() override;
 
diff --git a/clang/include/clang/Sema/DeclSpec.h 
b/clang/include/clang/Sema/DeclSpec.h
index fd5ca9b0d089f..75a9e937adb52 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -2331,9 +2331,10 @@ class Declarator {
   /// EndLoc, which should be the last token of the chunk.
   /// This function takes attrs by R-Value reference because it takes ownership
   /// of those attributes from the parameter.
-  void AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
-                   SourceLocation EndLoc,
-                   ArrayRef<DeclaratorChunk::LateAttrOpaquePtr> LateAttrs = 
{}) {
+  void
+  AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
+              SourceLocation EndLoc,
+              ArrayRef<DeclaratorChunk::LateAttrOpaquePtr> LateAttrs = {}) {
     DeclTypeInfo.push_back(TI);
     DeclTypeInfo.back().getAttrs().prepend(attrs.begin(), attrs.end());
     getAttributePool().takeAllFrom(attrs.getPool());

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

Reply via email to